fix(plugins): install optional plugin dependencies

This commit is contained in:
Peter Steinberger
2026-04-26 07:00:07 +01:00
parent 54f4c45e5d
commit ee2ab9a644
2 changed files with 48 additions and 4 deletions

View File

@@ -168,18 +168,32 @@ function setupPluginInstallDirs() {
return { tmpDir, pluginDir, extensionsDir };
}
function setupInstallPluginFromDirFixture(params?: { devDependencies?: Record<string, string> }) {
function setupInstallPluginFromDirFixture(params?: {
devDependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
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<string, string>;
devDependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
};
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: {

View File

@@ -32,6 +32,7 @@ type PluginInstallLogger = {
type PackageManifest = PluginPackageManifest & {
dependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
};
@@ -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,