- Adicionado estrutura completa do projeto - Configurado MCP server para Premiere Pro - Adicionado documentação e skills - Configurado Gitignore para o projeto
175 lines
7.9 KiB
JavaScript
Executable File
175 lines
7.9 KiB
JavaScript
Executable File
import { readFile, writeFile } from "node:fs/promises";
|
|
import { createHash } from "node:crypto";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import ts from "typescript";
|
|
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
const packagePath = resolve(root, "node_modules/@adobe/premierepro/package.json");
|
|
const declarationsPath = process.env.PREMIERE_API_DECLARATIONS_PATH
|
|
? resolve(process.env.PREMIERE_API_DECLARATIONS_PATH)
|
|
: resolve(root, "node_modules/@adobe/premierepro/src/premierepro.d.ts");
|
|
const coveragePath = resolve(root, "src/resources/adobe-uxp-coverage.json");
|
|
const outputPath = resolve(root, "src/resources/adobe-api-inventory.json");
|
|
const check = process.argv.includes("--check");
|
|
const validateOnly = process.argv.includes("--validate-only");
|
|
|
|
const [packageText, declarationsText, coverageText] = await Promise.all([
|
|
readFile(packagePath, "utf8"),
|
|
readFile(declarationsPath, "utf8"),
|
|
readFile(coveragePath, "utf8"),
|
|
]);
|
|
const packageMetadata = JSON.parse(packageText);
|
|
const coverage = JSON.parse(coverageText);
|
|
const coveredApis = new Set(coverage.entries.flatMap((entry) => entry.adobeApi));
|
|
const source = ts.createSourceFile(declarationsPath, declarationsText, ts.ScriptTarget.Latest, true);
|
|
if (source.parseDiagnostics.length > 0) {
|
|
const details = source.parseDiagnostics.map((diagnostic) => ts.flattenDiagnosticMessageText(diagnostic.messageText, " ")).join("; ");
|
|
throw new Error(`TypeScript declaration parse failed: ${details}`);
|
|
}
|
|
const compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
const normalizedDeclarations = declarationsText.replaceAll("\r\n", "\n");
|
|
const declarationsSha256 = createHash("sha256").update(normalizedDeclarations).digest("hex");
|
|
const canonicalOwners = new Map();
|
|
const rootDeclaration = source.statements.find((statement) => (
|
|
ts.isTypeAliasDeclaration(statement) && statement.name.text === "premierepro"
|
|
));
|
|
if (!rootDeclaration || !ts.isTypeLiteralNode(rootDeclaration.type)) {
|
|
throw new Error("Adobe declarations must expose a premierepro root type literal.");
|
|
}
|
|
for (const member of rootDeclaration.type.members) {
|
|
if (!ts.isPropertySignature(member) || !member.name || !member.type || !ts.isTypeReferenceNode(member.type)) continue;
|
|
canonicalOwners.set(member.type.typeName.getText(source), member.name.getText(source).replaceAll('"', ""));
|
|
}
|
|
|
|
function memberName(member) {
|
|
if (ts.isConstructSignatureDeclaration(member)) return "[[construct]]";
|
|
if (ts.isCallSignatureDeclaration(member)) return "[[call]]";
|
|
if (ts.isIndexSignatureDeclaration(member)) return "[[index]]";
|
|
if (!member.name) throw new Error(`Unsupported anonymous type member: ${ts.SyntaxKind[member.kind]}`);
|
|
if (ts.isIdentifier(member.name) || ts.isStringLiteral(member.name) || ts.isNumericLiteral(member.name)) {
|
|
return member.name.text;
|
|
}
|
|
return member.name.getText(source);
|
|
}
|
|
|
|
function memberKind(member) {
|
|
if (ts.isMethodSignature(member)) return "method";
|
|
if (ts.isPropertySignature(member)) return "property";
|
|
if (ts.isConstructSignatureDeclaration(member)) return "constructor";
|
|
if (ts.isCallSignatureDeclaration(member)) return "call";
|
|
if (ts.isIndexSignatureDeclaration(member)) return "index";
|
|
throw new Error(`Unsupported type member: ${ts.SyntaxKind[member.kind]}`);
|
|
}
|
|
|
|
const symbols = [];
|
|
function addMember(owner, name, kind) {
|
|
const canonicalOwner = canonicalOwners.get(owner) ?? owner;
|
|
const symbol = `${canonicalOwner}.${name}`;
|
|
const declarationSymbol = `${owner}.${name}`;
|
|
symbols.push({ symbol, kind, ...(symbol === declarationSymbol ? {} : { declarationSymbol }) });
|
|
}
|
|
|
|
function collectTypeMembers(owner, node) {
|
|
if (ts.isTypeLiteralNode(node)) {
|
|
for (const member of node.members) {
|
|
addMember(owner, memberName(member), memberKind(member));
|
|
}
|
|
return;
|
|
}
|
|
if (ts.isIntersectionTypeNode(node) || ts.isUnionTypeNode(node)) {
|
|
for (const type of node.types) collectTypeMembers(owner, type);
|
|
return;
|
|
}
|
|
if (ts.isParenthesizedTypeNode(node)) collectTypeMembers(owner, node.type);
|
|
else throw new Error(`Unsupported type expression for ${owner}: ${ts.SyntaxKind[node.kind]}`);
|
|
}
|
|
|
|
function collectModule(owner, moduleDeclaration) {
|
|
symbols.push({ symbol: owner, kind: "namespace" });
|
|
if (!moduleDeclaration.body) throw new Error(`Namespace ${owner} has no body.`);
|
|
if (ts.isModuleDeclaration(moduleDeclaration.body)) {
|
|
collectModule(`${owner}.${moduleDeclaration.body.name.getText(source).replaceAll('"', "")}`, moduleDeclaration.body);
|
|
return;
|
|
}
|
|
if (!ts.isModuleBlock(moduleDeclaration.body)) {
|
|
throw new Error(`Unsupported namespace body for ${owner}: ${ts.SyntaxKind[moduleDeclaration.body.kind]}`);
|
|
}
|
|
for (const child of moduleDeclaration.body.statements) {
|
|
if (ts.isEnumDeclaration(child)) {
|
|
const enumName = `${owner}.${child.name.text}`;
|
|
symbols.push({ symbol: enumName, kind: "enum" });
|
|
for (const member of child.members) {
|
|
symbols.push({ symbol: `${enumName}.${member.name.getText(source).replaceAll('"', "")}`, kind: "enumMember" });
|
|
}
|
|
} else if (ts.isModuleDeclaration(child)) {
|
|
collectModule(`${owner}.${child.name.getText(source).replaceAll('"', "")}`, child);
|
|
} else {
|
|
throw new Error(`Unsupported declaration in namespace ${owner}: ${ts.SyntaxKind[child.kind]}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const statement of source.statements) {
|
|
if (ts.isTypeAliasDeclaration(statement)) {
|
|
const owner = statement.name.text;
|
|
symbols.push({ symbol: owner, kind: "type" });
|
|
collectTypeMembers(owner, statement.type);
|
|
} else if (ts.isModuleDeclaration(statement)) {
|
|
collectModule(statement.name.getText(source).replaceAll('"', ""), statement);
|
|
} else if (ts.isExportAssignment(statement) && statement.expression.getText(source) === "premierepro") {
|
|
// The package's `export = premierepro` binds the root declaration object.
|
|
} else {
|
|
throw new Error(`Unsupported top-level Adobe declaration: ${ts.SyntaxKind[statement.kind]}`);
|
|
}
|
|
}
|
|
|
|
const uniqueSymbols = [...new Map(symbols.map((entry) => [entry.symbol, entry])).values()]
|
|
.sort((left, right) => compareText(left.symbol, right.symbol));
|
|
const entries = uniqueSymbols.map((entry) => ({
|
|
...entry,
|
|
coverage: coveredApis.has(entry.symbol) ? "mapped" : "unmapped",
|
|
}));
|
|
const mapped = entries.filter((entry) => entry.coverage === "mapped").length;
|
|
const declaredSymbols = new Set(entries.map((entry) => entry.symbol));
|
|
const manifestOnly = [...coveredApis].filter((symbol) => !declaredSymbols.has(symbol)).sort(compareText);
|
|
const inventory = {
|
|
schemaVersion: 1,
|
|
source: {
|
|
package: "@adobe/premierepro",
|
|
version: packageMetadata.version,
|
|
declarations: "node_modules/@adobe/premierepro/src/premierepro.d.ts",
|
|
declarationsSha256,
|
|
coverageManifest: "src/resources/adobe-uxp-coverage.json",
|
|
},
|
|
semantics: {
|
|
mapped: "The exact declaration symbol is referenced by at least one coverage-manifest entry; this alone is not live-host verification.",
|
|
unmapped: "No coverage-manifest entry references the exact declaration symbol; this is a review queue, not proof that a standalone MCP tool is appropriate.",
|
|
},
|
|
stats: {
|
|
total: entries.length,
|
|
mapped,
|
|
unmapped: entries.length - mapped,
|
|
manifestOnly: manifestOnly.length,
|
|
},
|
|
manifestOnly,
|
|
entries,
|
|
};
|
|
const rendered = `${JSON.stringify(inventory, null, 2)}\n`;
|
|
|
|
if (validateOnly) {
|
|
console.log(`Validated ${entries.length} Adobe API symbols from ${packageMetadata.version}.`);
|
|
} else if (check) {
|
|
let current = "";
|
|
try { current = await readFile(outputPath, "utf8"); } catch {}
|
|
if (current.replaceAll("\r\n", "\n") !== rendered) {
|
|
console.error("Adobe API inventory is stale. Run npm run adobe:api-inventory.");
|
|
process.exitCode = 1;
|
|
} else {
|
|
console.log(`Adobe API inventory is current: ${entries.length} symbols from ${packageMetadata.version}.`);
|
|
}
|
|
} else {
|
|
await writeFile(outputPath, rendered);
|
|
console.log(`Wrote ${entries.length} Adobe API symbols (${mapped} mapped, ${entries.length - mapped} unmapped).`);
|
|
}
|