refactor: simplify bundled plugin contracts

This commit is contained in:
Peter Steinberger
2026-03-26 21:53:47 +00:00
parent 8b42ad08e5
commit 48a65f7749
5 changed files with 307 additions and 213 deletions

View File

@@ -1,10 +1,11 @@
import path from "node:path";
import { collectBundledPluginSources } from "./lib/bundled-plugin-source-utils.mjs";
import { formatGeneratedModule } from "./lib/format-generated-module.mjs";
import { reportGeneratedOutputCli, writeGeneratedOutput } from "./lib/generated-output-utils.mjs";
import { writeGeneratedOutput } from "./lib/generated-output-utils.mjs";
const GENERATED_BY = "scripts/generate-bundled-plugin-metadata.mjs";
const DEFAULT_OUTPUT_PATH = "src/plugins/bundled-plugin-metadata.generated.ts";
const DEFAULT_ENTRIES_OUTPUT_PATH = "src/generated/bundled-plugin-entries.generated.ts";
const MANIFEST_KEY = "openclaw";
const FORMATTER_CWD = path.resolve(import.meta.dirname, "..");
const CANONICAL_PACKAGE_ID_ALIASES = {
@@ -134,6 +135,19 @@ function formatTypeScriptModule(source, { outputPath }) {
});
}
function toIdentifier(dirName) {
const cleaned = String(dirName)
.replace(/[^a-zA-Z0-9]+(.)/g, (_match, next) => next.toUpperCase())
.replace(/[^a-zA-Z0-9]/g, "")
.replace(/^[^a-zA-Z]+/g, "");
const base = cleaned || "plugin";
return `${base[0].toLowerCase()}${base.slice(1)}Plugin`;
}
function normalizeGeneratedImportPath(dirName, builtPath) {
return `../../extensions/${dirName}/${String(builtPath).replace(/^\.\//u, "")}`;
}
export function collectBundledPluginMetadata(params = {}) {
const repoRoot = path.resolve(params.repoRoot ?? process.cwd());
const entries = [];
@@ -202,23 +216,73 @@ export const GENERATED_BUNDLED_PLUGIN_METADATA = ${JSON.stringify(entries, null,
`;
}
export function writeBundledPluginMetadataModule(params = {}) {
const repoRoot = path.resolve(params.repoRoot ?? process.cwd());
const outputPath = path.resolve(repoRoot, params.outputPath ?? DEFAULT_OUTPUT_PATH);
const next = formatTypeScriptModule(
renderBundledPluginMetadataModule(collectBundledPluginMetadata({ repoRoot })),
{ outputPath },
);
return writeGeneratedOutput({
repoRoot,
outputPath: params.outputPath ?? DEFAULT_OUTPUT_PATH,
next,
check: params.check,
});
export function renderBundledPluginEntriesModule(entries) {
const imports = entries
.map((entry) => {
const identifier = toIdentifier(entry.dirName);
const importPath = normalizeGeneratedImportPath(entry.dirName, entry.source.built);
return `import ${identifier} from "${importPath}";`;
})
.join("\n");
const identifiers = entries.map((entry) => toIdentifier(entry.dirName)).join(",\n ");
return `// Auto-generated by ${GENERATED_BY}. Do not edit directly.
${imports}
export const GENERATED_BUNDLED_PLUGIN_ENTRIES = [
${identifiers}
] as const;
`;
}
reportGeneratedOutputCli({
importMetaUrl: import.meta.url,
label: "bundled-plugin-metadata",
run: ({ check }) => writeBundledPluginMetadataModule({ check }),
});
export function writeBundledPluginMetadataModule(params = {}) {
const repoRoot = path.resolve(params.repoRoot ?? process.cwd());
const entries = collectBundledPluginMetadata({ repoRoot });
const outputPath = path.resolve(repoRoot, params.outputPath ?? DEFAULT_OUTPUT_PATH);
const entriesOutputPath = path.resolve(
repoRoot,
params.entriesOutputPath ?? DEFAULT_ENTRIES_OUTPUT_PATH,
);
const metadataNext = formatTypeScriptModule(renderBundledPluginMetadataModule(entries), {
outputPath,
});
const registryNext = formatTypeScriptModule(renderBundledPluginEntriesModule(entries), {
outputPath: entriesOutputPath,
});
const metadataResult = writeGeneratedOutput({
repoRoot,
outputPath: params.outputPath ?? DEFAULT_OUTPUT_PATH,
next: metadataNext,
check: params.check,
});
const entriesResult = writeGeneratedOutput({
repoRoot,
outputPath: params.entriesOutputPath ?? DEFAULT_ENTRIES_OUTPUT_PATH,
next: registryNext,
check: params.check,
});
return {
changed: metadataResult.changed || entriesResult.changed,
wrote: metadataResult.wrote || entriesResult.wrote,
outputPaths: [metadataResult.outputPath, entriesResult.outputPath],
};
}
if (import.meta.url === new URL(process.argv[1] ?? "", "file:").href) {
const check = process.argv.includes("--check");
const result = writeBundledPluginMetadataModule({ check });
if (!result.changed) {
process.exitCode = 0;
} else if (check) {
for (const outputPath of result.outputPaths) {
const relativeOutputPath = path.relative(process.cwd(), outputPath);
console.error(`[bundled-plugin-metadata] stale generated output at ${relativeOutputPath}`);
}
process.exitCode = 1;
} else {
for (const outputPath of result.outputPaths) {
const relativeOutputPath = path.relative(process.cwd(), outputPath);
console.log(`[bundled-plugin-metadata] wrote ${relativeOutputPath}`);
}
}
}