pnpm-settings-migrator
v0.16.0
Published
Move pnpm settings from `pnpm` field in `package.json` and `.npmrc` file to `pnpm-workspace.yaml`
Readme
pnpm-settings-migrator
Move pnpm settings from pnpm field in package.json and .npmrc file to pnpm-workspace.yaml.
Usage
Run in your workspace root:
pnpm dlx pnpm-settings-migratorAfter migration, the CLI reports how many root settings changed and shows a GitHub-style YAML diff. Removed lines are red and added lines are green:
✔ 2 settings changed
packages:
- packages/*
+ - apps/*
overrides:
foo: 1.0.0
+ bar: 2.0.0When nothing needs updating, the only output is:
ℹ No changes needed.The CLI no longer prints a startup banner, missing optional files, a zero-change
count, or a duplicate completion message. Use --version to see the version.
Warnings are reserved for settings requiring attention. Information blocks have
one blank line between them, with no extra blank lines at the start or end.
URL credentials are hidden in diffs, warnings, and errors. Parser diagnostics
keep available locations but omit configuration source excerpts. For example,
https://user:p@[email protected]/ is displayed as
https://***@proxy.example/; the stored setting is preserved.
Other outcomes are reported according to the files actually changed:
- No configuration files:
ℹ No configuration files found. - Source cleanup with no settings diff:
✔ Migration completed. Source settings cleaned up. - Runtime migration with no settings diff:
✔ Migration completed. Node.js runtime updated in package.json. - File formatting changes with no settings diff:
✔ Migration completed. Configuration files updated. - Failure: one error report and exit code
1.
Library usage
import { migratePnpmSettings } from 'pnpm-settings-migrator'
const result = await migratePnpmSettings({ cwd: '/path/to/workspace' })Library calls now return a MigrationResult and do not print logs. The result
contains settingsChanges (before/after root workspace settings), changedFiles
(absolute paths, including created or removed files), sourceSettingsCleaned,
packageJsonRuntimeChanged, hasConfigurationFiles, and warnings. Errors are
thrown to the caller. Consumers that relied on console output should render the
returned changes and warnings themselves.
CLI Options
--cwd
- Type:
string - Default:
process.cwd()
Current working directory.
--sort-keys
- Type:
boolean - Default:
false
Sort keys when write pnpm-workspace.yaml.
--compatibility
- Type:
'auto' | 'v10' | 'v11' | 'v12' - Default:
'auto'
Compatibility major for migrated settings:
auto: infer fromtargetVersion,packageManager, ordevEngines.packageManager(pnpm@12+=>v12,pnpm@11=>v11, otherwisev10).v10: keep legacy settings and migrate schema-aligned pnpm config keys from.npmrc.v11: normalize to v11-compatible settings (allowBuilds,allowUnusedPatches, etc.).v12: use the v12 schema, includingglobalShims, and enable additional fields according to the confirmed target version.
An explicit major still uses a matching version from the project declarations.
For example, --compatibility v12 with packageManager: "[email protected]" enables
12.4 capabilities. A pin to another major supplies no minor capabilities for
the selected major; use --target-version to state the intended release.
In v11 and v12 modes, the migrator validates existing workspace keys,
filters incoming settings against the resolved capabilities, and applies
required normalization to legacy files and pnpm-workspace.yaml.
--target-version
- Type:
string(an exact version such as'12.4.0') - Default: inferred from the project when possible
Override the target pnpm version without changing the project's package manager
pin. Resolution order is explicit targetVersion, then packageManager, then
devEngines.packageManager. An explicit compatibility major must agree with
targetVersion; conflicts fail before any files are written.
await migratePnpmSettings() // Infer the target from the project
await migratePnpmSettings({ compatibility: 'v12' }) // Infer a matching minor
await migratePnpmSettings({ targetVersion: '12.4.0' }) // Target this exact releaseVersion ranges are not accepted by targetVersion. Project ranges, conflicting
devEngines entries, prereleases, and declarations without a confirmed matching
version retain the base major schema. Build metadata on exact stable versions
is accepted. Later stable minors automatically inherit known capabilities;
there are no minor-specific compatibility options.
For pnpm 12.4.0 and later stable v12 releases, the migrator additionally accepts
trustPolicyExcludePrune, python, cargo, pipelines, pipelineBase, and
packageConfigs, plus task fields outputs, inputs, env, cache, and
cargoTargetDir. On older or unconfirmed targets, a tasks object containing
these fields stays in its source as a whole.
For pnpm 12.5.0 and later stable v12 releases, the migrator also accepts
concurrencyGroups, task concurrencyGroup, platform lists in
supportedArchitectures, Python versions / overrides / constraints, and
registries declarations with ecosystem. Python registry packages routes
require 12.5.1. Unsupported nested settings stay in their source as a whole.
The legacy OS/CPU/libc architecture mapping continues to work.
pnpm 12.5 removes python.indexUrl and cargo.indexUrl. Settings containing
these fields are retained with an incompatibility warning; existing workspaces
containing them fail validation before any files are written. Replace them with
URL-keyed registries entries using ecosystem: pypi or ecosystem: cargo.
The registry prefix pkg is reserved from 12.5 onward. Tool download mirrors
under tools belong in global config.yaml or PNPM_CONFIG_TOOLS and are
never migrated into the project workspace.
On the v11 line, trustPolicyExcludePrune requires a confirmed stable version
from 11.27.0 onward; v12 support still starts at 12.4.0.
For pnpm 12.6.0 and later stable v12 releases, the migrator accepts autoDedupe,
saveTypes, tagVersionPrefix, and signed 32-bit tasks.*.priority values.
progress and loglevel also require 12.6.0 for v12 targets, when pnpm first
consumes them from workspace configuration; their v11 support is unchanged.
Invalid values and fields unsupported by the target remain in their sources.
The global-only macosBackup setting is preserved with a project-refused warning.
forceIgnoresPlatform requires pnpm 11.28.0+ or 12.7.0+. pnpm's defaults differ
between those majors; the migrator only moves explicit values. For v12,
publishWaitTimeout and reporter require 12.7.0+. The timeout is a nonnegative
safe integer in milliseconds (zero disables waiting); numeric .npmrc values
are converted to YAML numbers. Reporter values are default, append-only,
ndjson, and silent, or preserved environment placeholders.
From 11.28.0 / 12.7.0, dynamic userAgent values remain in their sources because
pnpm ignores project values containing environment placeholders. From 11.28.1,
invalid patchedDependencies maps and non-array packages values are retained
with diagnostics. From 12.8.2, globalShims belongs in trusted global
configuration and is retained with an incompatibility warning. Existing
workspaces containing these incompatible values fail validation before writes.
From 11.28.3, non-null allowBuilds values must be maps of booleans or strings.
From 11.28.4, non-null allowUnusedPatches must be boolean, and non-null
ignoredOptionalDependencies / requiredScripts must be string arrays.
From 11.28.5, httpProxy and httpsProxy must be strings, including when empty;
null is incompatible. Invalid values remain in their sources with diagnostics.
For pnpm 12.9.0 and later stable v12 releases, URL-keyed registries entries
can set networkConcurrency to a positive safe integer, or null to leave it
unset. On older or unconfirmed targets, the entire registry setting stays in
its source when it contains this field.
For pnpm 12.10.0 and later stable v12 releases, nodeLinker additionally accepts
loaded or { type: 'loaded', excluded: ['package-name'] }, and lockfile
accepts { includeResolutionSettings: true } to record resolution settings in
the lockfile. Existing linker strings and boolean lockfile values still work.
failIfNoMatch also requires 12.10.0 for v12 targets, when pnpm first consumes
it from workspace configuration. Unsupported or invalid nested settings stay
intact in their source; the migrator does not add pnpm's defaults.
For pnpm 12.11.0 and later stable v12 releases, permissions maps dependency
names or package IDs to build and skills decisions. Boolean build
decisions override matching allowBuilds entries; strings are undecided prompts
and null leaves a capability unset. Both settings are preserved for pnpm to
apply its precedence. Unknown capabilities or invalid nested values keep the
entire source object intact.
skills.dirs accepts an array of workspace-relative agent skill directories;
an empty array disables linking. Workspace provenance also requires 12.11.0
for v12 targets and accepts boolean or null values. Its v11 support is unchanged.
For example, targeting 12.11.0 migrates
{ "pnpm": { "permissions": { "example": { "build": false, "skills": true } }, "skills": { "dirs": [".agents/skills"] } } }
to equivalent workspace YAML. Targeting 12.10.1 retains these settings with
diagnostics.
pnpm v12 reads filter and filterProd from CLI arguments or updateConfig
hooks, not workspace YAML. These settings remain in their source with
diagnostics for v12 targets; existing YAML containing them blocks migration.
The 12.11.2 hook fix does not add workspace support.
Automated v10 to v11 conversions include:
managePackageManagerVersions,packageManagerStrict, andpackageManagerStrictVersion->pmOnFailonlyBuiltDependencies,onlyBuiltDependenciesFile,neverBuiltDependencies, andignoredBuiltDependencies->allowBuildsallowNonAppliedPatches->allowUnusedPatchesauditConfig.ignoreCves->auditConfig.ignoreGhsasuseNodeVersionand rootexecutionEnv.nodeVersion->package.json#devEngines.runtime.npmrcentries such asnode-mirror:release->nodeDownloadMirrors- removal of
ignoreDepScriptsandignorePatchFailures, which have no v11 equivalent
Notes:
.npmrcmigration is aligned with the target pnpm workspace schema. Unknown keys and settings supported only by a different pnpm version stay in.npmrcwith a warning.- Scalar list settings such as
public-hoist-pattern=*eslint*become YAML arrays (publicHoistPattern: ['*eslint*']). Existing arrays and patterns containing commas are preserved without splitting their values. - In
v11andv12, auth/registry keys and project-refused machine settings such asglobalDir,stateDir,configDir, andscopestay in.npmrc. - Registry declarations containing embedded credentials or dynamic
${...}URLs stay in their source with a warning. - Dynamic
${...}values inhttpProxy,httpsProxy,noProxy,proxy, andnoproxystay in their source for every target. Project YAML does not expand these placeholders; configure them globally in trusted configuration or use environment variables. Existing dynamic proxy settings in YAML block migration. The migrator never expands and writes these values. - pnpm v11 and confirmed stable v12 targets from 12.4.0 accept
packageConfigsas package-name maps or matcher arrays, withhoist,modulesDir,overrides,saveExact, andsavePrefix. Supported subproject.npmrcfields are moved there by package name. pnpm 12.0–12.3 does not supportpackageConfigs. - For v11 targets and v12 targets from 12.4.0, migrating
packageConfigsor subproject settings requiressharedWorkspaceLockfile: falsein the merged workspace. Otherwise source settings remain with a warning; existingpackageConfigsblocks migration until the effective lockfile mode is compatible. The migrator does not change this mode automatically. - Cleanup removes only source keys represented in the final workspace after
applying the selected merge strategy.
Each source is checked against its own values, including deprecated-key
replacements. Conflicting values in
.npmrc,package.json#pnpm, or Yarn resolutions remain in their original source. Unrecognized, refused, incompatible, or otherwise unsupportedpackage.json#pnpmchild keys remain inpackage.json. INI sections in.npmrcare retained conservatively; their child names are never treated as root keys during cleanup. - Runtime migration writes
devEngines.runtimebefore removing a workspace runtime declaration. A failed write preserves source settings for retry. If an existing runtime declaration conflicts with a workspace runtime, migration stops before writing any files. An identical Node.js declaration is reused. - YAML aliases remain when their anchors are unchanged and precede them. If replacement, removal, or sorting would invalidate an alias, its original value is written explicitly so unrelated settings keep their values.
- If no auth/registry lines remain after a v11 or v12 migration, the empty
.npmrcis removed. - Values moved from
auditConfig.ignoreCvesstill contain CVE IDs. Replace them manually with the corresponding GHSA IDs after migration. - The migrator does not update the
packageManagerversion, CI environment variables, shell setup, or pnpm commands in scripts. WhenpackageManagerstill pins pnpm 10, pass--compatibility v11explicitly and update the pin separately. - Compatibility checks cover pnpm 11.28.5 and 12.11.2, including 11.28.0,
11.28.1, 11.28.2, 11.28.3, 11.28.4, 12.7.0, 12.8.0, 12.8.1, 12.8.2,
12.9.0, 12.9.1, 12.10.0, 12.10.1, 12.11.0, and 12.11.1.
Earlier regressions remain at 11.25.0,
11.26.0, 11.27.1, 12.2.1, 12.3.4, 12.4.0, 12.4.2, 12.5.0, 12.5.1, and 12.6.0.
As audited on 2026-10-10,
npm
latestis 12.10.1; the highest confirmed stable release is 12.11.2 onnext-12. The v11 target is 11.28.5 onnext-11, whilelatest-11is 11.28.2. The project is pinned to 12.10.1. These checks verify the documented fixtures, not every setting in every release. v10 has unit regressions and source review through 10.34.6; real v10 consumption remains unverified. The removedpnpm install --resolution-onlyCLI flag is outside this settings migrator's scope; replace it withpnpm peers checkin scripts before upgrading.
For example, --target-version 12.4.0 migrates this legacy configuration:
{ "pnpm": { "pipelines": { "ci": ["build", "test"] }, "pipelineBase": "main" } }into pnpm-workspace.yaml and removes the applied pnpm keys:
pipelines:
ci: [build, test]
pipelineBase: mainWith --target-version 12.3.4, these settings remain in package.json with an
incompatibility warning. See the version audit
for the upstream release and schema references.
--replace-deprecated
- Type:
boolean - Default:
false
Force replacing deprecated pnpm settings with new keys and remove old keys during migration.
For v10, replacing legacy build permissions with allowBuilds requires a
confirmed stable target of 10.26.0 or later. Older or unconfirmed targets keep
the legacy setting names and report a warning. Canonical update sections
and registry prefixes take precedence over their deprecated aliases, including
when they come from different configuration sources.
Example conversions:
allowNonAppliedPatches->allowUnusedPatchesonlyBuiltDependencies/ignoredBuiltDependencies/neverBuiltDependencies->allowBuildsauditLevel/auditConfig->auditupdateConfig->updatecleanupUnusedCatalogs->catalogPruneenableGlobalVirtualStore->virtualStoreTypesideEffectsCacheReadonly/remoteSideEffectsCache-> structuredsideEffectsCache- non-conflicting
namedRegistriesaliases -> URL-keyedregistries
--strategy
- Type:
'discard' | 'merge' | 'overwrite' - Default:
'merge'
Strategy to handle conflicts when merging settings with existing pnpm-workspace.yaml:
discard: Keep existing values, only add new keys from incoming settings. For nested objects, merges keys from both.merge: Deep merge with array deduplication. Arrays are combined and deduplicated, objects are recursively merged, primitives keep existing values.overwrite: Use incoming values, only keep existing keys not present in incoming settings. For nested objects, merges keys from both.
packageConfigs matcher arrays preserve order and repeated entries because the
last matching entry wins. With merge, incoming matchers follow existing ones;
an overlapping end/start sequence is reused to keep repeated migrations stable.
For v11 targets, filter and filterProd use the same ordered merging: a repeated inclusion can
restore projects removed by an earlier exclusion. Cleanup verifies the combined
selector sequence. If package cleanup is disabled, later .npmrc selectors are
retained when needed to keep repeated migrations stable.
--no-yarn-resolutions
- Type:
boolean - Default behavior:
yarnResolutions=true(use this flag to disable)
Disable migrating resolutions field in package.json.
By default, plain package names are copied to pnpm overrides and global Yarn
selectors are translated: **/foo becomes foo, and **/@scope/foo becomes
@scope/foo. Path-specific selectors such as parent/child and
parent/**/child remain in resolutions with a warning. Conflicting selectors
that translate to the same pnpm key also remain for manual resolution.
Only individual resolutions whose values reach the final workspace are removed.
For example, an unsupported selector is now reported and retained instead of being copied into an override that prevents pnpm from installing:
WARN Kept Yarn resolution "parent/**/child" in package.json: its selector cannot be translated safely to pnpm overrides.
ℹ No changes needed.--no-show-changes
- Type:
boolean - Default behavior:
showChanges=true(use this flag to disable)
Hide the settings diff while keeping the outcome summary and warnings. This is a CLI display preference; library results always include settings changes.
--no-clean-npmrc
- Type:
boolean - Default behavior:
cleanNpmrc=true(use this flag to disable)
Disable removing pnpm settings in .npmrc file.
--no-clean-package-json
- Type:
boolean - Default behavior:
cleanPackageJson=true(use this flag to disable)
Disable removing migrated child keys from the pnpm field in package.json.
--no-newline-between
- Type:
boolean - Default behavior:
newlineBetween=true(use this flag to disable)
Disable adding newlines between each root keys.
Merge Strategy Examples
This document demonstrates how different merge strategies work when migrating pnpm settings.
Scenario
Existing pnpm-workspace.yaml:
packages:
- packages/*
overrides:
foo: 1.0.0Settings from package.json:
{
"pnpm": {
"packages": ["apps/*"],
"overrides": {
"bar": "2.0.0"
}
}
}Strategy: discard (Keep Existing)
pnpm dlx pnpm-settings-migrator --strategy discardResult:
packages:
- packages/* # Kept existing array value
overrides:
foo: 1.0.0 # Kept existing key
bar: 2.0.0 # Added new key from package.jsonUse this when you want to preserve your existing configuration and only add new settings.
Strategy: merge (Smart Merge - Default)
pnpm dlx pnpm-settings-migrator --strategy mergeResult:
packages:
- packages/* # From existing
- apps/* # From package.json (deduplicated)
overrides:
foo: 1.0.0 # From existing
bar: 2.0.0 # From package.jsonUse this for intelligent merging that combines arrays and deeply merges objects.
Strategy: overwrite (Use Incoming)
pnpm dlx pnpm-settings-migrator --strategy overwriteResult:
packages:
- apps/* # Replaced with incoming array value
overrides:
foo: 1.0.0 # Kept existing key (not in incoming)
bar: 2.0.0 # Added new key from package.jsonUse this when you want to prioritize settings from package.json and .npmrc.
Advanced Example
Existing pnpm-workspace.yaml:
packages:
- packages/*
- common
overrides:
react: 18.0.0
peerDependencyRules:
ignoreMissing:
- react-domSettings from package.json:
{
"pnpm": {
"packages": ["apps/*", "common"],
"overrides": {
"vue": "3.0.0"
},
"peerDependencyRules": {
"ignoreMissing": ["vue-router"]
}
}
}With --strategy merge:
packages:
- packages/*
- common # Deduplicated
- apps/*
overrides:
react: 18.0.0
vue: 3.0.0
peerDependencyRules:
ignoreMissing:
- react-dom
- vue-router # Arrays merged and deduplicated