refactor(plugins): extract alias candidate resolution

This commit is contained in:
Peter Steinberger
2026-03-08 00:28:36 +00:00
parent 7ac7b39eff
commit e758d49361
2 changed files with 74 additions and 20 deletions

View File

@@ -1351,6 +1351,20 @@ describe("loadOpenClawPlugins", () => {
expect(resolved).toBe(distFile); expect(resolved).toBe(distFile);
}); });
it("prefers dist candidates first for production src runtime", () => {
const { root, srcFile, distFile } = createPluginSdkAliasFixture();
const candidates = withEnv({ NODE_ENV: "production", VITEST: undefined }, () =>
__testing.listPluginSdkAliasCandidates({
srcFile: "index.ts",
distFile: "index.js",
modulePath: path.join(root, "src", "plugins", "loader.ts"),
}),
);
expect(candidates.indexOf(distFile)).toBeLessThan(candidates.indexOf(srcFile));
});
it("prefers src plugin-sdk alias when loader runs from src in non-production", () => { it("prefers src plugin-sdk alias when loader runs from src in non-production", () => {
const { root, srcFile } = createPluginSdkAliasFixture(); const { root, srcFile } = createPluginSdkAliasFixture();
@@ -1364,6 +1378,20 @@ describe("loadOpenClawPlugins", () => {
expect(resolved).toBe(srcFile); expect(resolved).toBe(srcFile);
}); });
it("prefers src candidates first for non-production src runtime", () => {
const { root, srcFile, distFile } = createPluginSdkAliasFixture();
const candidates = withEnv({ NODE_ENV: undefined }, () =>
__testing.listPluginSdkAliasCandidates({
srcFile: "index.ts",
distFile: "index.js",
modulePath: path.join(root, "src", "plugins", "loader.ts"),
}),
);
expect(candidates.indexOf(srcFile)).toBeLessThan(candidates.indexOf(distFile));
});
it("falls back to src plugin-sdk alias when dist is missing in production", () => { it("falls back to src plugin-sdk alias when dist is missing in production", () => {
const { root, srcFile, distFile } = createPluginSdkAliasFixture(); const { root, srcFile, distFile } = createPluginSdkAliasFixture();
fs.rmSync(distFile); fs.rmSync(distFile);

View File

@@ -47,6 +47,43 @@ const registryCache = new Map<string, PluginRegistry>();
const defaultLogger = () => createSubsystemLogger("plugins"); const defaultLogger = () => createSubsystemLogger("plugins");
function resolvePluginSdkAliasCandidateOrder(params: {
modulePath: string;
isProduction: boolean;
}) {
const normalizedModulePath = params.modulePath.replace(/\\/g, "/");
const isDistRuntime = normalizedModulePath.includes("/dist/");
return isDistRuntime || params.isProduction ? ["dist", "src"] : ["src", "dist"];
}
function listPluginSdkAliasCandidates(params: {
srcFile: string;
distFile: string;
modulePath: string;
}) {
const orderedKinds = resolvePluginSdkAliasCandidateOrder({
modulePath: params.modulePath,
isProduction: process.env.NODE_ENV === "production",
});
let cursor = path.dirname(params.modulePath);
const candidates: string[] = [];
for (let i = 0; i < 6; i += 1) {
const candidateMap = {
src: path.join(cursor, "src", "plugin-sdk", params.srcFile),
dist: path.join(cursor, "dist", "plugin-sdk", params.distFile),
} as const;
for (const kind of orderedKinds) {
candidates.push(candidateMap[kind]);
}
const parent = path.dirname(cursor);
if (parent === cursor) {
break;
}
cursor = parent;
}
return candidates;
}
const resolvePluginSdkAliasFile = (params: { const resolvePluginSdkAliasFile = (params: {
srcFile: string; srcFile: string;
distFile: string; distFile: string;
@@ -54,27 +91,14 @@ const resolvePluginSdkAliasFile = (params: {
}): string | null => { }): string | null => {
try { try {
const modulePath = params.modulePath ?? fileURLToPath(import.meta.url); const modulePath = params.modulePath ?? fileURLToPath(import.meta.url);
const isProduction = process.env.NODE_ENV === "production"; for (const candidate of listPluginSdkAliasCandidates({
const normalizedModulePath = modulePath.replace(/\\/g, "/"); srcFile: params.srcFile,
const isDistRuntime = normalizedModulePath.includes("/dist/"); distFile: params.distFile,
let cursor = path.dirname(modulePath); modulePath,
for (let i = 0; i < 6; i += 1) { })) {
const srcCandidate = path.join(cursor, "src", "plugin-sdk", params.srcFile); if (fs.existsSync(candidate)) {
const distCandidate = path.join(cursor, "dist", "plugin-sdk", params.distFile); return candidate;
const orderedCandidates =
isDistRuntime || isProduction
? [distCandidate, srcCandidate]
: [srcCandidate, distCandidate];
for (const candidate of orderedCandidates) {
if (fs.existsSync(candidate)) {
return candidate;
}
} }
const parent = path.dirname(cursor);
if (parent === cursor) {
break;
}
cursor = parent;
} }
} catch { } catch {
// ignore // ignore
@@ -190,6 +214,8 @@ const resolvePluginSdkScopedAliasMap = (): Record<string, string> => {
}; };
export const __testing = { export const __testing = {
listPluginSdkAliasCandidates,
resolvePluginSdkAliasCandidateOrder,
resolvePluginSdkAliasFile, resolvePluginSdkAliasFile,
}; };