pnpm-settings-migrator 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -7
- package/dist/cli.mjs +275 -29
- package/dist/index.d.mts +59 -2
- package/dist/index.mjs +274 -28
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -217,6 +217,52 @@ the lockfile. Existing linker strings and boolean `lockfile` values still work.
|
|
|
217
217
|
it from workspace configuration. Unsupported or invalid nested settings stay
|
|
218
218
|
intact in their source; the migrator does not add pnpm's defaults.
|
|
219
219
|
|
|
220
|
+
For pnpm 12.11.0 and later stable v12 releases, `permissions` maps dependency
|
|
221
|
+
names or package IDs to `build` and `skills` decisions. Boolean `build`
|
|
222
|
+
decisions override matching `allowBuilds` entries; strings are undecided prompts
|
|
223
|
+
and null leaves a capability unset. Both settings are preserved for pnpm to
|
|
224
|
+
apply its precedence. Unknown capabilities or invalid nested values keep the
|
|
225
|
+
entire source object intact.
|
|
226
|
+
|
|
227
|
+
`skills.dirs` accepts an array of workspace-relative agent skill directories;
|
|
228
|
+
an empty array disables linking. Workspace `provenance` also requires 12.11.0
|
|
229
|
+
for v12 targets and accepts boolean or null values. Its v11 support is unchanged.
|
|
230
|
+
For example, targeting 12.11.0 migrates
|
|
231
|
+
`{ "pnpm": { "permissions": { "example": { "build": false, "skills": true } }, "skills": { "dirs": [".agents/skills"] } } }`
|
|
232
|
+
to equivalent workspace YAML. Targeting 12.10.1 retains these settings with
|
|
233
|
+
diagnostics.
|
|
234
|
+
|
|
235
|
+
For pnpm 12.12.0 and later stable v12 releases, `addMissingPeerTypes` accepts
|
|
236
|
+
boolean or null values and `remoteCache` accepts the project organization:
|
|
237
|
+
`{ org: 'acme' }`. Empty and null remote-cache sections are preserved. Server,
|
|
238
|
+
team, signing, and publication fields belong in trusted global configuration
|
|
239
|
+
or environment variables. Objects containing those fields, unknown fields,
|
|
240
|
+
or invalid values remain intact in their source with diagnostics; existing
|
|
241
|
+
incompatible workspace values block migration before writes.
|
|
242
|
+
|
|
243
|
+
`remoteCache` and legacy `sideEffectsCache.remote` are kept independently so
|
|
244
|
+
pnpm applies its field-level precedence. The migrator does not insert defaults
|
|
245
|
+
or rewrite `allowBuilds`: pnpm 12.12 itself now honors explicit build denials
|
|
246
|
+
when `dangerouslyAllowAllBuilds` is true.
|
|
247
|
+
|
|
248
|
+
From 12.12.1, the registry prefix `tool` is reserved. Both `namedRegistries`
|
|
249
|
+
and URL-keyed `registries` using that exact prefix remain in their sources;
|
|
250
|
+
existing workspace declarations block migration. This restriction is
|
|
251
|
+
case-sensitive (`ToOl` remains valid), unlike the existing `pkg` restriction.
|
|
252
|
+
|
|
253
|
+
pnpm v12 reads `filter` and `filterProd` from CLI arguments or `updateConfig`
|
|
254
|
+
hooks, not workspace YAML. These settings remain in their source with
|
|
255
|
+
diagnostics for v12 targets; existing YAML containing them blocks migration.
|
|
256
|
+
The 12.11.2 hook fix does not add workspace support.
|
|
257
|
+
|
|
258
|
+
Other legacy fields ignored by the v12 workspace reader also stay in their
|
|
259
|
+
sources with diagnostics. These include `production`, `dev`, `recursive`,
|
|
260
|
+
`lockfileOnly`, `force`, `depth`, `saveDev`, `fetchingConcurrency`,
|
|
261
|
+
`requiredScripts`, `extraBinPaths`, `extraEnv`, and `workspacePackagePatterns`.
|
|
262
|
+
Existing YAML containing ignored fields blocks migration before writes.
|
|
263
|
+
For example, `production=true` cannot be moved into v12 YAML; use
|
|
264
|
+
`pnpm install --prod` to select production dependencies.
|
|
265
|
+
|
|
220
266
|
</details>
|
|
221
267
|
|
|
222
268
|
<details>
|
|
@@ -253,6 +299,9 @@ Notes:
|
|
|
253
299
|
such as `globalDir`, `stateDir`, `configDir`, and `scope` stay in `.npmrc`.
|
|
254
300
|
- Registry declarations containing embedded credentials or dynamic `${...}`
|
|
255
301
|
URLs stay in their source with a warning.
|
|
302
|
+
- Invalid registry locations, including relative paths and object property names
|
|
303
|
+
such as `__proto__`, remain in their sources with diagnostics. Registry
|
|
304
|
+
conversion never uses inherited object properties as declarations.
|
|
256
305
|
- Dynamic `${...}` values in `httpProxy`, `httpsProxy`, `noProxy`, `proxy`, and
|
|
257
306
|
`noproxy` stay in their source for every target. Project YAML does not expand
|
|
258
307
|
these placeholders; configure them globally in trusted configuration or use
|
|
@@ -277,10 +326,16 @@ Notes:
|
|
|
277
326
|
`package.json#pnpm` child keys remain in `package.json`.
|
|
278
327
|
INI sections in `.npmrc` are retained conservatively; their child names are
|
|
279
328
|
never treated as root keys during cleanup.
|
|
329
|
+
Bare boolean flags such as `save-exact` and quoted keys such as
|
|
330
|
+
`"save-exact"=true` are cleaned when migrated. Retained sections alone do not
|
|
331
|
+
cause repeated cleanup reports or rewrites.
|
|
280
332
|
- Runtime migration writes `devEngines.runtime` before removing a workspace
|
|
281
333
|
runtime declaration. A failed write preserves source settings for retry.
|
|
282
334
|
If an existing runtime declaration conflicts with a workspace runtime, migration
|
|
283
335
|
stops before writing any files. An identical Node.js declaration is reused.
|
|
336
|
+
- An `executionEnv` object containing fields besides `nodeVersion` remains
|
|
337
|
+
intact in its source with a warning. Existing workspace declarations with
|
|
338
|
+
such fields block migration before writes, so unmigrated values cannot be lost.
|
|
284
339
|
- YAML aliases remain when their anchors are unchanged and precede them.
|
|
285
340
|
If replacement, removal, or sorting would invalidate an alias, its original
|
|
286
341
|
value is written explicitly so unrelated settings keep their values.
|
|
@@ -291,16 +346,17 @@ Notes:
|
|
|
291
346
|
- The migrator does not update the `packageManager` version, CI environment variables,
|
|
292
347
|
shell setup, or pnpm commands in scripts. When `packageManager` still pins pnpm 10,
|
|
293
348
|
pass `--compatibility v11` explicitly and update the pin separately.
|
|
294
|
-
- Compatibility checks cover pnpm 11.28.5 and 12.
|
|
349
|
+
- Compatibility checks cover pnpm 11.28.5 and 12.12.1, including 11.28.0,
|
|
295
350
|
11.28.1, 11.28.2, 11.28.3, 11.28.4, 12.7.0, 12.8.0, 12.8.1, 12.8.2,
|
|
296
|
-
12.9.0,
|
|
351
|
+
12.9.0, 12.9.1, 12.10.0, 12.10.1, 12.11.0, 12.11.1, 12.11.2, and 12.12.0.
|
|
352
|
+
Earlier regressions remain at 11.25.0,
|
|
297
353
|
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.
|
|
298
|
-
As [audited on 2026-10-
|
|
299
|
-
npm `latest` is 12.
|
|
354
|
+
As [audited on 2026-10-13](docs/research/pnpm-settings-bump-2026-10-13.md),
|
|
355
|
+
npm `latest` is 12.11.2; the highest confirmed stable release is 12.12.1 on
|
|
300
356
|
`next-12`. The v11 target is 11.28.5 on `next-11`, while `latest-11` is
|
|
301
|
-
11.28.2. The project is pinned to 12.
|
|
357
|
+
11.28.2. The project is pinned to 12.11.2. These checks verify the documented
|
|
302
358
|
fixtures, not every setting in every release. v10 has unit regressions and
|
|
303
|
-
source review through 10.34.
|
|
359
|
+
source review through 10.34.7; real v10 consumption remains unverified.
|
|
304
360
|
The removed `pnpm install --resolution-only` CLI flag is outside this settings
|
|
305
361
|
migrator's scope; replace it with `pnpm peers check` in scripts before upgrading.
|
|
306
362
|
|
|
@@ -373,7 +429,7 @@ Strategy to handle conflicts when merging settings with existing `pnpm-workspace
|
|
|
373
429
|
`packageConfigs` matcher arrays preserve order and repeated entries because the
|
|
374
430
|
last matching entry wins. With `merge`, incoming matchers follow existing ones;
|
|
375
431
|
an overlapping end/start sequence is reused to keep repeated migrations stable.
|
|
376
|
-
`filter` and `filterProd` use the same ordered merging: a repeated inclusion can
|
|
432
|
+
For v11 targets, `filter` and `filterProd` use the same ordered merging: a repeated inclusion can
|
|
377
433
|
restore projects removed by an earlier exclusion. Cleanup verifies the combined
|
|
378
434
|
selector sequence. If package cleanup is disabled, later `.npmrc` selectors are
|
|
379
435
|
retained when needed to keep repeated migrations stable.
|
package/dist/cli.mjs
CHANGED
|
@@ -502,7 +502,12 @@ const PNPM_V11_WORKSPACE_SETTINGS_FIELDS = unique([...PNPM_V10_NPMRC_SETTINGS_FI
|
|
|
502
502
|
/**
|
|
503
503
|
* Settings that are supported by v11 but not by pnpm v12.
|
|
504
504
|
*/
|
|
505
|
-
const PNPM_V11_ONLY_WORKSPACE_SETTINGS = [
|
|
505
|
+
const PNPM_V11_ONLY_WORKSPACE_SETTINGS = [
|
|
506
|
+
"confirmModulesPurge",
|
|
507
|
+
"filter",
|
|
508
|
+
"filterProd",
|
|
509
|
+
"packageConfigs"
|
|
510
|
+
];
|
|
506
511
|
/**
|
|
507
512
|
* Fields pnpm v11 accepts inside a `packageConfigs` entry.
|
|
508
513
|
*/
|
|
@@ -614,6 +619,38 @@ const PNPM_V12_10_MINIMUM_VERSION = [
|
|
|
614
619
|
0
|
|
615
620
|
];
|
|
616
621
|
/**
|
|
622
|
+
* Release introducing package permissions, agent skills, and workspace provenance.
|
|
623
|
+
*/
|
|
624
|
+
const PNPM_V12_11_MINIMUM_VERSION = [
|
|
625
|
+
12,
|
|
626
|
+
11,
|
|
627
|
+
0
|
|
628
|
+
];
|
|
629
|
+
/**
|
|
630
|
+
* Release introducing remote cache configuration and missing peer types.
|
|
631
|
+
*/
|
|
632
|
+
const PNPM_V12_12_MINIMUM_VERSION = [
|
|
633
|
+
12,
|
|
634
|
+
12,
|
|
635
|
+
0
|
|
636
|
+
];
|
|
637
|
+
/**
|
|
638
|
+
* Release reserving the tool registry prefix.
|
|
639
|
+
*/
|
|
640
|
+
const PNPM_V12_12_1_MINIMUM_VERSION = [
|
|
641
|
+
12,
|
|
642
|
+
12,
|
|
643
|
+
1
|
|
644
|
+
];
|
|
645
|
+
/**
|
|
646
|
+
* Workspace settings first consumed by pnpm v12 in 12.11.0.
|
|
647
|
+
*/
|
|
648
|
+
const PNPM_V12_11_WORKSPACE_SETTINGS = [
|
|
649
|
+
"permissions",
|
|
650
|
+
"skills",
|
|
651
|
+
"provenance"
|
|
652
|
+
];
|
|
653
|
+
/**
|
|
617
654
|
* Workspace settings first consumed by pnpm v12 in 12.10.0.
|
|
618
655
|
*/
|
|
619
656
|
const PNPM_V12_10_WORKSPACE_SETTINGS = ["failIfNoMatch"];
|
|
@@ -631,6 +668,16 @@ const PNPM_V12_7_WORKSPACE_SETTINGS = [
|
|
|
631
668
|
* @see https://github.com/pnpm/pnpm/blob/v12.4.0/pnpm/crates/config/src/workspace_yaml.rs
|
|
632
669
|
*/
|
|
633
670
|
const PNPM_SETTINGS_CAPABILITIES = [
|
|
671
|
+
{
|
|
672
|
+
minimumVersion: PNPM_V12_12_MINIMUM_VERSION,
|
|
673
|
+
workspaceFields: ["addMissingPeerTypes", "remoteCache"],
|
|
674
|
+
taskFields: []
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
minimumVersion: PNPM_V12_11_MINIMUM_VERSION,
|
|
678
|
+
workspaceFields: PNPM_V12_11_WORKSPACE_SETTINGS,
|
|
679
|
+
taskFields: []
|
|
680
|
+
},
|
|
634
681
|
{
|
|
635
682
|
minimumVersion: PNPM_V11_28_MINIMUM_VERSION,
|
|
636
683
|
workspaceFields: ["forceIgnoresPlatform"],
|
|
@@ -707,9 +754,82 @@ const PNPM_V12_ONLY_WORKSPACE_SETTINGS = [
|
|
|
707
754
|
"globalShims"
|
|
708
755
|
];
|
|
709
756
|
/**
|
|
757
|
+
* Legacy fields absent from pnpm v12's workspace reader through 12.12.1.
|
|
758
|
+
* Removed aliases with explicit migration replacements are kept separately.
|
|
759
|
+
*
|
|
760
|
+
* @see https://github.com/pnpm/pnpm/blob/v12.12.1/pnpm/crates/config/src/workspace_yaml/settings.rs
|
|
761
|
+
*/
|
|
762
|
+
const PNPM_V12_IGNORED_WORKSPACE_SETTINGS = [
|
|
763
|
+
"access",
|
|
764
|
+
"allowNew",
|
|
765
|
+
"allowSameVersion",
|
|
766
|
+
"auditIgnorePrune",
|
|
767
|
+
"autoConfirmAllPrompts",
|
|
768
|
+
"binLinks",
|
|
769
|
+
"commitHooks",
|
|
770
|
+
"cpu",
|
|
771
|
+
"depth",
|
|
772
|
+
"description",
|
|
773
|
+
"dev",
|
|
774
|
+
"dryRun",
|
|
775
|
+
"enablePnp",
|
|
776
|
+
"extraBinPaths",
|
|
777
|
+
"extraEnv",
|
|
778
|
+
"fetchingConcurrency",
|
|
779
|
+
"force",
|
|
780
|
+
"git",
|
|
781
|
+
"gitTagVersion",
|
|
782
|
+
"global",
|
|
783
|
+
"globalPath",
|
|
784
|
+
"globalPrefix",
|
|
785
|
+
"ignoreCurrentSpecifiers",
|
|
786
|
+
"json",
|
|
787
|
+
"libc",
|
|
788
|
+
"localAddress",
|
|
789
|
+
"lockfileOnly",
|
|
790
|
+
"long",
|
|
791
|
+
"message",
|
|
792
|
+
"npmPath",
|
|
793
|
+
"only",
|
|
794
|
+
"os",
|
|
795
|
+
"packDestination",
|
|
796
|
+
"packGzipLevel",
|
|
797
|
+
"parseable",
|
|
798
|
+
"pnpmExecPath",
|
|
799
|
+
"preserveAbsolutePaths",
|
|
800
|
+
"production",
|
|
801
|
+
"publishBranch",
|
|
802
|
+
"recursive",
|
|
803
|
+
"registriesByPrefix",
|
|
804
|
+
"registriesByScope",
|
|
805
|
+
"registryOptionsByUrl",
|
|
806
|
+
"requiredScripts",
|
|
807
|
+
"runtime",
|
|
808
|
+
"save",
|
|
809
|
+
"saveDev",
|
|
810
|
+
"saveOptional",
|
|
811
|
+
"saveProd",
|
|
812
|
+
"sideEffectsCacheRead",
|
|
813
|
+
"sideEffectsCacheWrite",
|
|
814
|
+
"signGitTag",
|
|
815
|
+
"strictSsl",
|
|
816
|
+
"tag",
|
|
817
|
+
"tryLoadDefaultPnpmfile",
|
|
818
|
+
"umask",
|
|
819
|
+
"useGitBranchLockfile",
|
|
820
|
+
"useLockfile",
|
|
821
|
+
"useRunningStoreServer",
|
|
822
|
+
"version",
|
|
823
|
+
"workspacePackagePatterns",
|
|
824
|
+
"workspacePackages",
|
|
825
|
+
"workspacePrefix",
|
|
826
|
+
"workspaceRoot",
|
|
827
|
+
"yes"
|
|
828
|
+
];
|
|
829
|
+
/**
|
|
710
830
|
* Lookup used to remove v11-only fields from the v12 workspace allowlist.
|
|
711
831
|
*/
|
|
712
|
-
const v11OnlyWorkspaceSettings = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
|
|
832
|
+
const v11OnlyWorkspaceSettings = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
|
|
713
833
|
/**
|
|
714
834
|
* Fields accepted by registry declarations with pnpm 12.9 concurrency limits.
|
|
715
835
|
*/
|
|
@@ -725,7 +845,7 @@ const PNPM_V12_REGISTRY_DECLARATION_FIELDS = [
|
|
|
725
845
|
/**
|
|
726
846
|
* Settings accepted by a pnpm v12 project workspace manifest.
|
|
727
847
|
*/
|
|
728
|
-
const PNPM_V12_WORKSPACE_SETTINGS_FIELDS = [...PNPM_V11_WORKSPACE_SETTINGS_FIELDS.filter((field) => !v11OnlyWorkspaceSettings.has(field) && !PNPM_V12_6_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_7_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_10_WORKSPACE_SETTINGS.includes(field)), ...PNPM_V12_ONLY_WORKSPACE_SETTINGS];
|
|
848
|
+
const PNPM_V12_WORKSPACE_SETTINGS_FIELDS = [...PNPM_V11_WORKSPACE_SETTINGS_FIELDS.filter((field) => !v11OnlyWorkspaceSettings.has(field) && !PNPM_V12_6_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_7_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_10_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_11_WORKSPACE_SETTINGS.includes(field)), ...PNPM_V12_ONLY_WORKSPACE_SETTINGS];
|
|
729
849
|
//#endregion
|
|
730
850
|
//#region src/constants/settings-schema.ts
|
|
731
851
|
/**
|
|
@@ -744,7 +864,7 @@ const PROJECT_REFUSED_SETTINGS = new Set(PNPM_PROJECT_REFUSED_SETTINGS);
|
|
|
744
864
|
/**
|
|
745
865
|
* Fields reported as incompatible when selecting settings for v12.
|
|
746
866
|
*/
|
|
747
|
-
const PNPM_V11_ONLY_SETTINGS = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
|
|
867
|
+
const PNPM_V11_ONLY_SETTINGS = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
|
|
748
868
|
/**
|
|
749
869
|
* Allowed workspace fields for the v11 target.
|
|
750
870
|
*/
|
|
@@ -1093,6 +1213,63 @@ function formatSettingsIssues(options) {
|
|
|
1093
1213
|
return warnings;
|
|
1094
1214
|
}
|
|
1095
1215
|
//#endregion
|
|
1216
|
+
//#region src/features/settings/registries.ts
|
|
1217
|
+
/**
|
|
1218
|
+
* Check that a registry location is a URL rather than an object property name.
|
|
1219
|
+
*
|
|
1220
|
+
* @param value - Untrusted registry location
|
|
1221
|
+
*
|
|
1222
|
+
* @returns Whether the location is an absolute or scheme-relative URL
|
|
1223
|
+
*/
|
|
1224
|
+
function isRegistryUrl(value) {
|
|
1225
|
+
return isString(value) && URL.canParse(value.startsWith("//") ? `https:${value}` : value);
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Validate locations in legacy registry maps and URL-keyed declarations.
|
|
1229
|
+
*
|
|
1230
|
+
* @param key - Canonical workspace setting name
|
|
1231
|
+
* @param value - Untrusted registry configuration
|
|
1232
|
+
*
|
|
1233
|
+
* @returns Whether the complete setting must be retained in its source
|
|
1234
|
+
*/
|
|
1235
|
+
function hasInvalidRegistryLocations(key, value) {
|
|
1236
|
+
if (!["namedRegistries", "registries"].includes(key) || value === null) return false;
|
|
1237
|
+
if (!isPlainObject(value)) return true;
|
|
1238
|
+
if (key === "namedRegistries") return Object.values(value).some((url) => !isRegistryUrl(url));
|
|
1239
|
+
return Object.entries(value).some(([url, entry]) => isString(entry) ? !isRegistryUrl(entry) : !isRegistryUrl(url));
|
|
1240
|
+
}
|
|
1241
|
+
//#endregion
|
|
1242
|
+
//#region src/features/settings/permissions.ts
|
|
1243
|
+
/**
|
|
1244
|
+
* Validate a dependency's capability map without dropping unknown permissions.
|
|
1245
|
+
*
|
|
1246
|
+
* @param value - Untrusted per-package permission entry
|
|
1247
|
+
*
|
|
1248
|
+
* @returns Whether pnpm cannot consume every capability in the entry
|
|
1249
|
+
*/
|
|
1250
|
+
function hasInvalidPackagePermissions(value) {
|
|
1251
|
+
if (value === null) return false;
|
|
1252
|
+
if (!value || typeof value !== "object" || isArray(value)) return true;
|
|
1253
|
+
return Object.entries(value).some(([capability, decision]) => !["build", "skills"].includes(capability) || decision !== null && !isBoolean(decision) && !isString(decision));
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Validate permissions and skill directories introduced in pnpm 12.11.
|
|
1257
|
+
* Preserve an entire object when a nested value is invalid or ignored upstream.
|
|
1258
|
+
*
|
|
1259
|
+
* @param key - Canonical workspace setting name
|
|
1260
|
+
* @param value - Untrusted source or existing workspace value
|
|
1261
|
+
*
|
|
1262
|
+
* @returns Whether the setting contains unsupported nested values
|
|
1263
|
+
*/
|
|
1264
|
+
function hasInvalidPermissionSettings(key, value) {
|
|
1265
|
+
if (!["permissions", "skills"].includes(key) || value === null) return false;
|
|
1266
|
+
if (!value || typeof value !== "object" || isArray(value)) return true;
|
|
1267
|
+
if (key === "permissions") return Object.values(value).some(hasInvalidPackagePermissions);
|
|
1268
|
+
if (Object.keys(value).some((field) => field !== "dirs")) return true;
|
|
1269
|
+
const dirs = Reflect.get(value, "dirs");
|
|
1270
|
+
return Object.hasOwn(value, "dirs") && dirs !== null && (!isArray(dirs) || !dirs.every(isString));
|
|
1271
|
+
}
|
|
1272
|
+
//#endregion
|
|
1096
1273
|
//#region src/features/settings/pnpm-v12-values.ts
|
|
1097
1274
|
/**
|
|
1098
1275
|
* Check a loaded linker object without discarding unsupported options.
|
|
@@ -1206,6 +1383,22 @@ function hasIncompatibleV12SettingValue(key, value, target) {
|
|
|
1206
1383
|
return false;
|
|
1207
1384
|
}
|
|
1208
1385
|
//#endregion
|
|
1386
|
+
//#region src/features/settings/remote-cache.ts
|
|
1387
|
+
/**
|
|
1388
|
+
* Accept only the project-owned remote cache organization.
|
|
1389
|
+
* Keep machine configuration and unknown fields intact in their source.
|
|
1390
|
+
*
|
|
1391
|
+
* @param key - Canonical workspace setting name
|
|
1392
|
+
* @param value - Untrusted incoming or existing workspace value
|
|
1393
|
+
*
|
|
1394
|
+
* @returns Whether the remote cache setting cannot migrate to project YAML
|
|
1395
|
+
*/
|
|
1396
|
+
function hasInvalidRemoteCacheValue(key, value) {
|
|
1397
|
+
if (key !== "remoteCache" || value === null) return false;
|
|
1398
|
+
if (!value || typeof value !== "object" || isArray(value)) return true;
|
|
1399
|
+
return Object.entries(value).some(([field, entry]) => field !== "org" || entry !== null && !isString(entry));
|
|
1400
|
+
}
|
|
1401
|
+
//#endregion
|
|
1209
1402
|
//#region src/features/settings/versioned-values.ts
|
|
1210
1403
|
/**
|
|
1211
1404
|
* Bounds of the signed 32-bit integer used by pnpm's task priority.
|
|
@@ -1254,6 +1447,7 @@ function hasInvalidScalarValue(key, value, target) {
|
|
|
1254
1447
|
"ndjson",
|
|
1255
1448
|
"silent"
|
|
1256
1449
|
].includes(value);
|
|
1450
|
+
if (key === "provenance" || key === "addMissingPeerTypes") return value !== null && !isBoolean(value);
|
|
1257
1451
|
if (key === "progress") return !isBoolean(value);
|
|
1258
1452
|
if (key === "tagVersionPrefix") return !isString(value);
|
|
1259
1453
|
if (key === "loglevel") return !isString(value) || !value.includes("${") && ![
|
|
@@ -1283,6 +1477,17 @@ function hasInvalidReleaseValue(key, value, target) {
|
|
|
1283
1477
|
return false;
|
|
1284
1478
|
}
|
|
1285
1479
|
/**
|
|
1480
|
+
* Check release-specific reserved aliases without broadening case sensitivity.
|
|
1481
|
+
*
|
|
1482
|
+
* @param prefix - Registry alias as declared
|
|
1483
|
+
* @param target - Confirmed target version
|
|
1484
|
+
*
|
|
1485
|
+
* @returns Whether the alias shadows a protocol introduced by these releases
|
|
1486
|
+
*/
|
|
1487
|
+
function isReservedRegistryPrefix(prefix, target) {
|
|
1488
|
+
return prefix.toLowerCase() === "pkg" && supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION) || prefix === "tool" && supportsMinimumVersion(target.version, PNPM_V12_12_1_MINIMUM_VERSION);
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1286
1491
|
* Check value shapes that changed within a pnpm major release.
|
|
1287
1492
|
*
|
|
1288
1493
|
* Reject the whole setting so cleanup cannot discard unsupported nested values.
|
|
@@ -1294,7 +1499,7 @@ function hasInvalidReleaseValue(key, value, target) {
|
|
|
1294
1499
|
* @returns Whether the value uses a shape unsupported by the selected release
|
|
1295
1500
|
*/
|
|
1296
1501
|
function hasIncompatibleSettingValue(key, value, target) {
|
|
1297
|
-
if (hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
|
|
1502
|
+
if (hasInvalidPermissionSettings(key, value) || hasInvalidRemoteCacheValue(key, value) || hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
|
|
1298
1503
|
if (hasInvalidScalarValue(key, value, target)) return true;
|
|
1299
1504
|
const supportsV12_5 = supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION);
|
|
1300
1505
|
if (key === "supportedArchitectures" && isArray(value)) return !supportsV12_5;
|
|
@@ -1312,19 +1517,42 @@ function hasIncompatibleSettingValue(key, value, target) {
|
|
|
1312
1517
|
"constraints"
|
|
1313
1518
|
].some((field) => Object.hasOwn(value, field));
|
|
1314
1519
|
}
|
|
1315
|
-
if (key === "namedRegistries"
|
|
1520
|
+
if (key === "namedRegistries") return Object.keys(value).some((prefix) => isReservedRegistryPrefix(prefix, target));
|
|
1316
1521
|
if (key === "registries") return Object.values(value).some((entry) => {
|
|
1317
1522
|
if (!entry || typeof entry !== "object") return false;
|
|
1318
1523
|
if (Object.hasOwn(entry, "ecosystem") && !supportsV12_5) return true;
|
|
1319
1524
|
if (Object.hasOwn(entry, "packages") && !supportsMinimumVersion(target.version, PNPM_V12_5_1_MINIMUM_VERSION)) return true;
|
|
1320
1525
|
const prefix = Reflect.get(entry, "prefix");
|
|
1321
|
-
return
|
|
1526
|
+
return isString(prefix) && isReservedRegistryPrefix(prefix, target);
|
|
1322
1527
|
});
|
|
1323
1528
|
return false;
|
|
1324
1529
|
}
|
|
1325
1530
|
//#endregion
|
|
1326
1531
|
//#region src/features/settings/schema.ts
|
|
1327
1532
|
/**
|
|
1533
|
+
* Detect legacy runtime fields without a destination in pnpm v11 and newer.
|
|
1534
|
+
*
|
|
1535
|
+
* @param key - Canonical setting name
|
|
1536
|
+
* @param value - Untrusted legacy runtime declaration
|
|
1537
|
+
* @param compatibility - Target major controlling runtime conversion
|
|
1538
|
+
*
|
|
1539
|
+
* @returns Whether converting the runtime would discard sibling fields
|
|
1540
|
+
*/
|
|
1541
|
+
function hasUnmigratableRuntimeFields(key, value, compatibility) {
|
|
1542
|
+
return Boolean(key === "executionEnv" && compatibility !== "v10" && value && typeof value === "object" && Object.keys(value).some((field) => field !== "nodeVersion"));
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Check task declarations against the fields consumed by the target release.
|
|
1546
|
+
*
|
|
1547
|
+
* @param value - Untrusted task declarations
|
|
1548
|
+
* @param taskSettings - Supported task field names
|
|
1549
|
+
*
|
|
1550
|
+
* @returns Whether any declared task field is unsupported
|
|
1551
|
+
*/
|
|
1552
|
+
function hasUnsupportedTaskFields(value, taskSettings) {
|
|
1553
|
+
return Boolean(value && typeof value === "object" && Object.values(value).some((task) => task && typeof task === "object" && Object.keys(task).some((field) => !taskSettings.has(field))));
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1328
1556
|
* Create an empty collection of schema selection issues.
|
|
1329
1557
|
*
|
|
1330
1558
|
* @returns Empty issue lists for every supported issue category
|
|
@@ -1429,8 +1657,8 @@ function resolveSettingIssue({ allowedFields, target, key, npmrc, value }) {
|
|
|
1429
1657
|
if (compatibility !== "v10" && PROJECT_REFUSED_SETTINGS.has(key)) return "refused";
|
|
1430
1658
|
if (REGISTRY_SETTINGS.has(key) && containsUnsafeRegistryValue(value)) return "unsafe";
|
|
1431
1659
|
if (PROXY_SETTINGS.has(key) && isString(value) && value.includes("${")) return "unsafe";
|
|
1432
|
-
if (hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
1433
|
-
if (key === "tasks" &&
|
|
1660
|
+
if (hasInvalidRegistryLocations(key, value) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
1661
|
+
if (key === "tasks" && hasUnsupportedTaskFields(value, taskSettings)) return "incompatible";
|
|
1434
1662
|
if (!workspaceSettings.has(key)) return isSettingFromAnotherVersion(key, compatibility) ? "incompatible" : "unknown";
|
|
1435
1663
|
if (allowedFields && !allowedFields.has(key)) return "unsupported";
|
|
1436
1664
|
}
|
|
@@ -1839,8 +2067,17 @@ function getNpmrcLineKey(line) {
|
|
|
1839
2067
|
const trimmed = line.trim();
|
|
1840
2068
|
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) return;
|
|
1841
2069
|
const index = trimmed.indexOf("=");
|
|
1842
|
-
if (index
|
|
1843
|
-
|
|
2070
|
+
if (index === 0) return;
|
|
2071
|
+
let key = (index < 0 ? trimmed : trimmed.slice(0, index)).trim();
|
|
2072
|
+
if (key.startsWith("\"") && key.endsWith("\"") || key.startsWith("'") && key.endsWith("'")) {
|
|
2073
|
+
if (key.startsWith("'")) key = key.slice(1, -1);
|
|
2074
|
+
try {
|
|
2075
|
+
const parsed = JSON.parse(key);
|
|
2076
|
+
if (!isString(parsed)) return;
|
|
2077
|
+
key = parsed;
|
|
2078
|
+
} catch {}
|
|
2079
|
+
} else key = key.replace(/\\(?<escaped>[\\;#])|[;#].*$/gu, (_, escaped) => escaped ?? "");
|
|
2080
|
+
return normalizeNpmrcKey(key);
|
|
1844
2081
|
}
|
|
1845
2082
|
/**
|
|
1846
2083
|
* Check whether a `.npmrc` key is auth/registry-related and should stay in
|
|
@@ -1867,7 +2104,7 @@ function isNpmrcAuthOrRegistryKey(key) {
|
|
|
1867
2104
|
* @param compatibility - Concrete pnpm compatibility target
|
|
1868
2105
|
* @param migratedKeys - Original `.npmrc` keys that were migrated
|
|
1869
2106
|
*
|
|
1870
|
-
* @returns
|
|
2107
|
+
* @returns Whether the file was changed or removed
|
|
1871
2108
|
*
|
|
1872
2109
|
* @throws {Error} When file read/write operations fail
|
|
1873
2110
|
*
|
|
@@ -1880,18 +2117,25 @@ async function pruneNpmrc(path, compatibility, migratedKeys) {
|
|
|
1880
2117
|
const migratedKeySet = new Set(migratedKeys.map(normalizeNpmrcKey));
|
|
1881
2118
|
const content = await fsReadFile(path);
|
|
1882
2119
|
let inSection = false;
|
|
1883
|
-
|
|
2120
|
+
let removed = false;
|
|
2121
|
+
const lines = content.split(/\r\n|[\r\n]/u).filter((line) => {
|
|
1884
2122
|
if (/^\[[^\]]*\]\s*$/u.test(line.replace(/^\uFEFF/u, ""))) inSection = true;
|
|
1885
2123
|
if (inSection) return true;
|
|
1886
2124
|
const key = getNpmrcLineKey(line);
|
|
1887
2125
|
if (!key) return true;
|
|
1888
|
-
|
|
1889
|
-
|
|
2126
|
+
if (migratedKeySet.has(key)) {
|
|
2127
|
+
removed = true;
|
|
2128
|
+
return false;
|
|
2129
|
+
}
|
|
2130
|
+
return true;
|
|
2131
|
+
});
|
|
2132
|
+
if (!removed) return false;
|
|
2133
|
+
const updatedContent = lines.join("\n").trimEnd();
|
|
1890
2134
|
if (compatibility !== "v10" && !updatedContent.trim()) {
|
|
1891
2135
|
await fsRemoveFile(path);
|
|
1892
|
-
return;
|
|
2136
|
+
return true;
|
|
1893
2137
|
}
|
|
1894
|
-
|
|
2138
|
+
return fsWriteFileIfChanged(path, updatedContent);
|
|
1895
2139
|
}
|
|
1896
2140
|
/**
|
|
1897
2141
|
* Read `.npmrc` and return settings that should be migrated into workspace config.
|
|
@@ -2321,6 +2565,7 @@ function resolveReplacementSettingKey(sourceKey, targetKey, npmrc) {
|
|
|
2321
2565
|
* @returns Original source keys that can be pruned
|
|
2322
2566
|
*/
|
|
2323
2567
|
function selectAppliedRootKeys({ appliedOrderedFilterKeys, finalSettings, normalizedSettings, keys, npmrc = false, runtimeApplied, runtimeVersion, sourceSettings }) {
|
|
2568
|
+
const hasUnappliedSkillsDisable = normalizedSettings.skills?.dirs?.length === 0 && finalSettings.skills?.dirs?.length !== 0;
|
|
2324
2569
|
return keys.filter((sourceKey) => {
|
|
2325
2570
|
const targetKey = npmrc ? Object.keys(camelcaseKeys({ [sourceKey]: true }))[0] ?? sourceKey : sourceKey;
|
|
2326
2571
|
if (!Object.hasOwn(normalizedSettings, targetKey)) {
|
|
@@ -2343,6 +2588,7 @@ function selectAppliedRootKeys({ appliedOrderedFilterKeys, finalSettings, normal
|
|
|
2343
2588
|
const expected = Reflect.get(normalizedSettings, targetKey);
|
|
2344
2589
|
return appliedOrderedFilterKeys.has(targetKey) && containsOrderedFilterValue(actual, expected);
|
|
2345
2590
|
}
|
|
2591
|
+
if (targetKey === "skills" && hasUnappliedSkillsDisable) return false;
|
|
2346
2592
|
return containsMigratedValue(Reflect.get(finalSettings, targetKey), Reflect.get(normalizedSettings, targetKey));
|
|
2347
2593
|
});
|
|
2348
2594
|
}
|
|
@@ -2428,10 +2674,10 @@ async function persistMigration(options) {
|
|
|
2428
2674
|
if (!cleanNpmrc) return result;
|
|
2429
2675
|
const pruneTasks = projectNpmrcs.projects.flatMap((project) => {
|
|
2430
2676
|
const appliedKeys = selectAppliedProjectKeys(project, finalSettings);
|
|
2431
|
-
return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then(() => project.npmrcPath)] : [];
|
|
2677
|
+
return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then((changed) => changed ? project.npmrcPath : void 0)] : [];
|
|
2432
2678
|
});
|
|
2433
|
-
if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then(() => npmrcPath));
|
|
2434
|
-
const cleanedNpmrcPaths = await Promise.all(pruneTasks);
|
|
2679
|
+
if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then((changed) => changed ? npmrcPath : void 0));
|
|
2680
|
+
const cleanedNpmrcPaths = (await Promise.all(pruneTasks)).filter(isString);
|
|
2435
2681
|
result.changedFiles.push(...cleanedNpmrcPaths);
|
|
2436
2682
|
result.sourceSettingsCleaned ||= cleanedNpmrcPaths.length > 0;
|
|
2437
2683
|
return result;
|
|
@@ -2527,9 +2773,9 @@ function normalizeSideEffectsCache(settings) {
|
|
|
2527
2773
|
* @returns Nothing; the URL's declaration is created or updated in place
|
|
2528
2774
|
*/
|
|
2529
2775
|
function addRegistryScope(declarations, url, scope) {
|
|
2530
|
-
declarations
|
|
2531
|
-
const declaration = declarations[url];
|
|
2776
|
+
const declaration = declarations.get(url) ?? {};
|
|
2532
2777
|
declaration.scopes = unique([...declaration.scopes ?? [], scope]);
|
|
2778
|
+
declarations.set(url, declaration);
|
|
2533
2779
|
}
|
|
2534
2780
|
/**
|
|
2535
2781
|
* Find source URLs with multiple active aliases across the merged settings.
|
|
@@ -2558,9 +2804,9 @@ function findRegistryAliasConflicts(prefixesByUrl, namedRegistries, canonicalPre
|
|
|
2558
2804
|
function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
|
|
2559
2805
|
const { namedRegistries } = settings;
|
|
2560
2806
|
if (!namedRegistries) return;
|
|
2561
|
-
const declarations =
|
|
2807
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
2562
2808
|
for (const [key, value] of Object.entries(settings.registries ?? {})) if (isString(value)) addRegistryScope(declarations, value, key === "default" ? "@" : key);
|
|
2563
|
-
else declarations
|
|
2809
|
+
else declarations.set(key, { ...value });
|
|
2564
2810
|
const canonicalPrefixes = new Set(Object.values(canonicalSettings.registries ?? {}).flatMap((value) => !isString(value) && isString(value?.prefix) ? [value.prefix] : []));
|
|
2565
2811
|
const prefixesByUrl = /* @__PURE__ */ new Map();
|
|
2566
2812
|
const registryAliases = Object.entries(namedRegistries).filter(([prefix]) => !canonicalPrefixes.has(prefix));
|
|
@@ -2575,17 +2821,17 @@ function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
|
|
|
2575
2821
|
return;
|
|
2576
2822
|
}
|
|
2577
2823
|
for (const [url, [prefix]] of prefixesByUrl) {
|
|
2578
|
-
declarations
|
|
2579
|
-
const
|
|
2580
|
-
const canonicalDeclaration = canonicalSettings.registries?.[url];
|
|
2824
|
+
const declaration = declarations.get(url) ?? {};
|
|
2825
|
+
const canonicalDeclaration = canonicalSettings.registries && Object.hasOwn(canonicalSettings.registries, url) ? canonicalSettings.registries[url] : void 0;
|
|
2581
2826
|
const declaredPrefix = (isString(canonicalDeclaration) ? void 0 : canonicalDeclaration?.prefix) ?? declaration.prefix;
|
|
2582
2827
|
if (isString(declaredPrefix) && declaredPrefix !== prefix) {
|
|
2583
2828
|
warnings.push(`namedRegistries was kept because ${url} already declares prefix ${declaredPrefix}.`);
|
|
2584
2829
|
return;
|
|
2585
2830
|
}
|
|
2586
2831
|
declaration.prefix = prefix;
|
|
2832
|
+
declarations.set(url, declaration);
|
|
2587
2833
|
}
|
|
2588
|
-
settings.registries = declarations;
|
|
2834
|
+
settings.registries = Object.fromEntries(declarations);
|
|
2589
2835
|
Reflect.deleteProperty(settings, "namedRegistries");
|
|
2590
2836
|
}
|
|
2591
2837
|
/**
|
|
@@ -3147,7 +3393,7 @@ const bold = getColor("bold");
|
|
|
3147
3393
|
//#endregion
|
|
3148
3394
|
//#region package.json
|
|
3149
3395
|
var name = "pnpm-settings-migrator";
|
|
3150
|
-
var version = "0.
|
|
3396
|
+
var version = "0.17.0";
|
|
3151
3397
|
//#endregion
|
|
3152
3398
|
//#region src/cli/options.ts
|
|
3153
3399
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -230,10 +230,52 @@ interface PnpmLockfileSettings {
|
|
|
230
230
|
*/
|
|
231
231
|
includeResolutionSettings?: boolean | null;
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Dependency capabilities introduced in pnpm 12.11.0. Strings are undecided
|
|
235
|
+
* prompts; null leaves a capability unset.
|
|
236
|
+
*/
|
|
237
|
+
interface PnpmPackagePermissions {
|
|
238
|
+
/**
|
|
239
|
+
* Lifecycle script decision, taking precedence over the matching allowBuilds
|
|
240
|
+
* entry when boolean. An undecided string leaves that entry effective.
|
|
241
|
+
*/
|
|
242
|
+
build?: boolean | string | null;
|
|
243
|
+
/**
|
|
244
|
+
* Whether pnpm may link the agent skills shipped by the dependency.
|
|
245
|
+
*/
|
|
246
|
+
skills?: boolean | string | null;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Agent skill linking configuration introduced in pnpm 12.11.0.
|
|
250
|
+
*/
|
|
251
|
+
interface PnpmSkillsSettings {
|
|
252
|
+
/**
|
|
253
|
+
* Workspace-relative agent skill directories, replacing detected directories.
|
|
254
|
+
* An empty list disables linking; null leaves directory detection enabled.
|
|
255
|
+
*/
|
|
256
|
+
dirs?: string[] | null;
|
|
257
|
+
}
|
|
233
258
|
/**
|
|
234
259
|
* Settings supported by pnpm v12, subject to the resolved version capabilities.
|
|
235
260
|
*/
|
|
236
261
|
interface PnpmSettingsV12 {
|
|
262
|
+
/**
|
|
263
|
+
* Link available companion @types packages as optional peers (pnpm 12.12+).
|
|
264
|
+
* Null leaves pnpm's default unchanged.
|
|
265
|
+
*/
|
|
266
|
+
addMissingPeerTypes?: boolean | null;
|
|
267
|
+
/**
|
|
268
|
+
* Project-owned remote cache configuration (pnpm 12.12+).
|
|
269
|
+
*/
|
|
270
|
+
remoteCache?: PnpmRemoteCacheSettings | null;
|
|
271
|
+
/**
|
|
272
|
+
* Per-dependency build and agent skill permissions (pnpm 12.11+).
|
|
273
|
+
*/
|
|
274
|
+
permissions?: Record<string, PnpmPackagePermissions | null> | null;
|
|
275
|
+
/**
|
|
276
|
+
* Agent skill linking options (pnpm 12.11+).
|
|
277
|
+
*/
|
|
278
|
+
skills?: PnpmSkillsSettings | null;
|
|
237
279
|
/**
|
|
238
280
|
* Dependency linking mode. Loaded linking and its options require pnpm 12.10+.
|
|
239
281
|
*/
|
|
@@ -320,6 +362,16 @@ interface PnpmSettingsV12 {
|
|
|
320
362
|
*/
|
|
321
363
|
tasks?: Record<string, PnpmTaskSettings>;
|
|
322
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* Remote cache options allowed in project YAML. Server and signing settings
|
|
367
|
+
* belong in trusted global configuration or environment variables.
|
|
368
|
+
*/
|
|
369
|
+
interface PnpmRemoteCacheSettings {
|
|
370
|
+
/**
|
|
371
|
+
* Organization owning shared artifacts; null leaves it unset.
|
|
372
|
+
*/
|
|
373
|
+
org?: string | null;
|
|
374
|
+
}
|
|
323
375
|
//#endregion
|
|
324
376
|
//#region src/types/workspace.d.ts
|
|
325
377
|
/**
|
|
@@ -440,6 +492,11 @@ interface PnpmSettingsDeprecated {
|
|
|
440
492
|
* Settings introduced in pnpm v11.
|
|
441
493
|
*/
|
|
442
494
|
interface PnpmSettingsV11 {
|
|
495
|
+
/**
|
|
496
|
+
* Publish provenance preference. Consumed from workspace configuration by
|
|
497
|
+
* pnpm v11 and pnpm v12 starting with 12.11. Null leaves it unset.
|
|
498
|
+
*/
|
|
499
|
+
provenance?: boolean | null;
|
|
443
500
|
/**
|
|
444
501
|
* Fail when no workspace project matches a filter. Accepted by pnpm v11 and
|
|
445
502
|
* consumed from workspace configuration by pnpm v12 starting with 12.10.
|
|
@@ -484,7 +541,7 @@ interface PnpmSettingsV11 {
|
|
|
484
541
|
/**
|
|
485
542
|
* `pnpm-workspace.yaml` types.
|
|
486
543
|
*/
|
|
487
|
-
type PnpmWorkspace = Omit<PnpmSettings, 'lockfile' | 'nodeLinker' | 'registries' | 'supportedArchitectures' | 'tasks'> & PnpmSettingsDeprecated & PnpmSettingsV11 & PnpmSettingsV12 & PnpmWorkspaceLegacy;
|
|
544
|
+
type PnpmWorkspace = Omit<PnpmSettings, 'lockfile' | 'nodeLinker' | 'provenance' | 'registries' | 'supportedArchitectures' | 'tasks'> & PnpmSettingsDeprecated & PnpmSettingsV11 & PnpmSettingsV12 & PnpmWorkspaceLegacy;
|
|
488
545
|
//#endregion
|
|
489
546
|
//#region src/types/package-json.d.ts
|
|
490
547
|
/**
|
|
@@ -692,4 +749,4 @@ export declare function migratePnpmSettings(rawOptions?: Options): Promise<Migra
|
|
|
692
749
|
*/
|
|
693
750
|
export declare function resolveOptions(options?: Options): ResolvedOptions;
|
|
694
751
|
//#endregion
|
|
695
|
-
export type { CompatibilityTarget, GlobalShimPolicy, MergeStrategy, MigrationResult, NpmRC, Options, PackageJson, PackageManagerEngine, PnpmCargoSettings, PnpmLoadedNodeLinkerSettings, PnpmLockfileSettings, PnpmPythonSettings, PnpmRegistryDeclaration, PnpmSettingsDeprecated, PnpmSettingsV11, PnpmSettingsV12, PnpmTaskSettings, PnpmWorkspace, PnpmWorkspaceLegacy, ResolvedOptions, RuntimeEngine, SettingsChange };
|
|
752
|
+
export type { CompatibilityTarget, GlobalShimPolicy, MergeStrategy, MigrationResult, NpmRC, Options, PackageJson, PackageManagerEngine, PnpmCargoSettings, PnpmLoadedNodeLinkerSettings, PnpmLockfileSettings, PnpmPackagePermissions, PnpmPythonSettings, PnpmRegistryDeclaration, PnpmRemoteCacheSettings, PnpmSettingsDeprecated, PnpmSettingsV11, PnpmSettingsV12, PnpmSkillsSettings, PnpmTaskSettings, PnpmWorkspace, PnpmWorkspaceLegacy, ResolvedOptions, RuntimeEngine, SettingsChange };
|
package/dist/index.mjs
CHANGED
|
@@ -500,7 +500,12 @@ const PNPM_V11_WORKSPACE_SETTINGS_FIELDS = unique([...PNPM_V10_NPMRC_SETTINGS_FI
|
|
|
500
500
|
/**
|
|
501
501
|
* Settings that are supported by v11 but not by pnpm v12.
|
|
502
502
|
*/
|
|
503
|
-
const PNPM_V11_ONLY_WORKSPACE_SETTINGS = [
|
|
503
|
+
const PNPM_V11_ONLY_WORKSPACE_SETTINGS = [
|
|
504
|
+
"confirmModulesPurge",
|
|
505
|
+
"filter",
|
|
506
|
+
"filterProd",
|
|
507
|
+
"packageConfigs"
|
|
508
|
+
];
|
|
504
509
|
/**
|
|
505
510
|
* Fields pnpm v11 accepts inside a `packageConfigs` entry.
|
|
506
511
|
*/
|
|
@@ -612,6 +617,38 @@ const PNPM_V12_10_MINIMUM_VERSION = [
|
|
|
612
617
|
0
|
|
613
618
|
];
|
|
614
619
|
/**
|
|
620
|
+
* Release introducing package permissions, agent skills, and workspace provenance.
|
|
621
|
+
*/
|
|
622
|
+
const PNPM_V12_11_MINIMUM_VERSION = [
|
|
623
|
+
12,
|
|
624
|
+
11,
|
|
625
|
+
0
|
|
626
|
+
];
|
|
627
|
+
/**
|
|
628
|
+
* Release introducing remote cache configuration and missing peer types.
|
|
629
|
+
*/
|
|
630
|
+
const PNPM_V12_12_MINIMUM_VERSION = [
|
|
631
|
+
12,
|
|
632
|
+
12,
|
|
633
|
+
0
|
|
634
|
+
];
|
|
635
|
+
/**
|
|
636
|
+
* Release reserving the tool registry prefix.
|
|
637
|
+
*/
|
|
638
|
+
const PNPM_V12_12_1_MINIMUM_VERSION = [
|
|
639
|
+
12,
|
|
640
|
+
12,
|
|
641
|
+
1
|
|
642
|
+
];
|
|
643
|
+
/**
|
|
644
|
+
* Workspace settings first consumed by pnpm v12 in 12.11.0.
|
|
645
|
+
*/
|
|
646
|
+
const PNPM_V12_11_WORKSPACE_SETTINGS = [
|
|
647
|
+
"permissions",
|
|
648
|
+
"skills",
|
|
649
|
+
"provenance"
|
|
650
|
+
];
|
|
651
|
+
/**
|
|
615
652
|
* Workspace settings first consumed by pnpm v12 in 12.10.0.
|
|
616
653
|
*/
|
|
617
654
|
const PNPM_V12_10_WORKSPACE_SETTINGS = ["failIfNoMatch"];
|
|
@@ -629,6 +666,16 @@ const PNPM_V12_7_WORKSPACE_SETTINGS = [
|
|
|
629
666
|
* @see https://github.com/pnpm/pnpm/blob/v12.4.0/pnpm/crates/config/src/workspace_yaml.rs
|
|
630
667
|
*/
|
|
631
668
|
const PNPM_SETTINGS_CAPABILITIES = [
|
|
669
|
+
{
|
|
670
|
+
minimumVersion: PNPM_V12_12_MINIMUM_VERSION,
|
|
671
|
+
workspaceFields: ["addMissingPeerTypes", "remoteCache"],
|
|
672
|
+
taskFields: []
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
minimumVersion: PNPM_V12_11_MINIMUM_VERSION,
|
|
676
|
+
workspaceFields: PNPM_V12_11_WORKSPACE_SETTINGS,
|
|
677
|
+
taskFields: []
|
|
678
|
+
},
|
|
632
679
|
{
|
|
633
680
|
minimumVersion: PNPM_V11_28_MINIMUM_VERSION,
|
|
634
681
|
workspaceFields: ["forceIgnoresPlatform"],
|
|
@@ -705,9 +752,82 @@ const PNPM_V12_ONLY_WORKSPACE_SETTINGS = [
|
|
|
705
752
|
"globalShims"
|
|
706
753
|
];
|
|
707
754
|
/**
|
|
755
|
+
* Legacy fields absent from pnpm v12's workspace reader through 12.12.1.
|
|
756
|
+
* Removed aliases with explicit migration replacements are kept separately.
|
|
757
|
+
*
|
|
758
|
+
* @see https://github.com/pnpm/pnpm/blob/v12.12.1/pnpm/crates/config/src/workspace_yaml/settings.rs
|
|
759
|
+
*/
|
|
760
|
+
const PNPM_V12_IGNORED_WORKSPACE_SETTINGS = [
|
|
761
|
+
"access",
|
|
762
|
+
"allowNew",
|
|
763
|
+
"allowSameVersion",
|
|
764
|
+
"auditIgnorePrune",
|
|
765
|
+
"autoConfirmAllPrompts",
|
|
766
|
+
"binLinks",
|
|
767
|
+
"commitHooks",
|
|
768
|
+
"cpu",
|
|
769
|
+
"depth",
|
|
770
|
+
"description",
|
|
771
|
+
"dev",
|
|
772
|
+
"dryRun",
|
|
773
|
+
"enablePnp",
|
|
774
|
+
"extraBinPaths",
|
|
775
|
+
"extraEnv",
|
|
776
|
+
"fetchingConcurrency",
|
|
777
|
+
"force",
|
|
778
|
+
"git",
|
|
779
|
+
"gitTagVersion",
|
|
780
|
+
"global",
|
|
781
|
+
"globalPath",
|
|
782
|
+
"globalPrefix",
|
|
783
|
+
"ignoreCurrentSpecifiers",
|
|
784
|
+
"json",
|
|
785
|
+
"libc",
|
|
786
|
+
"localAddress",
|
|
787
|
+
"lockfileOnly",
|
|
788
|
+
"long",
|
|
789
|
+
"message",
|
|
790
|
+
"npmPath",
|
|
791
|
+
"only",
|
|
792
|
+
"os",
|
|
793
|
+
"packDestination",
|
|
794
|
+
"packGzipLevel",
|
|
795
|
+
"parseable",
|
|
796
|
+
"pnpmExecPath",
|
|
797
|
+
"preserveAbsolutePaths",
|
|
798
|
+
"production",
|
|
799
|
+
"publishBranch",
|
|
800
|
+
"recursive",
|
|
801
|
+
"registriesByPrefix",
|
|
802
|
+
"registriesByScope",
|
|
803
|
+
"registryOptionsByUrl",
|
|
804
|
+
"requiredScripts",
|
|
805
|
+
"runtime",
|
|
806
|
+
"save",
|
|
807
|
+
"saveDev",
|
|
808
|
+
"saveOptional",
|
|
809
|
+
"saveProd",
|
|
810
|
+
"sideEffectsCacheRead",
|
|
811
|
+
"sideEffectsCacheWrite",
|
|
812
|
+
"signGitTag",
|
|
813
|
+
"strictSsl",
|
|
814
|
+
"tag",
|
|
815
|
+
"tryLoadDefaultPnpmfile",
|
|
816
|
+
"umask",
|
|
817
|
+
"useGitBranchLockfile",
|
|
818
|
+
"useLockfile",
|
|
819
|
+
"useRunningStoreServer",
|
|
820
|
+
"version",
|
|
821
|
+
"workspacePackagePatterns",
|
|
822
|
+
"workspacePackages",
|
|
823
|
+
"workspacePrefix",
|
|
824
|
+
"workspaceRoot",
|
|
825
|
+
"yes"
|
|
826
|
+
];
|
|
827
|
+
/**
|
|
708
828
|
* Lookup used to remove v11-only fields from the v12 workspace allowlist.
|
|
709
829
|
*/
|
|
710
|
-
const v11OnlyWorkspaceSettings = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
|
|
830
|
+
const v11OnlyWorkspaceSettings = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
|
|
711
831
|
/**
|
|
712
832
|
* Fields accepted by registry declarations with pnpm 12.9 concurrency limits.
|
|
713
833
|
*/
|
|
@@ -723,7 +843,7 @@ const PNPM_V12_REGISTRY_DECLARATION_FIELDS = [
|
|
|
723
843
|
/**
|
|
724
844
|
* Settings accepted by a pnpm v12 project workspace manifest.
|
|
725
845
|
*/
|
|
726
|
-
const PNPM_V12_WORKSPACE_SETTINGS_FIELDS = [...PNPM_V11_WORKSPACE_SETTINGS_FIELDS.filter((field) => !v11OnlyWorkspaceSettings.has(field) && !PNPM_V12_6_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_7_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_10_WORKSPACE_SETTINGS.includes(field)), ...PNPM_V12_ONLY_WORKSPACE_SETTINGS];
|
|
846
|
+
const PNPM_V12_WORKSPACE_SETTINGS_FIELDS = [...PNPM_V11_WORKSPACE_SETTINGS_FIELDS.filter((field) => !v11OnlyWorkspaceSettings.has(field) && !PNPM_V12_6_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_7_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_10_WORKSPACE_SETTINGS.includes(field) && !PNPM_V12_11_WORKSPACE_SETTINGS.includes(field)), ...PNPM_V12_ONLY_WORKSPACE_SETTINGS];
|
|
727
847
|
//#endregion
|
|
728
848
|
//#region src/constants/settings-schema.ts
|
|
729
849
|
/**
|
|
@@ -742,7 +862,7 @@ const PROJECT_REFUSED_SETTINGS = new Set(PNPM_PROJECT_REFUSED_SETTINGS);
|
|
|
742
862
|
/**
|
|
743
863
|
* Fields reported as incompatible when selecting settings for v12.
|
|
744
864
|
*/
|
|
745
|
-
const PNPM_V11_ONLY_SETTINGS = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
|
|
865
|
+
const PNPM_V11_ONLY_SETTINGS = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
|
|
746
866
|
/**
|
|
747
867
|
* Allowed workspace fields for the v11 target.
|
|
748
868
|
*/
|
|
@@ -1086,6 +1206,63 @@ function formatSettingsIssues(options) {
|
|
|
1086
1206
|
return warnings;
|
|
1087
1207
|
}
|
|
1088
1208
|
//#endregion
|
|
1209
|
+
//#region src/features/settings/registries.ts
|
|
1210
|
+
/**
|
|
1211
|
+
* Check that a registry location is a URL rather than an object property name.
|
|
1212
|
+
*
|
|
1213
|
+
* @param value - Untrusted registry location
|
|
1214
|
+
*
|
|
1215
|
+
* @returns Whether the location is an absolute or scheme-relative URL
|
|
1216
|
+
*/
|
|
1217
|
+
function isRegistryUrl(value) {
|
|
1218
|
+
return isString(value) && URL.canParse(value.startsWith("//") ? `https:${value}` : value);
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Validate locations in legacy registry maps and URL-keyed declarations.
|
|
1222
|
+
*
|
|
1223
|
+
* @param key - Canonical workspace setting name
|
|
1224
|
+
* @param value - Untrusted registry configuration
|
|
1225
|
+
*
|
|
1226
|
+
* @returns Whether the complete setting must be retained in its source
|
|
1227
|
+
*/
|
|
1228
|
+
function hasInvalidRegistryLocations(key, value) {
|
|
1229
|
+
if (!["namedRegistries", "registries"].includes(key) || value === null) return false;
|
|
1230
|
+
if (!isPlainObject(value)) return true;
|
|
1231
|
+
if (key === "namedRegistries") return Object.values(value).some((url) => !isRegistryUrl(url));
|
|
1232
|
+
return Object.entries(value).some(([url, entry]) => isString(entry) ? !isRegistryUrl(entry) : !isRegistryUrl(url));
|
|
1233
|
+
}
|
|
1234
|
+
//#endregion
|
|
1235
|
+
//#region src/features/settings/permissions.ts
|
|
1236
|
+
/**
|
|
1237
|
+
* Validate a dependency's capability map without dropping unknown permissions.
|
|
1238
|
+
*
|
|
1239
|
+
* @param value - Untrusted per-package permission entry
|
|
1240
|
+
*
|
|
1241
|
+
* @returns Whether pnpm cannot consume every capability in the entry
|
|
1242
|
+
*/
|
|
1243
|
+
function hasInvalidPackagePermissions(value) {
|
|
1244
|
+
if (value === null) return false;
|
|
1245
|
+
if (!value || typeof value !== "object" || isArray(value)) return true;
|
|
1246
|
+
return Object.entries(value).some(([capability, decision]) => !["build", "skills"].includes(capability) || decision !== null && !isBoolean(decision) && !isString(decision));
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Validate permissions and skill directories introduced in pnpm 12.11.
|
|
1250
|
+
* Preserve an entire object when a nested value is invalid or ignored upstream.
|
|
1251
|
+
*
|
|
1252
|
+
* @param key - Canonical workspace setting name
|
|
1253
|
+
* @param value - Untrusted source or existing workspace value
|
|
1254
|
+
*
|
|
1255
|
+
* @returns Whether the setting contains unsupported nested values
|
|
1256
|
+
*/
|
|
1257
|
+
function hasInvalidPermissionSettings(key, value) {
|
|
1258
|
+
if (!["permissions", "skills"].includes(key) || value === null) return false;
|
|
1259
|
+
if (!value || typeof value !== "object" || isArray(value)) return true;
|
|
1260
|
+
if (key === "permissions") return Object.values(value).some(hasInvalidPackagePermissions);
|
|
1261
|
+
if (Object.keys(value).some((field) => field !== "dirs")) return true;
|
|
1262
|
+
const dirs = Reflect.get(value, "dirs");
|
|
1263
|
+
return Object.hasOwn(value, "dirs") && dirs !== null && (!isArray(dirs) || !dirs.every(isString));
|
|
1264
|
+
}
|
|
1265
|
+
//#endregion
|
|
1089
1266
|
//#region src/features/settings/pnpm-v12-values.ts
|
|
1090
1267
|
/**
|
|
1091
1268
|
* Check a loaded linker object without discarding unsupported options.
|
|
@@ -1199,6 +1376,22 @@ function hasIncompatibleV12SettingValue(key, value, target) {
|
|
|
1199
1376
|
return false;
|
|
1200
1377
|
}
|
|
1201
1378
|
//#endregion
|
|
1379
|
+
//#region src/features/settings/remote-cache.ts
|
|
1380
|
+
/**
|
|
1381
|
+
* Accept only the project-owned remote cache organization.
|
|
1382
|
+
* Keep machine configuration and unknown fields intact in their source.
|
|
1383
|
+
*
|
|
1384
|
+
* @param key - Canonical workspace setting name
|
|
1385
|
+
* @param value - Untrusted incoming or existing workspace value
|
|
1386
|
+
*
|
|
1387
|
+
* @returns Whether the remote cache setting cannot migrate to project YAML
|
|
1388
|
+
*/
|
|
1389
|
+
function hasInvalidRemoteCacheValue(key, value) {
|
|
1390
|
+
if (key !== "remoteCache" || value === null) return false;
|
|
1391
|
+
if (!value || typeof value !== "object" || isArray(value)) return true;
|
|
1392
|
+
return Object.entries(value).some(([field, entry]) => field !== "org" || entry !== null && !isString(entry));
|
|
1393
|
+
}
|
|
1394
|
+
//#endregion
|
|
1202
1395
|
//#region src/features/settings/versioned-values.ts
|
|
1203
1396
|
/**
|
|
1204
1397
|
* Bounds of the signed 32-bit integer used by pnpm's task priority.
|
|
@@ -1247,6 +1440,7 @@ function hasInvalidScalarValue(key, value, target) {
|
|
|
1247
1440
|
"ndjson",
|
|
1248
1441
|
"silent"
|
|
1249
1442
|
].includes(value);
|
|
1443
|
+
if (key === "provenance" || key === "addMissingPeerTypes") return value !== null && !isBoolean(value);
|
|
1250
1444
|
if (key === "progress") return !isBoolean(value);
|
|
1251
1445
|
if (key === "tagVersionPrefix") return !isString(value);
|
|
1252
1446
|
if (key === "loglevel") return !isString(value) || !value.includes("${") && ![
|
|
@@ -1276,6 +1470,17 @@ function hasInvalidReleaseValue(key, value, target) {
|
|
|
1276
1470
|
return false;
|
|
1277
1471
|
}
|
|
1278
1472
|
/**
|
|
1473
|
+
* Check release-specific reserved aliases without broadening case sensitivity.
|
|
1474
|
+
*
|
|
1475
|
+
* @param prefix - Registry alias as declared
|
|
1476
|
+
* @param target - Confirmed target version
|
|
1477
|
+
*
|
|
1478
|
+
* @returns Whether the alias shadows a protocol introduced by these releases
|
|
1479
|
+
*/
|
|
1480
|
+
function isReservedRegistryPrefix(prefix, target) {
|
|
1481
|
+
return prefix.toLowerCase() === "pkg" && supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION) || prefix === "tool" && supportsMinimumVersion(target.version, PNPM_V12_12_1_MINIMUM_VERSION);
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1279
1484
|
* Check value shapes that changed within a pnpm major release.
|
|
1280
1485
|
*
|
|
1281
1486
|
* Reject the whole setting so cleanup cannot discard unsupported nested values.
|
|
@@ -1287,7 +1492,7 @@ function hasInvalidReleaseValue(key, value, target) {
|
|
|
1287
1492
|
* @returns Whether the value uses a shape unsupported by the selected release
|
|
1288
1493
|
*/
|
|
1289
1494
|
function hasIncompatibleSettingValue(key, value, target) {
|
|
1290
|
-
if (hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
|
|
1495
|
+
if (hasInvalidPermissionSettings(key, value) || hasInvalidRemoteCacheValue(key, value) || hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
|
|
1291
1496
|
if (hasInvalidScalarValue(key, value, target)) return true;
|
|
1292
1497
|
const supportsV12_5 = supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION);
|
|
1293
1498
|
if (key === "supportedArchitectures" && isArray(value)) return !supportsV12_5;
|
|
@@ -1305,19 +1510,42 @@ function hasIncompatibleSettingValue(key, value, target) {
|
|
|
1305
1510
|
"constraints"
|
|
1306
1511
|
].some((field) => Object.hasOwn(value, field));
|
|
1307
1512
|
}
|
|
1308
|
-
if (key === "namedRegistries"
|
|
1513
|
+
if (key === "namedRegistries") return Object.keys(value).some((prefix) => isReservedRegistryPrefix(prefix, target));
|
|
1309
1514
|
if (key === "registries") return Object.values(value).some((entry) => {
|
|
1310
1515
|
if (!entry || typeof entry !== "object") return false;
|
|
1311
1516
|
if (Object.hasOwn(entry, "ecosystem") && !supportsV12_5) return true;
|
|
1312
1517
|
if (Object.hasOwn(entry, "packages") && !supportsMinimumVersion(target.version, PNPM_V12_5_1_MINIMUM_VERSION)) return true;
|
|
1313
1518
|
const prefix = Reflect.get(entry, "prefix");
|
|
1314
|
-
return
|
|
1519
|
+
return isString(prefix) && isReservedRegistryPrefix(prefix, target);
|
|
1315
1520
|
});
|
|
1316
1521
|
return false;
|
|
1317
1522
|
}
|
|
1318
1523
|
//#endregion
|
|
1319
1524
|
//#region src/features/settings/schema.ts
|
|
1320
1525
|
/**
|
|
1526
|
+
* Detect legacy runtime fields without a destination in pnpm v11 and newer.
|
|
1527
|
+
*
|
|
1528
|
+
* @param key - Canonical setting name
|
|
1529
|
+
* @param value - Untrusted legacy runtime declaration
|
|
1530
|
+
* @param compatibility - Target major controlling runtime conversion
|
|
1531
|
+
*
|
|
1532
|
+
* @returns Whether converting the runtime would discard sibling fields
|
|
1533
|
+
*/
|
|
1534
|
+
function hasUnmigratableRuntimeFields(key, value, compatibility) {
|
|
1535
|
+
return Boolean(key === "executionEnv" && compatibility !== "v10" && value && typeof value === "object" && Object.keys(value).some((field) => field !== "nodeVersion"));
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Check task declarations against the fields consumed by the target release.
|
|
1539
|
+
*
|
|
1540
|
+
* @param value - Untrusted task declarations
|
|
1541
|
+
* @param taskSettings - Supported task field names
|
|
1542
|
+
*
|
|
1543
|
+
* @returns Whether any declared task field is unsupported
|
|
1544
|
+
*/
|
|
1545
|
+
function hasUnsupportedTaskFields(value, taskSettings) {
|
|
1546
|
+
return Boolean(value && typeof value === "object" && Object.values(value).some((task) => task && typeof task === "object" && Object.keys(task).some((field) => !taskSettings.has(field))));
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1321
1549
|
* Create an empty collection of schema selection issues.
|
|
1322
1550
|
*
|
|
1323
1551
|
* @returns Empty issue lists for every supported issue category
|
|
@@ -1422,8 +1650,8 @@ function resolveSettingIssue({ allowedFields, target, key, npmrc, value }) {
|
|
|
1422
1650
|
if (compatibility !== "v10" && PROJECT_REFUSED_SETTINGS.has(key)) return "refused";
|
|
1423
1651
|
if (REGISTRY_SETTINGS.has(key) && containsUnsafeRegistryValue(value)) return "unsafe";
|
|
1424
1652
|
if (PROXY_SETTINGS.has(key) && isString(value) && value.includes("${")) return "unsafe";
|
|
1425
|
-
if (hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
1426
|
-
if (key === "tasks" &&
|
|
1653
|
+
if (hasInvalidRegistryLocations(key, value) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
1654
|
+
if (key === "tasks" && hasUnsupportedTaskFields(value, taskSettings)) return "incompatible";
|
|
1427
1655
|
if (!workspaceSettings.has(key)) return isSettingFromAnotherVersion(key, compatibility) ? "incompatible" : "unknown";
|
|
1428
1656
|
if (allowedFields && !allowedFields.has(key)) return "unsupported";
|
|
1429
1657
|
}
|
|
@@ -1832,8 +2060,17 @@ function getNpmrcLineKey(line) {
|
|
|
1832
2060
|
const trimmed = line.trim();
|
|
1833
2061
|
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) return;
|
|
1834
2062
|
const index = trimmed.indexOf("=");
|
|
1835
|
-
if (index
|
|
1836
|
-
|
|
2063
|
+
if (index === 0) return;
|
|
2064
|
+
let key = (index < 0 ? trimmed : trimmed.slice(0, index)).trim();
|
|
2065
|
+
if (key.startsWith("\"") && key.endsWith("\"") || key.startsWith("'") && key.endsWith("'")) {
|
|
2066
|
+
if (key.startsWith("'")) key = key.slice(1, -1);
|
|
2067
|
+
try {
|
|
2068
|
+
const parsed = JSON.parse(key);
|
|
2069
|
+
if (!isString(parsed)) return;
|
|
2070
|
+
key = parsed;
|
|
2071
|
+
} catch {}
|
|
2072
|
+
} else key = key.replace(/\\(?<escaped>[\\;#])|[;#].*$/gu, (_, escaped) => escaped ?? "");
|
|
2073
|
+
return normalizeNpmrcKey(key);
|
|
1837
2074
|
}
|
|
1838
2075
|
/**
|
|
1839
2076
|
* Check whether a `.npmrc` key is auth/registry-related and should stay in
|
|
@@ -1860,7 +2097,7 @@ function isNpmrcAuthOrRegistryKey(key) {
|
|
|
1860
2097
|
* @param compatibility - Concrete pnpm compatibility target
|
|
1861
2098
|
* @param migratedKeys - Original `.npmrc` keys that were migrated
|
|
1862
2099
|
*
|
|
1863
|
-
* @returns
|
|
2100
|
+
* @returns Whether the file was changed or removed
|
|
1864
2101
|
*
|
|
1865
2102
|
* @throws {Error} When file read/write operations fail
|
|
1866
2103
|
*
|
|
@@ -1873,18 +2110,25 @@ async function pruneNpmrc(path, compatibility, migratedKeys) {
|
|
|
1873
2110
|
const migratedKeySet = new Set(migratedKeys.map(normalizeNpmrcKey));
|
|
1874
2111
|
const content = await fsReadFile(path);
|
|
1875
2112
|
let inSection = false;
|
|
1876
|
-
|
|
2113
|
+
let removed = false;
|
|
2114
|
+
const lines = content.split(/\r\n|[\r\n]/u).filter((line) => {
|
|
1877
2115
|
if (/^\[[^\]]*\]\s*$/u.test(line.replace(/^\uFEFF/u, ""))) inSection = true;
|
|
1878
2116
|
if (inSection) return true;
|
|
1879
2117
|
const key = getNpmrcLineKey(line);
|
|
1880
2118
|
if (!key) return true;
|
|
1881
|
-
|
|
1882
|
-
|
|
2119
|
+
if (migratedKeySet.has(key)) {
|
|
2120
|
+
removed = true;
|
|
2121
|
+
return false;
|
|
2122
|
+
}
|
|
2123
|
+
return true;
|
|
2124
|
+
});
|
|
2125
|
+
if (!removed) return false;
|
|
2126
|
+
const updatedContent = lines.join("\n").trimEnd();
|
|
1883
2127
|
if (compatibility !== "v10" && !updatedContent.trim()) {
|
|
1884
2128
|
await fsRemoveFile(path);
|
|
1885
|
-
return;
|
|
2129
|
+
return true;
|
|
1886
2130
|
}
|
|
1887
|
-
|
|
2131
|
+
return fsWriteFileIfChanged(path, updatedContent);
|
|
1888
2132
|
}
|
|
1889
2133
|
/**
|
|
1890
2134
|
* Read `.npmrc` and return settings that should be migrated into workspace config.
|
|
@@ -2314,6 +2558,7 @@ function resolveReplacementSettingKey(sourceKey, targetKey, npmrc) {
|
|
|
2314
2558
|
* @returns Original source keys that can be pruned
|
|
2315
2559
|
*/
|
|
2316
2560
|
function selectAppliedRootKeys({ appliedOrderedFilterKeys, finalSettings, normalizedSettings, keys, npmrc = false, runtimeApplied, runtimeVersion, sourceSettings }) {
|
|
2561
|
+
const hasUnappliedSkillsDisable = normalizedSettings.skills?.dirs?.length === 0 && finalSettings.skills?.dirs?.length !== 0;
|
|
2317
2562
|
return keys.filter((sourceKey) => {
|
|
2318
2563
|
const targetKey = npmrc ? Object.keys(camelcaseKeys({ [sourceKey]: true }))[0] ?? sourceKey : sourceKey;
|
|
2319
2564
|
if (!Object.hasOwn(normalizedSettings, targetKey)) {
|
|
@@ -2336,6 +2581,7 @@ function selectAppliedRootKeys({ appliedOrderedFilterKeys, finalSettings, normal
|
|
|
2336
2581
|
const expected = Reflect.get(normalizedSettings, targetKey);
|
|
2337
2582
|
return appliedOrderedFilterKeys.has(targetKey) && containsOrderedFilterValue(actual, expected);
|
|
2338
2583
|
}
|
|
2584
|
+
if (targetKey === "skills" && hasUnappliedSkillsDisable) return false;
|
|
2339
2585
|
return containsMigratedValue(Reflect.get(finalSettings, targetKey), Reflect.get(normalizedSettings, targetKey));
|
|
2340
2586
|
});
|
|
2341
2587
|
}
|
|
@@ -2421,10 +2667,10 @@ async function persistMigration(options) {
|
|
|
2421
2667
|
if (!cleanNpmrc) return result;
|
|
2422
2668
|
const pruneTasks = projectNpmrcs.projects.flatMap((project) => {
|
|
2423
2669
|
const appliedKeys = selectAppliedProjectKeys(project, finalSettings);
|
|
2424
|
-
return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then(() => project.npmrcPath)] : [];
|
|
2670
|
+
return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then((changed) => changed ? project.npmrcPath : void 0)] : [];
|
|
2425
2671
|
});
|
|
2426
|
-
if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then(() => npmrcPath));
|
|
2427
|
-
const cleanedNpmrcPaths = await Promise.all(pruneTasks);
|
|
2672
|
+
if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then((changed) => changed ? npmrcPath : void 0));
|
|
2673
|
+
const cleanedNpmrcPaths = (await Promise.all(pruneTasks)).filter(isString);
|
|
2428
2674
|
result.changedFiles.push(...cleanedNpmrcPaths);
|
|
2429
2675
|
result.sourceSettingsCleaned ||= cleanedNpmrcPaths.length > 0;
|
|
2430
2676
|
return result;
|
|
@@ -2520,9 +2766,9 @@ function normalizeSideEffectsCache(settings) {
|
|
|
2520
2766
|
* @returns Nothing; the URL's declaration is created or updated in place
|
|
2521
2767
|
*/
|
|
2522
2768
|
function addRegistryScope(declarations, url, scope) {
|
|
2523
|
-
declarations
|
|
2524
|
-
const declaration = declarations[url];
|
|
2769
|
+
const declaration = declarations.get(url) ?? {};
|
|
2525
2770
|
declaration.scopes = unique([...declaration.scopes ?? [], scope]);
|
|
2771
|
+
declarations.set(url, declaration);
|
|
2526
2772
|
}
|
|
2527
2773
|
/**
|
|
2528
2774
|
* Find source URLs with multiple active aliases across the merged settings.
|
|
@@ -2551,9 +2797,9 @@ function findRegistryAliasConflicts(prefixesByUrl, namedRegistries, canonicalPre
|
|
|
2551
2797
|
function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
|
|
2552
2798
|
const { namedRegistries } = settings;
|
|
2553
2799
|
if (!namedRegistries) return;
|
|
2554
|
-
const declarations =
|
|
2800
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
2555
2801
|
for (const [key, value] of Object.entries(settings.registries ?? {})) if (isString(value)) addRegistryScope(declarations, value, key === "default" ? "@" : key);
|
|
2556
|
-
else declarations
|
|
2802
|
+
else declarations.set(key, { ...value });
|
|
2557
2803
|
const canonicalPrefixes = new Set(Object.values(canonicalSettings.registries ?? {}).flatMap((value) => !isString(value) && isString(value?.prefix) ? [value.prefix] : []));
|
|
2558
2804
|
const prefixesByUrl = /* @__PURE__ */ new Map();
|
|
2559
2805
|
const registryAliases = Object.entries(namedRegistries).filter(([prefix]) => !canonicalPrefixes.has(prefix));
|
|
@@ -2568,17 +2814,17 @@ function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
|
|
|
2568
2814
|
return;
|
|
2569
2815
|
}
|
|
2570
2816
|
for (const [url, [prefix]] of prefixesByUrl) {
|
|
2571
|
-
declarations
|
|
2572
|
-
const
|
|
2573
|
-
const canonicalDeclaration = canonicalSettings.registries?.[url];
|
|
2817
|
+
const declaration = declarations.get(url) ?? {};
|
|
2818
|
+
const canonicalDeclaration = canonicalSettings.registries && Object.hasOwn(canonicalSettings.registries, url) ? canonicalSettings.registries[url] : void 0;
|
|
2574
2819
|
const declaredPrefix = (isString(canonicalDeclaration) ? void 0 : canonicalDeclaration?.prefix) ?? declaration.prefix;
|
|
2575
2820
|
if (isString(declaredPrefix) && declaredPrefix !== prefix) {
|
|
2576
2821
|
warnings.push(`namedRegistries was kept because ${url} already declares prefix ${declaredPrefix}.`);
|
|
2577
2822
|
return;
|
|
2578
2823
|
}
|
|
2579
2824
|
declaration.prefix = prefix;
|
|
2825
|
+
declarations.set(url, declaration);
|
|
2580
2826
|
}
|
|
2581
|
-
settings.registries = declarations;
|
|
2827
|
+
settings.registries = Object.fromEntries(declarations);
|
|
2582
2828
|
Reflect.deleteProperty(settings, "namedRegistries");
|
|
2583
2829
|
}
|
|
2584
2830
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pnpm-settings-migrator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Move pnpm settings from `pnpm` field in `package.json` and `.npmrc` file to `pnpm-workspace.yaml`",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"migrator",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@ntnyq/utils": "^0.23.0",
|
|
46
46
|
"@pnpm/types": "^1102.1.1",
|
|
47
|
-
"cac": "^7.
|
|
47
|
+
"cac": "^7.1.0",
|
|
48
48
|
"camelcase-keys": "^10.0.3",
|
|
49
49
|
"consola": "^3.4.2",
|
|
50
50
|
"defu": "^6.1.7",
|
|
@@ -56,17 +56,17 @@
|
|
|
56
56
|
"yaml": "^2.9.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@types/node": "^26.6.
|
|
59
|
+
"@types/node": "^26.6.5",
|
|
60
60
|
"bumpp": "^12.3.0",
|
|
61
61
|
"husky": "^9.1.7",
|
|
62
62
|
"nano-staged": "^1.0.2",
|
|
63
63
|
"npm-run-all2": "^9.0.3",
|
|
64
|
-
"oxfmt": "^0.
|
|
65
|
-
"oxlint": "^1.
|
|
66
|
-
"tsdown": "^0.23.
|
|
64
|
+
"oxfmt": "^0.73.0",
|
|
65
|
+
"oxlint": "^1.88.0",
|
|
66
|
+
"tsdown": "^0.23.1",
|
|
67
67
|
"tsx": "^4.23.15",
|
|
68
68
|
"typescript": "^7.0.2",
|
|
69
|
-
"vitest": "^5.0.
|
|
69
|
+
"vitest": "^5.0.4"
|
|
70
70
|
},
|
|
71
71
|
"nano-staged": {
|
|
72
72
|
"*.{js,ts,mjs,tsx}": "oxlint --fix",
|