























@@ -934,6 +934,20 @@ describe("uninstallPlugin", () => {
934934const hoistedDir = path.join(npmRoot, "node_modules", "is-number");
935935await fs.mkdir(pluginDir, { recursive: true });
936936await fs.mkdir(hoistedDir, { recursive: true });
937+await fs.writeFile(
938+path.join(npmRoot, "package.json"),
939+`${JSON.stringify(
940+ {
941+ private: true,
942+ dependencies: {
943+ "@openclaw/kitchen-sink": "1.0.0",
944+ "is-number": "7.0.0",
945+ },
946+ },
947+ null,
948+ 2,
949+ )}\n`,
950+);
937951await fs.writeFile(path.join(pluginDir, "package.json"), "{}");
938952await fs.writeFile(path.join(hoistedDir, "package.json"), "{}");
939953@@ -1003,6 +1017,20 @@ describe("uninstallPlugin", () => {
10031017const peerLink = path.join(peerPluginDir, "node_modules", "openclaw");
10041018await fs.mkdir(removedPluginDir, { recursive: true });
10051019await fs.mkdir(path.dirname(peerLink), { recursive: true });
1020+await fs.writeFile(
1021+path.join(npmRoot, "package.json"),
1022+`${JSON.stringify(
1023+ {
1024+ private: true,
1025+ dependencies: {
1026+ "removed-plugin": "1.0.0",
1027+ "peer-plugin": "1.0.0",
1028+ },
1029+ },
1030+ null,
1031+ 2,
1032+ )}\n`,
1033+);
10061034await fs.writeFile(path.join(removedPluginDir, "package.json"), "{}\n");
10071035await fs.writeFile(
10081036path.join(peerPluginDir, "package.json"),
@@ -1054,6 +1082,20 @@ describe("uninstallPlugin", () => {
10541082const peerPluginDir = path.join(npmRoot, "node_modules", "peer-plugin");
10551083const peerLink = path.join(peerPluginDir, "node_modules", "openclaw");
10561084await fs.mkdir(peerLink, { recursive: true });
1085+await fs.writeFile(
1086+path.join(npmRoot, "package.json"),
1087+`${JSON.stringify(
1088+ {
1089+ private: true,
1090+ dependencies: {
1091+ "missing-plugin": "1.0.0",
1092+ "peer-plugin": "1.0.0",
1093+ },
1094+ },
1095+ null,
1096+ 2,
1097+ )}\n`,
1098+);
10571099await fs.writeFile(
10581100path.join(peerPluginDir, "package.json"),
10591101`${JSON.stringify(
@@ -1103,6 +1145,33 @@ describe("uninstallPlugin", () => {
11031145await expect(fs.lstat(peerLink).then((stat) => stat.isSymbolicLink())).resolves.toBe(true);
11041146});
110511471148+it("removes stale npm install config when the managed npm root is already absent", async () => {
1149+const stateDir = path.join(tempDir, "state");
1150+const extensionsDir = path.join(stateDir, "extensions");
1151+const npmRoot = path.join(stateDir, "npm");
1152+const pluginDir = path.join(npmRoot, "node_modules", "missing-plugin");
1153+1154+const result = await uninstallPlugin({
1155+config: createPluginConfig({
1156+entries: createSinglePluginEntries("missing-plugin"),
1157+installs: {
1158+"missing-plugin": createNpmInstallRecord("missing-plugin", pluginDir),
1159+},
1160+}),
1161+pluginId: "missing-plugin",
1162+deleteFiles: true,
1163+ extensionsDir,
1164+});
1165+1166+const successfulResult = expectSuccessfulUninstall(result);
1167+expect(successfulResult.config.plugins).toBeUndefined();
1168+expect(successfulResult.actions.entry).toBe(true);
1169+expect(successfulResult.actions.install).toBe(true);
1170+expect(successfulResult.actions.directory).toBe(false);
1171+expect(successfulResult.warnings).toEqual([]);
1172+expect(runCommandWithTimeoutMock).not.toHaveBeenCalled();
1173+});
1174+11061175it("warns and still removes npm package dirs when npm prune cleanup fails", async () => {
11071176runCommandWithTimeoutMock.mockResolvedValueOnce({
11081177code: 1,
@@ -1117,6 +1186,19 @@ describe("uninstallPlugin", () => {
11171186const npmRoot = path.join(stateDir, "npm");
11181187const pluginDir = path.join(npmRoot, "node_modules", "demo-plugin");
11191188await fs.mkdir(pluginDir, { recursive: true });
1189+await fs.writeFile(
1190+path.join(npmRoot, "package.json"),
1191+`${JSON.stringify(
1192+ {
1193+ private: true,
1194+ dependencies: {
1195+ "demo-plugin": "1.0.0",
1196+ },
1197+ },
1198+ null,
1199+ 2,
1200+ )}\n`,
1201+);
1120120211211203const result = await uninstallPlugin({
11221204config: createPluginConfig({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。