Skip to content

fix: omit @strapi devDependencies for local plugins - #202

Draft
singhlovepreet9 wants to merge 1 commit into
strapi:mainfrom
singhlovepreet9:fix-local-plugin-dev-deps
Draft

singhlovepreet9 wants to merge 1 commit into
strapi:mainfrom
singhlovepreet9:fix-local-plugin-dev-deps

Conversation

@singhlovepreet9

Copy link
Copy Markdown

Fixes strapi/strapi#22946

Problem

In Strapi 5, local plugins (which are scaffolded inside a project's src/plugins/ directory) end up with a large number of heavy @strapi/ packages in their devDependencies, like @strapi/strapi, @strapi/design-system, and @strapi/icons. Since most Strapi 5 projects do not have root workspace hoisting enabled by default, running an install step inside a local plugin duplicates these heavy dependencies inside src/plugins/my-plugin/node_modules, leading to massive plugin folder sizes (e.g. 1.6 GB).

Root Cause

@strapi/sdk-plugin init indiscriminately populates devDependencies for both standalone and local plugins.

Fix

This PR utilizes the existing isStrapiProject check to omit heavy @strapi/ packages from the generated devDependencies when a plugin is initialized locally inside a Strapi project. The packages correctly remain in peerDependencies so that sdk-plugin build still externalizes them properly during bundling.

Verification

  • Wrote new unit tests mimicking a local plugin initialization (isStrapiProject = true) and verified devDependencies correctly omits the heavy packages while preserving them in peerDependencies.
  • pnpm run test:unit passes cleanly.
  • pnpm run lint passes cleanly.
  • Fresh-eyes review confirms this strictly targets the root cause with zero risk to standalone plugins.

@changeset-bot

changeset-bot Bot commented Jul 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: df1990f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b88744ef-5574-4ab3-ad04-f4e7cc8eb7c7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@unrevised6419

Copy link
Copy Markdown
Contributor

The problem this targets is real (a 1.6 GB src/plugins/my-plugin/node_modules is nasty), but I don't think dropping the devDependencies is the right fix — I used to think the same thing and it turned out to be wrong for a few concrete reasons.

devDependencies + peerDependencies is one unit, not redundancy

For anything a plugin's own source imports, there are exactly two valid ways to declare it:

  1. dependencies — the plugin owns and ships the dependency; or
  2. peerDependencies + devDependencies — the host provides it at runtime (peer), and the plugin installs it locally so it can typecheck, lint and bundle (dev).

A devDependencies-only entry is the broken case: a consumer installing the plugin never gets devDeps, so nothing imported from source may live there alone. That's exactly why the template lists these packages twice — it's the correct pattern, not a bug.

This PR removes the other half of the pair. A peerDependencies-only entry declares a contract with nothing installed to satisfy it locally, so strapi-plugin build and tsc inside the plugin can only resolve @strapi/design-system / @strapi/icons / @strapi/strapi by walking outside the package boundary — which is precisely what a package manifest exists to avoid.

The "they're already available at the root" premise doesn't hold

A project generated by create-strapi is not a workspace, and src/plugins/* is not a workspace package — there is no hoisting contract to lean on. And @strapi/design-system / @strapi/icons aren't direct dependencies of a generated Strapi 5 project either; they resolve only because npm flattens them into the root node_modules as transitive deps of @strapi/admin. Consequences:

  • the version the plugin builds against is whatever @strapi/admin happened to hoist, not the ^2.0.0 the plugin still declares in peerDependencies;
  • it breaks under pnpm and Yarn PnP, which deliberately don't expose undeclared transitive deps;
  • it breaks the moment the plugin is moved out of src/plugins to be published — which is the documented graduation path for a local plugin, and the one case where a correct manifest matters most.

So the generated plugin would build "by accident" on npm, and not at all on stricter installers.

The size comes from the install, not from the manifest

sdk-plugin init doesn't run an install. The 1.6 GB appears when someone runs npm install inside src/plugins/my-plugin, where a non-workspace nested package necessarily duplicates its whole tree. Fixes that address that without weakening the manifest:

  • register the local plugin from the root — "my-plugin": "file:src/plugins/my-plugin" in the project's package.json, or declare src/plugins/* as a workspace — so a single root install covers it and no nested node_modules is ever created;
  • have init print that guidance (or add the file: entry itself) when isStrapiProject is true, instead of emitting a manifest that only resolves through hoisting luck.

That keeps the plugin self-contained and publishable while still giving the user the small install they're after.

Smaller notes

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.

Strapi 5 local plugins : size has significantly increased

2 participants