Skip to content

fix(plugin): install monorepo sub-plugin deps when not hoisted#1007

Open
Benjamin-eecs wants to merge 1 commit intojackwener:mainfrom
Benjamin-eecs:fix/monorepo-sub-plugin-deps
Open

fix(plugin): install monorepo sub-plugin deps when not hoisted#1007
Benjamin-eecs wants to merge 1 commit intojackwener:mainfrom
Benjamin-eecs:fix/monorepo-sub-plugin-deps

Conversation

@Benjamin-eecs
Copy link
Copy Markdown

@Benjamin-eecs Benjamin-eecs commented Apr 13, 2026

Summary

Fixes #722.

Monorepo sub-plugins that declare their own production dependencies (e.g. undici) fail at runtime when the monorepo root doesn't hoist those packages (no npm workspaces configured).

This adds a per-sub-plugin npm install for any sub-plugin whose package.json has non-empty dependencies. When the root already satisfies everything, this is a fast no-op.

Changes

  • Added hasOwnDependencies(dir) to check if a sub-plugin has production deps
  • postInstallMonorepoLifecycle now calls installDependencies per sub-plugin when needed
  • Updated and added tests covering both cases (with and without own deps)

Test plan

  • All 74 existing plugin tests pass
  • New test: sub-plugin with dependencies: { undici: "^8.0.0" } gets its own npm install
  • Existing test: sub-plugin without deps still skips the extra install

Copilot AI review requested due to automatic review settings April 13, 2026 21:18
@Benjamin-eecs Benjamin-eecs force-pushed the fix/monorepo-sub-plugin-deps branch from 7e553f8 to c447cb1 Compare April 13, 2026 21:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes monorepo sub-plugin runtime failures when dependencies aren’t hoisted by ensuring sub-plugins with production dependencies get their own install step during the monorepo post-install lifecycle.

Changes:

  • Added hasOwnDependencies(dir) to detect whether a sub-plugin declares production dependencies.
  • Updated postInstallMonorepoLifecycle to run npm install --omit=dev per sub-plugin when needed.
  • Updated/added tests for skipping sub-plugins without deps and installing for sub-plugins with deps.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/plugin.ts Adds dependency detection and conditionally installs deps per sub-plugin in monorepo installs.
src/plugin.test.ts Updates the monorepo lifecycle tests and adds coverage for sub-plugins that declare deps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +583 to +584
} catch {
return false;
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasOwnDependencies swallows JSON parse/read errors and returns false, which can cause sub-plugin installs to silently skip npm install even when a package.json exists but is malformed. That can lead to a successful install followed by a runtime "Cannot find package" error instead of failing early with a clear message. Consider surfacing a PluginError for invalid package.json, or conservatively treating parse failures as "has deps" so installDependencies runs and produces an actionable failure.

Suggested change
} catch {
return false;
} catch (err) {
throw new PluginError(
`Invalid package.json in ${dir}: ${getErrorMessage(err)}`,
'Fix the plugin package.json so dependencies can be determined and installed correctly.',
);

Copilot uses AI. Check for mistakes.
Comment on lines 630 to 636
function postInstallMonorepoLifecycle(repoDir: string, pluginDirs: string[]): void {
installDependencies(repoDir);
for (const pluginDir of pluginDirs) {
if (pluginDir !== repoDir && hasOwnDependencies(pluginDir)) {
installDependencies(pluginDir);
}
finalizePluginRuntime(pluginDir);
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will run npm install --omit=dev inside every sub-plugin that has a non-empty dependencies object. In large monorepos this can multiply install time and may repeatedly touch the workspace/root lockfile even when the root install already covers dependency resolution (e.g., npm workspaces/hoisting). Consider detecting a workspace/hoisting setup at the repo root and skipping per-sub-plugin installs in that case, or otherwise limiting redundant installs (e.g., only when the root has no workspaces and the sub-plugin lacks a usable node_modules).

Copilot uses AI. Check for mistakes.
Comment on lines 642 to 652
@@ -650,6 +650,26 @@ describe('postInstallMonorepoLifecycle', () => {
expect(npmCalls[0][2]).toMatchObject({ cwd: repoDir });
expect(npmCalls.some(([, , opts]) => opts?.cwd === subDir)).toBe(false);
});
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "skips sub-plugins without own dependencies" test currently doesn't create a package.json in subDir, so it can pass because the file is missing rather than because hasOwnDependencies correctly returns false for an existing package.json with empty/missing dependencies. To cover the intended behavior, write a minimal package.json for subDir that has no dependencies (or an empty object) and assert that no npm install runs in subDir.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: cannot find undici when install plugin.

2 participants