diff --git a/src/plugins/install.test.ts b/src/plugins/install.test.ts index 9d39062a779..a0f81ae98b8 100644 --- a/src/plugins/install.test.ts +++ b/src/plugins/install.test.ts @@ -168,18 +168,32 @@ function setupPluginInstallDirs() { return { tmpDir, pluginDir, extensionsDir }; } -function setupInstallPluginFromDirFixture(params?: { devDependencies?: Record }) { +function setupInstallPluginFromDirFixture(params?: { + devDependencies?: Record; + optionalDependencies?: Record; + omitDependencies?: boolean; +}) { const caseDir = suiteTempRootTracker.makeTempDir(); const stateDir = path.join(caseDir, "state"); const pluginDir = path.join(caseDir, "plugin"); fs.mkdirSync(stateDir, { recursive: true }); fs.cpSync(installPluginFromDirTemplateDir, pluginDir, { recursive: true }); - if (params?.devDependencies) { + if (params?.devDependencies || params?.optionalDependencies || params?.omitDependencies) { const packageJsonPath = path.join(pluginDir, "package.json"); const manifest = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as { + dependencies?: Record; devDependencies?: Record; + optionalDependencies?: Record; }; - manifest.devDependencies = params.devDependencies; + if (params.omitDependencies) { + delete manifest.dependencies; + } + if (params.devDependencies) { + manifest.devDependencies = params.devDependencies; + } + if (params.optionalDependencies) { + manifest.optionalDependencies = params.optionalDependencies; + } fs.writeFileSync(packageJsonPath, JSON.stringify(manifest), "utf-8"); } return { pluginDir, extensionsDir: path.join(stateDir, "extensions") }; @@ -2103,6 +2117,32 @@ describe("installPluginFromDir", () => { }); }); + it("runs npm install for optional-only dependencies", async () => { + const { pluginDir, extensionsDir } = setupInstallPluginFromDirFixture({ + omitDependencies: true, + optionalDependencies: { + "left-pad": "1.3.0", + }, + }); + + const run = vi.mocked(runCommandWithTimeout); + mockSuccessfulCommandRun(run); + + const res = await installPluginFromDir({ + dirPath: pluginDir, + extensionsDir, + }); + + expect(res.ok).toBe(true); + if (!res.ok) { + return; + } + expectSingleNpmInstallIgnoreScriptsCall({ + calls: run.mock.calls as Array<[unknown, { cwd?: string } | undefined]>, + expectedTargetDir: res.targetDir, + }); + }); + it("strips workspace devDependencies before npm install", async () => { const { pluginDir, extensionsDir } = setupInstallPluginFromDirFixture({ devDependencies: { diff --git a/src/plugins/install.ts b/src/plugins/install.ts index 74b6b7756f3..0d23636a27b 100644 --- a/src/plugins/install.ts +++ b/src/plugins/install.ts @@ -32,6 +32,7 @@ type PluginInstallLogger = { type PackageManifest = PluginPackageManifest & { dependencies?: Record; + optionalDependencies?: Record; peerDependencies?: Record; }; @@ -797,7 +798,10 @@ async function installPluginFromPackageDir( return scanResult; } - const deps = manifest.dependencies ?? {}; + const deps = { + ...manifest.dependencies, + ...manifest.optionalDependencies, + }; const peerDeps = manifest.peerDependencies ?? {}; return await installPluginDirectoryIntoExtensions({ sourceDir: params.packageDir,