mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
improve: speed up plugin npm package tests (#121989)
* test(packaging): collapse duplicate plugin installs * test(packaging): combine bundled dependency proof * test(packaging): name packed artifact proof --------- Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
committed by
GitHub
parent
79016b415a
commit
33ce7313d7
@@ -388,8 +388,25 @@ describe("plugin npm package manifest staging", () => {
|
|||||||
it("overlays package-local runtime metadata while packing and restores source package json", () => {
|
it("overlays package-local runtime metadata while packing and restores source package json", () => {
|
||||||
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-package-runtime-");
|
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-package-runtime-");
|
||||||
const packageDir = writePublishablePluginPackage(repoDir);
|
const packageDir = writePublishablePluginPackage(repoDir);
|
||||||
|
const sourcePackageJson = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
||||||
|
sourcePackageJson.openclaw.channel = {
|
||||||
|
id: "diffs",
|
||||||
|
configuredState: {
|
||||||
|
specifier: "./configured-state",
|
||||||
|
exportName: "hasConfiguredChannelState",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
writeJsonFile(join(packageDir, "package.json"), sourcePackageJson);
|
||||||
|
writeFileText(
|
||||||
|
join(packageDir, "configured-state.ts"),
|
||||||
|
"export function hasConfiguredChannelState() {}\n",
|
||||||
|
);
|
||||||
writeFileText(join(packageDir, "dist", "index.js"), "export {};\n");
|
writeFileText(join(packageDir, "dist", "index.js"), "export {};\n");
|
||||||
writeFileText(join(packageDir, "dist", "setup-entry.js"), "export {};\n");
|
writeFileText(join(packageDir, "dist", "setup-entry.js"), "export {};\n");
|
||||||
|
writeFileText(
|
||||||
|
join(packageDir, "dist", "configured-state.js"),
|
||||||
|
"export function hasConfiguredChannelState() { return true; }\n",
|
||||||
|
);
|
||||||
|
|
||||||
const resolved = resolveAugmentedPluginNpmPackageJson({
|
const resolved = resolveAugmentedPluginNpmPackageJson({
|
||||||
repoRoot: repoDir,
|
repoRoot: repoDir,
|
||||||
@@ -414,6 +431,13 @@ describe("plugin npm package manifest staging", () => {
|
|||||||
openclaw: {
|
openclaw: {
|
||||||
extensions: ["./index.ts"],
|
extensions: ["./index.ts"],
|
||||||
setupEntry: "./dist/setup-entry.js",
|
setupEntry: "./dist/setup-entry.js",
|
||||||
|
channel: {
|
||||||
|
id: "diffs",
|
||||||
|
configuredState: {
|
||||||
|
specifier: "./dist/configured-state.js",
|
||||||
|
exportName: "hasConfiguredChannelState",
|
||||||
|
},
|
||||||
|
},
|
||||||
compat: {
|
compat: {
|
||||||
pluginApi: ">=2026.4.30",
|
pluginApi: ">=2026.4.30",
|
||||||
},
|
},
|
||||||
@@ -436,6 +460,9 @@ describe("plugin npm package manifest staging", () => {
|
|||||||
expect(stagedPackageJson.openclaw.runtimeExtensions).toEqual(["./dist/index.js"]);
|
expect(stagedPackageJson.openclaw.runtimeExtensions).toEqual(["./dist/index.js"]);
|
||||||
expect(stagedPackageJson.openclaw.setupEntry).toBe("./dist/setup-entry.js");
|
expect(stagedPackageJson.openclaw.setupEntry).toBe("./dist/setup-entry.js");
|
||||||
expect(stagedPackageJson.openclaw.runtimeSetupEntry).toBe("./dist/setup-entry.js");
|
expect(stagedPackageJson.openclaw.runtimeSetupEntry).toBe("./dist/setup-entry.js");
|
||||||
|
expect(stagedPackageJson.openclaw.channel.configuredState.specifier).toBe(
|
||||||
|
"./dist/configured-state.js",
|
||||||
|
);
|
||||||
expect(stagedPackageJson.bundledDependencies).toEqual([]);
|
expect(stagedPackageJson.bundledDependencies).toEqual([]);
|
||||||
expect(stagedPackageJson.bundleDependencies).toBeUndefined();
|
expect(stagedPackageJson.bundleDependencies).toBeUndefined();
|
||||||
expect(stagedPackageJson.files).toContain("dist/**");
|
expect(stagedPackageJson.files).toContain("dist/**");
|
||||||
@@ -448,159 +475,135 @@ describe("plugin npm package manifest staging", () => {
|
|||||||
expect(readFileSync(join(packageDir, "package.json"), "utf8")).toBe(originalText);
|
expect(readFileSync(join(packageDir, "package.json"), "utf8")).toBe(originalText);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each(
|
it("packs and loads both mapped channel-state probes from one package artifact", () => {
|
||||||
[
|
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-package-state-runtime-");
|
||||||
{
|
const packageDir = writePublishablePluginPackage(repoDir);
|
||||||
label: "ESM configured-state",
|
const sourcePackageJson = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
||||||
metadataKey: "configuredState",
|
sourcePackageJson.openclaw.build = { runtimeFormat: "cjs" };
|
||||||
runtimeFormat: "esm",
|
sourcePackageJson.openclaw.channel = {
|
||||||
sourceName: "configured-state",
|
id: "diffs",
|
||||||
|
configuredState: {
|
||||||
|
specifier: "./configured-state",
|
||||||
exportName: "hasConfiguredChannelState",
|
exportName: "hasConfiguredChannelState",
|
||||||
},
|
},
|
||||||
{
|
persistedAuthState: {
|
||||||
label: "CommonJS configured-state",
|
specifier: "./dist/auth-presence.cjs",
|
||||||
metadataKey: "configuredState",
|
exportName: "hasPersistedChannelAuth",
|
||||||
runtimeFormat: "cjs",
|
},
|
||||||
sourceName: "configured-state",
|
};
|
||||||
|
writeJsonFile(join(packageDir, "package.json"), sourcePackageJson);
|
||||||
|
writeFileText(
|
||||||
|
join(packageDir, "configured-state.ts"),
|
||||||
|
"export function hasConfiguredChannelState() {}\n",
|
||||||
|
);
|
||||||
|
writeFileText(
|
||||||
|
join(packageDir, "auth-presence.ts"),
|
||||||
|
"export function hasPersistedChannelAuth() {}\n",
|
||||||
|
);
|
||||||
|
writeFileText(join(packageDir, "dist", "index.cjs"), "module.exports = {};\n");
|
||||||
|
writeFileText(join(packageDir, "dist", "setup-entry.cjs"), "module.exports = {};\n");
|
||||||
|
writeFileText(
|
||||||
|
join(packageDir, "dist", "configured-state.cjs"),
|
||||||
|
"exports.hasConfiguredChannelState = () => true;\n",
|
||||||
|
);
|
||||||
|
writeFileText(
|
||||||
|
join(packageDir, "dist", "auth-presence.cjs"),
|
||||||
|
"exports.hasPersistedChannelAuth = () => true;\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
const originalText = readFileSync(join(packageDir, "package.json"), "utf8");
|
||||||
|
withAugmentedPluginNpmManifestForPackage({ repoRoot: repoDir, packageDir }, () => {
|
||||||
|
const stagedPackageJson = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
||||||
|
expect(stagedPackageJson.openclaw.channel.configuredState).toEqual({
|
||||||
|
specifier: "./dist/configured-state.cjs",
|
||||||
exportName: "hasConfiguredChannelState",
|
exportName: "hasConfiguredChannelState",
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "ESM persisted-auth state",
|
|
||||||
metadataKey: "persistedAuthState",
|
|
||||||
runtimeFormat: "esm",
|
|
||||||
sourceName: "auth-presence",
|
|
||||||
exportName: "hasPersistedChannelAuth",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "CommonJS persisted-auth state",
|
|
||||||
metadataKey: "persistedAuthState",
|
|
||||||
runtimeFormat: "cjs",
|
|
||||||
sourceName: "auth-presence",
|
|
||||||
exportName: "hasPersistedChannelAuth",
|
|
||||||
},
|
|
||||||
].flatMap((testCase) => [
|
|
||||||
{ ...testCase, label: `${testCase.label} from source`, specifierKind: "source" },
|
|
||||||
{ ...testCase, label: `${testCase.label} from built runtime`, specifierKind: "runtime" },
|
|
||||||
]),
|
|
||||||
)(
|
|
||||||
"packs and loads $label from the actual installed plugin runtime",
|
|
||||||
({ metadataKey, runtimeFormat, sourceName, exportName, specifierKind }) => {
|
|
||||||
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-package-state-runtime-");
|
|
||||||
const packageDir = writePublishablePluginPackage(repoDir);
|
|
||||||
const extension = runtimeFormat === "cjs" ? ".cjs" : ".js";
|
|
||||||
const sourceSpecifier = `./${sourceName}`;
|
|
||||||
const runtimeSpecifier = `./dist/${sourceName}${extension}`;
|
|
||||||
const sourcePackageJson = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
|
||||||
sourcePackageJson.openclaw.channel = {
|
|
||||||
id: "diffs",
|
|
||||||
[metadataKey]: {
|
|
||||||
specifier: specifierKind === "runtime" ? runtimeSpecifier : sourceSpecifier,
|
|
||||||
exportName,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (runtimeFormat === "cjs") {
|
|
||||||
sourcePackageJson.openclaw.build = { runtimeFormat: "cjs" };
|
|
||||||
}
|
|
||||||
writeJsonFile(join(packageDir, "package.json"), sourcePackageJson);
|
|
||||||
writeFileText(join(packageDir, `${sourceName}.ts`), `export function ${exportName}() {}\n`);
|
|
||||||
writeFileText(join(packageDir, "dist", `index${extension}`), "export {};\n");
|
|
||||||
writeFileText(join(packageDir, "dist", `setup-entry${extension}`), "export {};\n");
|
|
||||||
writeFileText(
|
|
||||||
join(packageDir, "dist", `${sourceName}${extension}`),
|
|
||||||
runtimeFormat === "cjs"
|
|
||||||
? `exports.${exportName} = () => true;\n`
|
|
||||||
: `export function ${exportName}() { return true; }\n`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const originalText = readFileSync(join(packageDir, "package.json"), "utf8");
|
|
||||||
withAugmentedPluginNpmManifestForPackage({ repoRoot: repoDir, packageDir }, () => {
|
|
||||||
const stagedPackageJson = JSON.parse(
|
|
||||||
readFileSync(join(packageDir, "package.json"), "utf8"),
|
|
||||||
);
|
|
||||||
expect(stagedPackageJson.openclaw.channel[metadataKey]).toEqual({
|
|
||||||
specifier: runtimeSpecifier,
|
|
||||||
exportName,
|
|
||||||
});
|
|
||||||
|
|
||||||
const packedFiles = listNpmPackDryRunFiles(packageDir);
|
|
||||||
expect(packedFiles).toContain(runtimeSpecifier.slice(2));
|
|
||||||
expect(packedFiles).not.toContain(`${sourceName}.ts`);
|
|
||||||
|
|
||||||
const consumerDir = join(repoDir, "external-consumer");
|
|
||||||
mkdirSync(consumerDir, { recursive: true });
|
|
||||||
writeJsonFile(join(consumerDir, "package.json"), { private: true, type: "module" });
|
|
||||||
|
|
||||||
const packInvocation = resolvePluginNpmCommand([
|
|
||||||
"pack",
|
|
||||||
"--json",
|
|
||||||
"--ignore-scripts",
|
|
||||||
"--pack-destination",
|
|
||||||
consumerDir,
|
|
||||||
]);
|
|
||||||
const pack = spawnSync(packInvocation.command, packInvocation.args, {
|
|
||||||
cwd: packageDir,
|
|
||||||
encoding: "utf8",
|
|
||||||
...(packInvocation.env ? { env: packInvocation.env } : {}),
|
|
||||||
...(packInvocation.shell !== undefined ? { shell: packInvocation.shell } : {}),
|
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
|
||||||
...(packInvocation.windowsVerbatimArguments !== undefined
|
|
||||||
? { windowsVerbatimArguments: packInvocation.windowsVerbatimArguments }
|
|
||||||
: {}),
|
|
||||||
});
|
|
||||||
expect(pack.status, pack.stderr).toBe(0);
|
|
||||||
const [packedPackage] = JSON.parse(pack.stdout) as [{ filename: string }];
|
|
||||||
|
|
||||||
const installInvocation = resolvePluginNpmCommand([
|
|
||||||
"install",
|
|
||||||
"--ignore-scripts",
|
|
||||||
"--omit=peer",
|
|
||||||
"--no-audit",
|
|
||||||
"--no-fund",
|
|
||||||
"--package-lock=false",
|
|
||||||
join(consumerDir, packedPackage.filename),
|
|
||||||
]);
|
|
||||||
const install = spawnSync(installInvocation.command, installInvocation.args, {
|
|
||||||
cwd: consumerDir,
|
|
||||||
encoding: "utf8",
|
|
||||||
...(installInvocation.env ? { env: installInvocation.env } : {}),
|
|
||||||
...(installInvocation.shell !== undefined ? { shell: installInvocation.shell } : {}),
|
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
|
||||||
...(installInvocation.windowsVerbatimArguments !== undefined
|
|
||||||
? { windowsVerbatimArguments: installInvocation.windowsVerbatimArguments }
|
|
||||||
: {}),
|
|
||||||
});
|
|
||||||
expect(install.status, install.stderr).toBe(0);
|
|
||||||
|
|
||||||
const installedRoot = join(consumerDir, "node_modules", "@openclaw", "diffs");
|
|
||||||
const load = spawnSync(
|
|
||||||
process.execPath,
|
|
||||||
[
|
|
||||||
"--input-type=module",
|
|
||||||
"--eval",
|
|
||||||
`import fs from "node:fs";\n` +
|
|
||||||
`import { pathToFileURL } from "node:url";\n` +
|
|
||||||
`const root = ${JSON.stringify(installedRoot)};\n` +
|
|
||||||
`const pkg = JSON.parse(fs.readFileSync(root + "/package.json", "utf8"));\n` +
|
|
||||||
`const state = pkg.openclaw.channel[${JSON.stringify(metadataKey)}];\n` +
|
|
||||||
`const loaded = await import(new URL(state.specifier, pathToFileURL(root + "/")));\n` +
|
|
||||||
`if (loaded[state.exportName]?.() !== true) throw new Error("installed state checker failed");\n` +
|
|
||||||
`process.stdout.write("INSTALLED_PLUGIN_CHANNEL_STATE_OK\\n");\n`,
|
|
||||||
],
|
|
||||||
{ cwd: consumerDir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
|
|
||||||
);
|
|
||||||
expect(load.status, load.stderr).toBe(0);
|
|
||||||
expect(load.stdout).toBe("INSTALLED_PLUGIN_CHANNEL_STATE_OK\n");
|
|
||||||
});
|
});
|
||||||
expect(readFileSync(join(packageDir, "package.json"), "utf8")).toBe(originalText);
|
expect(stagedPackageJson.openclaw.channel.persistedAuthState).toEqual({
|
||||||
},
|
specifier: "./dist/auth-presence.cjs",
|
||||||
);
|
exportName: "hasPersistedChannelAuth",
|
||||||
|
});
|
||||||
|
|
||||||
it("installs and cleans package-local bundled dependencies while packing", () => {
|
const consumerDir = join(repoDir, "external-consumer");
|
||||||
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-package-bundled-deps-");
|
mkdirSync(consumerDir, { recursive: true });
|
||||||
|
writeJsonFile(join(consumerDir, "package.json"), { private: true, type: "module" });
|
||||||
|
|
||||||
|
const packInvocation = resolvePluginNpmCommand([
|
||||||
|
"pack",
|
||||||
|
"--json",
|
||||||
|
"--ignore-scripts",
|
||||||
|
"--pack-destination",
|
||||||
|
consumerDir,
|
||||||
|
]);
|
||||||
|
const pack = spawnSync(packInvocation.command, packInvocation.args, {
|
||||||
|
cwd: packageDir,
|
||||||
|
encoding: "utf8",
|
||||||
|
...(packInvocation.env ? { env: packInvocation.env } : {}),
|
||||||
|
...(packInvocation.shell !== undefined ? { shell: packInvocation.shell } : {}),
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
...(packInvocation.windowsVerbatimArguments !== undefined
|
||||||
|
? { windowsVerbatimArguments: packInvocation.windowsVerbatimArguments }
|
||||||
|
: {}),
|
||||||
|
});
|
||||||
|
expect(pack.status, pack.stderr).toBe(0);
|
||||||
|
const [packedPackage] = JSON.parse(pack.stdout) as [
|
||||||
|
{ filename: string; files: Array<{ path: string }> },
|
||||||
|
];
|
||||||
|
const packedFiles = packedPackage.files.map((file) => file.path);
|
||||||
|
expect(packedFiles).toContain("dist/configured-state.cjs");
|
||||||
|
expect(packedFiles).toContain("dist/auth-presence.cjs");
|
||||||
|
expect(packedFiles).not.toContain("configured-state.ts");
|
||||||
|
expect(packedFiles).not.toContain("auth-presence.ts");
|
||||||
|
|
||||||
|
const extract = spawnSync(
|
||||||
|
"tar",
|
||||||
|
["-xzf", join(consumerDir, packedPackage.filename), "-C", consumerDir],
|
||||||
|
{
|
||||||
|
encoding: "utf8",
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(extract.status, extract.stderr).toBe(0);
|
||||||
|
|
||||||
|
const packageRoot = join(consumerDir, "package");
|
||||||
|
const load = spawnSync(
|
||||||
|
process.execPath,
|
||||||
|
[
|
||||||
|
"--input-type=module",
|
||||||
|
"--eval",
|
||||||
|
`
|
||||||
|
import fs from "node:fs";
|
||||||
|
import { pathToFileURL } from "node:url";
|
||||||
|
const root = ${JSON.stringify(packageRoot)};
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(root + "/package.json", "utf8"));
|
||||||
|
for (const key of ["configuredState", "persistedAuthState"]) {
|
||||||
|
const state = pkg.openclaw.channel[key];
|
||||||
|
const loaded = await import(new URL(state.specifier, pathToFileURL(root + "/")));
|
||||||
|
if (loaded[state.exportName]?.() !== true) throw new Error("packed state checker failed: " + key);
|
||||||
|
}
|
||||||
|
process.stdout.write("PACKED_PLUGIN_CHANNEL_STATE_OK\\n");
|
||||||
|
`,
|
||||||
|
],
|
||||||
|
{
|
||||||
|
cwd: packageRoot,
|
||||||
|
encoding: "utf8",
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(load.status, load.stderr).toBe(0);
|
||||||
|
expect(load.stdout).toBe("PACKED_PLUGIN_CHANNEL_STATE_OK\n");
|
||||||
|
});
|
||||||
|
expect(readFileSync(join(packageDir, "package.json"), "utf8")).toBe(originalText);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("stages portable bundled dependencies without polluting pack output", () => {
|
||||||
|
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-package-portable-optional-");
|
||||||
const packageDir = writePublishablePluginPackage(repoDir);
|
const packageDir = writePublishablePluginPackage(repoDir);
|
||||||
writeFileText(join(packageDir, "dist", "index.js"), "export {};\n");
|
writeFileText(join(packageDir, "dist", "index.js"), "export {};\n");
|
||||||
writeFileText(join(packageDir, "dist", "setup-entry.js"), "export {};\n");
|
writeFileText(join(packageDir, "dist", "setup-entry.js"), "export {};\n");
|
||||||
writeLocalDependencyPackage(packageDir);
|
writeOptionalPlatformDependencyPackage(packageDir);
|
||||||
|
writeLocalDependencyPackage(packageDir, {
|
||||||
|
optionalDependencySpec: "file:../../deps/optional-platform-dep",
|
||||||
|
});
|
||||||
writeJsonFile(join(packageDir, "package.json"), {
|
writeJsonFile(join(packageDir, "package.json"), {
|
||||||
name: "@openclaw/diffs",
|
name: "@openclaw/diffs",
|
||||||
version: "2026.5.3",
|
version: "2026.5.3",
|
||||||
@@ -624,69 +627,19 @@ describe("plugin npm package manifest staging", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const originalText = readFileSync(join(packageDir, "package.json"), "utf8");
|
const originalText = readFileSync(join(packageDir, "package.json"), "utf8");
|
||||||
const nodeModulesPath = join(packageDir, "node_modules");
|
|
||||||
expect(existsSync(nodeModulesPath)).toBe(false);
|
|
||||||
|
|
||||||
withAugmentedPluginNpmManifestForPackage(
|
|
||||||
{ repoRoot: repoDir, packageDir, bundleDependencies: true },
|
|
||||||
() => {
|
|
||||||
const stagedPackageJson = JSON.parse(
|
|
||||||
readFileSync(join(packageDir, "package.json"), "utf8"),
|
|
||||||
);
|
|
||||||
expect(stagedPackageJson.bundledDependencies).toEqual(["local-runtime-dep"]);
|
|
||||||
expect(stagedPackageJson.bundleDependencies).toBeUndefined();
|
|
||||||
expect(stagedPackageJson.devDependencies).toBeUndefined();
|
|
||||||
expect(existsSync(join(nodeModulesPath, "local-runtime-dep", "package.json"))).toBe(true);
|
|
||||||
expect(existsSync(join(packageDir, "package-lock.json"))).toBe(false);
|
|
||||||
const packedFiles = listNpmPackDryRunFiles(packageDir);
|
|
||||||
expect(packedFiles).toContain("node_modules/local-runtime-dep/package.json");
|
|
||||||
expect(packedFiles).not.toContain("package-lock.json");
|
|
||||||
expect(packedFiles).not.toContain("npm-shrinkwrap.json");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(existsSync(nodeModulesPath)).toBe(false);
|
|
||||||
expect(existsSync(join(packageDir, "package-lock.json"))).toBe(false);
|
|
||||||
expect(readFileSync(join(packageDir, "package.json"), "utf8")).toBe(originalText);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("force-installs missing optional bundled dependencies for portable packs", () => {
|
|
||||||
const repoDir = makeTempRepoRoot(tempDirs, "openclaw-plugin-npm-package-portable-optional-");
|
|
||||||
const packageDir = writePublishablePluginPackage(repoDir);
|
|
||||||
writeFileText(join(packageDir, "dist", "index.js"), "export {};\n");
|
|
||||||
writeFileText(join(packageDir, "dist", "setup-entry.js"), "export {};\n");
|
|
||||||
writeOptionalPlatformDependencyPackage(packageDir);
|
|
||||||
writeLocalDependencyPackage(packageDir, {
|
|
||||||
optionalDependencySpec: "file:../../deps/optional-platform-dep",
|
|
||||||
});
|
|
||||||
writeJsonFile(join(packageDir, "package.json"), {
|
|
||||||
name: "@openclaw/diffs",
|
|
||||||
version: "2026.5.3",
|
|
||||||
type: "module",
|
|
||||||
dependencies: {
|
|
||||||
"local-runtime-dep": "file:./deps/local-runtime-dep",
|
|
||||||
},
|
|
||||||
openclaw: {
|
|
||||||
extensions: ["./index.ts"],
|
|
||||||
setupEntry: "./setup-entry.ts",
|
|
||||||
compat: {
|
|
||||||
pluginApi: ">=2026.4.30",
|
|
||||||
},
|
|
||||||
release: {
|
|
||||||
publishToNpm: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const nodeModulesPath = join(packageDir, "node_modules");
|
const nodeModulesPath = join(packageDir, "node_modules");
|
||||||
const manifestModuleUrl = new URL(
|
const manifestModuleUrl = new URL(
|
||||||
"../scripts/lib/plugin-npm-package-manifest.mts",
|
"../scripts/lib/plugin-npm-package-manifest.mts",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
).href;
|
).href;
|
||||||
const childSource = `
|
const childSource = `
|
||||||
import { existsSync } from "node:fs";
|
import { spawnSync } from "node:child_process";
|
||||||
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { withAugmentedPluginNpmManifestForPackage } from ${JSON.stringify(manifestModuleUrl)};
|
import {
|
||||||
|
resolvePluginNpmCommand,
|
||||||
|
withAugmentedPluginNpmManifestForPackage,
|
||||||
|
} from ${JSON.stringify(manifestModuleUrl)};
|
||||||
|
|
||||||
const packageDir = ${JSON.stringify(packageDir)};
|
const packageDir = ${JSON.stringify(packageDir)};
|
||||||
const nodeModulesPath = ${JSON.stringify(nodeModulesPath)};
|
const nodeModulesPath = ${JSON.stringify(nodeModulesPath)};
|
||||||
@@ -697,12 +650,42 @@ withAugmentedPluginNpmManifestForPackage(
|
|||||||
bundleDependencies: true,
|
bundleDependencies: true,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
|
const stagedPackageJson = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
||||||
|
if (JSON.stringify(stagedPackageJson.bundledDependencies) !== '["local-runtime-dep"]') {
|
||||||
|
throw new Error("bundled dependencies were not staged");
|
||||||
|
}
|
||||||
|
if (stagedPackageJson.bundleDependencies || stagedPackageJson.devDependencies) {
|
||||||
|
throw new Error("unpublishable dependency metadata remained staged");
|
||||||
|
}
|
||||||
if (!existsSync(join(nodeModulesPath, "local-runtime-dep", "package.json"))) {
|
if (!existsSync(join(nodeModulesPath, "local-runtime-dep", "package.json"))) {
|
||||||
throw new Error("missing bundled runtime dependency");
|
throw new Error("missing bundled runtime dependency");
|
||||||
}
|
}
|
||||||
if (!existsSync(join(nodeModulesPath, "optional-platform-dep", "package.json"))) {
|
if (!existsSync(join(nodeModulesPath, "optional-platform-dep", "package.json"))) {
|
||||||
throw new Error("missing portable optional bundled dependency");
|
throw new Error("missing portable optional bundled dependency");
|
||||||
}
|
}
|
||||||
|
if (existsSync(join(packageDir, "package-lock.json"))) {
|
||||||
|
throw new Error("package lock remained staged");
|
||||||
|
}
|
||||||
|
const invocation = resolvePluginNpmCommand(["pack", "--dry-run", "--json", "--ignore-scripts"]);
|
||||||
|
const pack = spawnSync(invocation.command, invocation.args, {
|
||||||
|
cwd: packageDir,
|
||||||
|
encoding: "utf8",
|
||||||
|
...(invocation.env ? { env: invocation.env } : {}),
|
||||||
|
...(invocation.shell !== undefined ? { shell: invocation.shell } : {}),
|
||||||
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
...(invocation.windowsVerbatimArguments !== undefined
|
||||||
|
? { windowsVerbatimArguments: invocation.windowsVerbatimArguments }
|
||||||
|
: {}),
|
||||||
|
});
|
||||||
|
if (pack.status !== 0) throw new Error(pack.stderr || "npm pack failed");
|
||||||
|
const [packedPackage] = JSON.parse(pack.stdout);
|
||||||
|
const packedFiles = packedPackage.files.map((file) => file.path);
|
||||||
|
if (!packedFiles.includes("node_modules/local-runtime-dep/package.json")) {
|
||||||
|
throw new Error("bundled runtime dependency was not packed");
|
||||||
|
}
|
||||||
|
if (packedFiles.includes("package-lock.json") || packedFiles.includes("npm-shrinkwrap.json")) {
|
||||||
|
throw new Error("package lock was packed");
|
||||||
|
}
|
||||||
process.stdout.write("pack-json\\n");
|
process.stdout.write("pack-json\\n");
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -721,6 +704,8 @@ withAugmentedPluginNpmManifestForPackage(
|
|||||||
expect(result.stdout).toBe("pack-json\n");
|
expect(result.stdout).toBe("pack-json\n");
|
||||||
|
|
||||||
expect(existsSync(nodeModulesPath)).toBe(false);
|
expect(existsSync(nodeModulesPath)).toBe(false);
|
||||||
|
expect(existsSync(join(packageDir, "package-lock.json"))).toBe(false);
|
||||||
|
expect(readFileSync(join(packageDir, "package.json"), "utf8")).toBe(originalText);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("honors plugin package opt-out for bundled runtime dependencies", () => {
|
it("honors plugin package opt-out for bundled runtime dependencies", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user