fix: keep bundled runtime deps out of release pack

This commit is contained in:
Peter Steinberger 2026-03-18 23:18:36 +00:00
parent b9c4db1a77
commit 6e044ace28
3 changed files with 70 additions and 24 deletions

View File

@ -88,24 +88,10 @@ function stagePluginRuntimeOverlay(sourceDir, targetDir) {
function linkPluginNodeModules(params) {
const runtimeNodeModulesDir = path.join(params.runtimePluginDir, "node_modules");
removePathIfExists(runtimeNodeModulesDir);
if (params.distPluginDir) {
removePathIfExists(path.join(params.distPluginDir, "node_modules"));
}
if (!fs.existsSync(params.sourcePluginNodeModulesDir)) {
return;
}
fs.symlinkSync(params.sourcePluginNodeModulesDir, runtimeNodeModulesDir, symlinkType());
// Runtime wrappers re-export from dist/extensions/<plugin>/index.js, so Node
// resolves bare-specifier dependencies relative to the dist plugin directory.
// copy-bundled-plugin-metadata removes dist node_modules; restore the link here.
if (params.distPluginDir) {
removePathIfExists(path.join(params.distPluginDir, "node_modules"));
}
if (params.distPluginDir) {
const distNodeModulesDir = path.join(params.distPluginDir, "node_modules");
fs.symlinkSync(params.sourcePluginNodeModulesDir, distNodeModulesDir, symlinkType());
}
}
export function stageBundledPluginRuntime(params = {}) {

View File

@ -2,14 +2,79 @@ import fs from "node:fs";
import path from "node:path";
import { pluginSdkEntrypoints } from "./lib/plugin-sdk-entries.mjs";
const RUNTIME_SHIMS: Partial<Record<string, string>> = {
"secret-input-runtime": [
"export {",
" hasConfiguredSecretInput,",
" normalizeResolvedSecretInputString,",
" normalizeSecretInputString,",
'} from "./config-runtime.js";',
"",
].join("\n"),
"webhook-path": [
"/** Normalize webhook paths into the canonical registry form used by route lookup. */",
"export function normalizeWebhookPath(raw) {",
" const trimmed = raw.trim();",
" if (!trimmed) {",
' return "/";',
" }",
' const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;',
' if (withSlash.length > 1 && withSlash.endsWith("/")) {',
" return withSlash.slice(0, -1);",
" }",
" return withSlash;",
"}",
"",
"/** Resolve the effective webhook path from explicit path, URL, or default fallback. */",
"export function resolveWebhookPath(params) {",
" const trimmedPath = params.webhookPath?.trim();",
" if (trimmedPath) {",
" return normalizeWebhookPath(trimmedPath);",
" }",
" if (params.webhookUrl?.trim()) {",
" try {",
" const parsed = new URL(params.webhookUrl);",
' return normalizeWebhookPath(parsed.pathname || "/");',
" } catch {",
" return null;",
" }",
" }",
" return params.defaultPath ?? null;",
"}",
"",
].join("\n"),
};
const TYPE_SHIMS: Partial<Record<string, string>> = {
"secret-input-runtime": [
"export {",
" hasConfiguredSecretInput,",
" normalizeResolvedSecretInputString,",
" normalizeSecretInputString,",
'} from "./config-runtime.js";',
"",
].join("\n"),
};
// `tsc` emits declarations under `dist/plugin-sdk/src/plugin-sdk/*` because the source lives
// at `src/plugin-sdk/*` and `rootDir` is `.` (repo root, to support cross-src/extensions refs).
//
// Our package export map points subpath `types` at `dist/plugin-sdk/<entry>.d.ts`, so we
// generate stable entry d.ts files that re-export the real declarations.
for (const entry of pluginSdkEntrypoints) {
const out = path.join(process.cwd(), `dist/plugin-sdk/${entry}.d.ts`);
fs.mkdirSync(path.dirname(out), { recursive: true });
// NodeNext: reference the runtime specifier with `.js`, TS will map it to `.d.ts`.
fs.writeFileSync(out, `export * from "./src/plugin-sdk/${entry}.js";\n`, "utf8");
const typeOut = path.join(process.cwd(), `dist/plugin-sdk/${entry}.d.ts`);
fs.mkdirSync(path.dirname(typeOut), { recursive: true });
fs.writeFileSync(
typeOut,
TYPE_SHIMS[entry] ?? `export * from "./src/plugin-sdk/${entry}.js";\n`,
"utf8",
);
const runtimeShim = RUNTIME_SHIMS[entry];
if (!runtimeShim) {
continue;
}
const runtimeOut = path.join(process.cwd(), `dist/plugin-sdk/${entry}.js`);
fs.mkdirSync(path.dirname(runtimeOut), { recursive: true });
fs.writeFileSync(runtimeOut, runtimeShim, "utf8");
}

View File

@ -49,12 +49,7 @@ describe("stageBundledPluginRuntime", () => {
expect(fs.realpathSync(path.join(runtimePluginDir, "node_modules"))).toBe(
fs.realpathSync(sourcePluginNodeModulesDir),
);
// dist/ also gets a node_modules symlink so bare-specifier resolution works
// from the actual code location that the runtime wrapper re-exports into
const distNodeModules = path.join(distPluginDir, "node_modules");
expect(fs.lstatSync(distNodeModules).isSymbolicLink()).toBe(true);
expect(fs.realpathSync(distNodeModules)).toBe(fs.realpathSync(sourcePluginNodeModulesDir));
expect(fs.existsSync(path.join(distPluginDir, "node_modules"))).toBe(false);
});
it("writes wrappers that forward plugin entry imports into canonical dist files", async () => {