refactor(config): remove rawLocalConfig and force* hoist flags#11199
Conversation
rawLocalConfig detected whether hoist settings were explicitly set. In v11, config values are always authoritative. - Remove rawLocalConfig from ConfigContext, config reader, inheritPickedConfig, UniversalOptions - Remove forceHoistPattern, forcePublicHoistPattern, forceShamefullyHoist — validateModules always checks now - Simplify save-workspace-protocol check - Remove dead rawLocalConfig overrides in deploy/patchCommit
There was a problem hiding this comment.
Pull request overview
This PR refactors config handling by removing rawLocalConfig from the config context/options plumbing and deleting “force* hoist” flags, making configured hoist patterns always authoritative during module validation. It also simplifies workspace save-workspace-protocol conflict handling to always throw.
Changes:
- Remove
rawLocalConfigfromConfigContext/UniversalOptionsand update config inheritance + tests/util defaults accordingly. - Remove
forceHoistPattern/forcePublicHoistPattern/forceShamefullyHoistand makevalidateModulesalways check hoist/public-hoist pattern mismatches. - Simplify
installDepsworkspace conflict logic to always throw whenlink-workspace-packages=falseandsave-workspace-protocol=false.
Reviewed changes
Copilot reviewed 39 out of 39 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| releasing/commands/test/publish/utils/index.ts | Drop rawLocalConfig from test default options. |
| releasing/commands/test/deploy/utils/index.ts | Drop rawLocalConfig from test default options. |
| releasing/commands/src/deploy/deploy.ts | Remove rawLocalConfig overrides; rely on explicit frozen-lockfile options in deploy flows. |
| patching/commands/test/utils/index.ts | Drop rawLocalConfig from test default options. |
| patching/commands/src/patchCommit.ts | Remove rawLocalConfig override when calling install.handler(). |
| installing/deps-installer/test/install/hoist.ts | Update hoist mismatch tests to reflect removal of forceHoistPattern. |
| installing/deps-installer/src/install/validateModules.ts | Remove force flags from API and make hoist/public-hoist checks unconditional. |
| installing/deps-installer/src/install/index.ts | Stop passing force hoist flags into validateModules. |
| installing/deps-installer/src/install/extendInstallOptions.ts | Remove force* fields from strict install options typings. |
| installing/context/src/index.ts | Remove force hoist/public-hoist flags from context option types. |
| installing/commands/test/utils/index.ts | Drop rawLocalConfig from test default options. |
| installing/commands/test/update/issue-7415.ts | Drop rawLocalConfig from test default options. |
| installing/commands/test/update/interactive.ts | Drop rawLocalConfig from test default options. |
| installing/commands/test/prune.ts | Drop rawLocalConfig from test default options. |
| installing/commands/test/peerDependencies.ts | Drop rawLocalConfig from test default options and remove raw override. |
| installing/commands/test/fetch.ts | Drop rawLocalConfig from test default options. |
| installing/commands/test/add.ts | Drop rawLocalConfig from test default options and remove raw override path. |
| installing/commands/src/remove.ts | Remove rawLocalConfig from handler option pick list. |
| installing/commands/src/recursive.ts | Remove rawLocalConfig + force hoist flags from recursive options and plumbing. |
| installing/commands/src/installDeps.ts | Remove rawLocalConfig usage; always throw on workspace save-workspace-protocol conflict; remove force-hoist derivation. |
| installing/commands/src/install.ts | Remove rawLocalConfig from install command option type. |
| exec/commands/test/utils/index.ts | Drop rawLocalConfig from exec/dlx test default options. |
| engine/runtime/commands/src/env/node.ts | Remove rawLocalConfig from env/node command options typing. |
| engine/pm/commands/test/self-updater/selfUpdate.test.ts | Drop rawLocalConfig from self-updater test options. |
| deps/inspection/commands/test/outdated/utils/index.ts | Drop rawLocalConfig from test default options. |
| deps/inspection/commands/test/listing/utils/index.ts | Drop rawLocalConfig from test default options. |
| deps/compliance/commands/test/sbom/utils/index.ts | Drop rawLocalConfig from test default options. |
| deps/compliance/commands/test/licenses/utils/index.ts | Drop rawLocalConfig from test default options. |
| deps/compliance/commands/test/audit/utils/options.ts | Drop rawLocalConfig from test default options. |
| config/reader/test/index.ts | Remove skipped tests that asserted rawLocalConfig behavior. |
| config/reader/src/loadNpmrcFiles.ts | Update workspace npmrc field comment (no longer “for rawLocalConfig”). |
| config/reader/src/inheritPickedConfig.ts | Remove rawLocalConfig inheritance from picked-config helper. |
| config/reader/src/index.ts | Stop constructing rawLocalConfig; remove it from returned context. |
| config/reader/src/Config.ts | Remove rawLocalConfig from UniversalOptions and ConfigContext. |
| config/reader/src/auth.test.ts | Update auth inheritance tests to no longer assert rawLocalConfig merging. |
| config/commands/test/utils/index.ts | Drop rawLocalConfig from config-command test context. |
| building/commands/test/build/utils/index.ts | Drop rawLocalConfig from build test default options. |
| building/commands/src/build/recursive.ts | Remove rawLocalConfig from build recursive option typing. |
| building/commands/src/build/rebuild.ts | Remove rawLocalConfig from rebuild option typing. |
Comments suppressed due to low confidence (2)
installing/deps-installer/src/install/validateModules.ts:75
- validateModules() now always checks publicHoistPattern mismatch, but when opts.forceNewModules is true and the workspace root is not an importer (rootProject == null), this path throws instead of purging the lockfileDir/node_modules (unlike the later compatibility checks that add a synthetic importer when rootProject is null). Consider purging the lockfileDir modules dir here as well when rootProject is null (or pushing a synthetic importer into importersToPurge) so filtered/workspace-root-less installs behave consistently.
installing/deps-installer/src/install/validateModules.ts:93 - The hoistPattern mismatch check is now unconditional but only runs when rootProject != null. In workspaces where the lockfileDir/root is not an importer (rootProject == null), hoist-pattern differences in .modules.yaml will no longer be detected/purged. Consider running this comparison against the root modules directory regardless of rootProject presence (and purge lockfileDir/node_modules when forceNewModules is true).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return install.handler({ | ||
| ...opts, | ||
| patchedDependencies, | ||
| rawLocalConfig: { | ||
| ...opts.rawLocalConfig, | ||
| 'frozen-lockfile': false, | ||
| }, | ||
| }) as Promise<undefined> |
There was a problem hiding this comment.
patch-commit invokes install.handler() after updating patchedDependencies, which may require updating the lockfile. With the rawLocalConfig override removed, this call will now respect a workspace/user frozen-lockfile setting and can fail unexpectedly. Consider explicitly setting frozenLockfile: false (and/or preferFrozenLockfile: false) for this install invocation to preserve the previous behavior and avoid blocking patch-commit when frozen-lockfile is enabled in config.
patch-commit needs to update the lockfile after changing patchedDependencies, so frozen lockfile must be disabled.
Instead of throwing when linkWorkspacePackages is off and saveWorkspaceProtocol is falsy, auto-enable the workspace protocol. The previous error was only useful when the user explicitly passed --no-save-workspace-protocol, which required rawLocalConfig to detect.
Now that hoist pattern mismatches are always validated, the test triggers node_modules purge which needs TTY confirmation disabled.
Now that hoist pattern mismatches are always validated, e2e tests must use consistent hoist settings across commands operating on the same node_modules.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!opts.linkWorkspacePackages && !opts.saveWorkspaceProtocol) { | ||
| if (opts.rawLocalConfig['save-workspace-protocol'] === false) { | ||
| throw new PnpmError('BAD_OPTIONS', 'This workspace has link-workspace-packages turned off, \ | ||
| so dependencies are linked from the workspace only when the workspace protocol is used. \ | ||
| Either set link-workspace-packages to true or don\'t use the --no-save-workspace-protocol option \ | ||
| when running add/update with the --workspace option') | ||
| } else { | ||
| opts.saveWorkspaceProtocol = true | ||
| } | ||
| opts.saveWorkspaceProtocol = true | ||
| } |
There was a problem hiding this comment.
The new logic forces saveWorkspaceProtocol to true when workspace is used with linkWorkspacePackages=false, even if saveWorkspaceProtocol was explicitly set to false by the caller/CLI. This contradicts the PR description which says we “always throw on conflict”; either update the PR description (and any related docs/changeset) to reflect the new behavior, or reintroduce an explicit-conflict error using cliOptions/explicitlySetKeys to detect --no-save-workspace-protocol.
pnpm remove does not accept --shamefully-hoist flag, so set it in pnpm-workspace.yaml instead to keep it consistent across add and remove commands.
Closes #11488. `pnpm fetch` writes forced-empty `hoistPattern: []` and `publicHoistPattern: []` into `.modules.yaml` (because its `virtualStoreOnly` install path skips hoisting). In v10 the follow-up `pnpm install` ignored these unless the user had explicitly set a hoist-pattern in their config. v11's [#11199](#11199) removed that explicit-config gate, so `validateModules` now always sees the empty patterns as a hoist-pattern change and purges `node_modules` — slow on every CI run, and per the bug report sometimes leaves the modules dir in an `ERR_MODULE_NOT_FOUND` state on subsequent runs. The fix marks `.modules.yaml` with a new `virtualStoreOnly: true` field after a fetch. `validateModules` recognizes this flag as "incomplete install state" and skips the `PUBLIC_HOIST_PATTERN_DIFF` / `HOIST_PATTERN_DIFF` comparisons. The next install then completes the missing post-import linking in place rather than purging. The flag is dropped from `.modules.yaml` once a normal install runs. A genuine hoist-pattern change (without a fetch in between) still triggers the purge as before — verified manually with `publicHoistPattern` in `pnpm-workspace.yaml`.
Closes #11488. `pnpm fetch` writes forced-empty `hoistPattern: []` and `publicHoistPattern: []` into `.modules.yaml` (because its `virtualStoreOnly` install path skips hoisting). In v10 the follow-up `pnpm install` ignored these unless the user had explicitly set a hoist-pattern in their config. v11's [#11199](#11199) removed that explicit-config gate, so `validateModules` now always sees the empty patterns as a hoist-pattern change and purges `node_modules` — slow on every CI run, and per the bug report sometimes leaves the modules dir in an `ERR_MODULE_NOT_FOUND` state on subsequent runs. The fix marks `.modules.yaml` with a new `virtualStoreOnly: true` field after a fetch. `validateModules` recognizes this flag as "incomplete install state" and skips the `PUBLIC_HOIST_PATTERN_DIFF` / `HOIST_PATTERN_DIFF` comparisons. The next install then completes the missing post-import linking in place rather than purging. The flag is dropped from `.modules.yaml` once a normal install runs. A genuine hoist-pattern change (without a fetch in between) still triggers the purge as before — verified manually with `publicHoistPattern` in `pnpm-workspace.yaml`.
Summary
rawLocalConfigfromConfigContext, config reader,inheritPickedConfig,UniversalOptions, and all consumersforceHoistPattern,forcePublicHoistPattern,forceShamefullyHoist—validateModulesnow always checks for hoist pattern mismatches. Config values are always authoritative (v11 breaking change)saveWorkspaceProtocol = truewhen using--workspacewithlinkWorkspacePackagesoff, instead of throwing an error (the error was only useful whenrawLocalConfigcould distinguish explicit--no-save-workspace-protocolfrom the default)frozenLockfile: falseexplicitly inpatchCommitinstall call, since it needs to update the lockfile after changingpatchedDependenciesrawLocalConfigoverrides indeployandpatchCommitTest plan