Files
jhonny-editor/bm/premiere-pro-mcp-main/scripts/generate-adobe-api-inventory.mjs
T
João Henrique b541f502ba feat: initial commit - Jhonny Editor
- Adicionado estrutura completa do projeto
- Configurado MCP server para Premiere Pro
- Adicionado documentação e skills
- Configurado Gitignore para o projeto
2026-09-08 09:59:31 -04:00

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).`);
}