pnpm-settings-migrator 0.19.0 → 0.20.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 +54 -8
- package/dist/cli.mjs +178 -63
- package/dist/index.d.mts +24 -2
- package/dist/index.mjs +177 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -333,6 +333,26 @@ destination write fails. Credentials and trusted global settings keep their
|
|
|
333
333
|
existing protections. Earlier or unconfirmed targets retain incoming include
|
|
334
334
|
indexes with diagnostics and reject existing included workspaces.
|
|
335
335
|
|
|
336
|
+
From pnpm 12.16.0, `strictSsl` and `runtime` accept booleans or null,
|
|
337
|
+
`localAddress` and `tag` accept strings or null, and `requiredScripts` accepts
|
|
338
|
+
string arrays or null. These settings remain in their sources for earlier or
|
|
339
|
+
unconfirmed v12 targets. `runtime: false` disables installing declared runtimes;
|
|
340
|
+
`tag: next` selects that distribution tag when adding a package without a version.
|
|
341
|
+
Invalid IP address strings stay unchanged because pnpm treats them as unset.
|
|
342
|
+
String placeholders are preserved without expansion. Invalid types keep the
|
|
343
|
+
complete setting in its source. For example, `required-scripts[]=build` becomes
|
|
344
|
+
`requiredScripts: [build]` at 12.16.0 and stays in `.npmrc` at 12.15.0.
|
|
345
|
+
|
|
346
|
+
From pnpm 11.28.9, v11 registry declarations also accept `serverType: pnpr`.
|
|
347
|
+
Earlier or unconfirmed v11 targets retain the complete registry map. The setting
|
|
348
|
+
does not enable v12-only registry fields such as `networkConcurrency` or `ecosystem`.
|
|
349
|
+
|
|
350
|
+
pnpm 12.16 no longer consumes `scriptsPrependNodePath`, so the migrator keeps
|
|
351
|
+
incoming values in their sources and rejects existing workspace values before
|
|
352
|
+
writes. Project `ci` values and the global-only `symlinkedLockfileTargetDirs`
|
|
353
|
+
also stay in their sources. Configure the latter in trusted global configuration
|
|
354
|
+
or through `PNPM_CONFIG_SYMLINKED_LOCKFILE_TARGET_DIRS`.
|
|
355
|
+
|
|
336
356
|
pnpm v12 reads `filter` and `filterProd` from CLI arguments or `updateConfig`
|
|
337
357
|
hooks, not workspace YAML. These settings remain in their source with
|
|
338
358
|
diagnostics for v12 targets; existing YAML containing them blocks migration.
|
|
@@ -385,6 +405,24 @@ Notes:
|
|
|
385
405
|
- Invalid registry locations, including relative paths and object property names
|
|
386
406
|
such as `__proto__`, remain in their sources with diagnostics. Registry
|
|
387
407
|
conversion never uses inherited object properties as declarations.
|
|
408
|
+
- Registry maps are validated as a whole, including declarations without
|
|
409
|
+
versioned fields. An invalid sibling
|
|
410
|
+
declaration keeps every registry in its source under all merge strategies;
|
|
411
|
+
existing incompatible maps block migration before any writes.
|
|
412
|
+
- A URL key must name a declaration object. String values are accepted only in
|
|
413
|
+
the legacy scope map representation, whose keys cannot look like URLs.
|
|
414
|
+
Invalid registry settings remain intact in `package.json` and `.npmrc`, while
|
|
415
|
+
unrelated valid settings can still migrate and be cleaned from their sources.
|
|
416
|
+
- Legacy `<scope>: <url>` routes and URL-keyed declarations cannot be mixed,
|
|
417
|
+
including when `replaceDeprecated` introduces the mixture. The structure is
|
|
418
|
+
checked independently of versioned registry fields.
|
|
419
|
+
- Registry prefixes must be unique. A scope may be repeated only when it routes
|
|
420
|
+
to the same registry after the target pnpm version's URL normalization. From
|
|
421
|
+
12.11.1, the exact HTTPS and protocol-relative npm `.com` registry roots are
|
|
422
|
+
equivalent to `.org`; earlier or unconfirmed v12 targets compare them literally.
|
|
423
|
+
Routing conflicts introduced by merging or deprecated-setting conversion stop
|
|
424
|
+
migration before any destination writes
|
|
425
|
+
or source cleanup, including when registries belong to an included YAML file.
|
|
388
426
|
- Dynamic `${...}` values in `httpProxy`, `httpsProxy`, `noProxy`, `proxy`, and
|
|
389
427
|
`noproxy` stay in their source for every target. Project YAML does not expand
|
|
390
428
|
these placeholders; configure them globally in trusted configuration or use
|
|
@@ -429,21 +467,29 @@ Notes:
|
|
|
429
467
|
- The migrator does not update the `packageManager` version, CI environment variables,
|
|
430
468
|
shell setup, or pnpm commands in scripts. When `packageManager` still pins pnpm 10,
|
|
431
469
|
pass `--compatibility v11` explicitly and update the pin separately.
|
|
432
|
-
- Compatibility checks cover pnpm 11.28.
|
|
470
|
+
- Compatibility checks cover pnpm 11.28.9 and 12.16.1, including 11.28.0,
|
|
433
471
|
11.28.1, 11.28.2, 11.28.3, 11.28.4, 11.28.5, 11.28.6, 12.7.0, 12.8.0,
|
|
434
472
|
12.8.1, 12.8.2, 12.9.0, 12.9.1, 12.10.0, 12.10.1, 12.11.0, 12.11.1,
|
|
435
|
-
12.11.2, 12.12.0, 12.12.1, 12.13.0,
|
|
473
|
+
12.11.2, 12.12.0, 12.12.1, 12.13.0, 12.14.0, 11.28.7, 11.28.8,
|
|
474
|
+
12.15.0, and 12.16.0.
|
|
436
475
|
Earlier regressions remain at 11.25.0,
|
|
437
476
|
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.
|
|
438
|
-
As [audited on 2026-10-
|
|
439
|
-
npm `latest` and the highest confirmed stable release are 12.
|
|
440
|
-
`latest-12` and `next-12`. The highest stable v11 release, 11.28.
|
|
441
|
-
`
|
|
442
|
-
|
|
477
|
+
As [audited on 2026-10-29](docs/research/pnpm-settings-bump-2026-10-29.md),
|
|
478
|
+
npm `latest` and the highest confirmed stable release are 12.16.1, also on
|
|
479
|
+
`latest-12` and `next-12`. The highest stable v11 release, 11.28.9, is on
|
|
480
|
+
`latest-11` and `next-11`. GitHub marks both targets as non-prereleases.
|
|
481
|
+
The project is pinned to 12.16.1. These checks verify the
|
|
443
482
|
documented fixtures, not every setting in every release. Local and config
|
|
444
483
|
dependency `include` consumption is verified on 12.15.0, with 12.14.0 as the
|
|
445
484
|
preceding boundary. v10 has unit regressions and source review
|
|
446
|
-
through 10.34.
|
|
485
|
+
through 10.34.9; real v10 consumption remains unverified. The 12.16 fixtures
|
|
486
|
+
verify TLS certificate handling against a temporary HTTPS registry, dist-tag
|
|
487
|
+
selection, runtime suppression, required script enforcement, and frozen
|
|
488
|
+
installs. They use OpenSSL to create a disposable certificate.
|
|
489
|
+
Routing fixtures at 11.28.8, 11.28.9, 12.9.0, 12.10.0, 12.11.0, 12.11.1,
|
|
490
|
+
12.11.2, 12.15.0, and 12.16.1 verify prefix/scope conflicts, mixed registry
|
|
491
|
+
representations after conversion, and rejection before writes under all three
|
|
492
|
+
strategies. They compare native URL normalization across the 12.11.1 boundary.
|
|
447
493
|
The removed `pnpm install --resolution-only` CLI flag is outside this settings
|
|
448
494
|
migrator's scope; replace it with `pnpm peers check` in scripts before upgrading.
|
|
449
495
|
|
package/dist/cli.mjs
CHANGED
|
@@ -335,6 +335,7 @@ const PNPM_PROJECT_REFUSED_SETTINGS = [
|
|
|
335
335
|
"scope",
|
|
336
336
|
"selectedProjectsGraph",
|
|
337
337
|
"stateDir",
|
|
338
|
+
"symlinkedLockfileTargetDirs",
|
|
338
339
|
"tools",
|
|
339
340
|
"userConfig",
|
|
340
341
|
"userconfig",
|
|
@@ -635,6 +636,14 @@ const PNPM_V12_11_MINIMUM_VERSION = [
|
|
|
635
636
|
0
|
|
636
637
|
];
|
|
637
638
|
/**
|
|
639
|
+
* Release canonicalizing npm's HTTPS and protocol-relative registry aliases.
|
|
640
|
+
*/
|
|
641
|
+
const PNPM_V12_11_1_MINIMUM_VERSION = [
|
|
642
|
+
12,
|
|
643
|
+
11,
|
|
644
|
+
1
|
|
645
|
+
];
|
|
646
|
+
/**
|
|
638
647
|
* Release introducing remote cache configuration and missing peer types.
|
|
639
648
|
*/
|
|
640
649
|
const PNPM_V12_12_MINIMUM_VERSION = [
|
|
@@ -683,6 +692,32 @@ const PNPM_V12_15_MINIMUM_VERSION = [
|
|
|
683
692
|
0
|
|
684
693
|
];
|
|
685
694
|
/**
|
|
695
|
+
* Release introducing pnpr registry declarations in v11.
|
|
696
|
+
*/
|
|
697
|
+
const PNPM_V11_28_9_MINIMUM_VERSION = [
|
|
698
|
+
11,
|
|
699
|
+
28,
|
|
700
|
+
9
|
|
701
|
+
];
|
|
702
|
+
/**
|
|
703
|
+
* Release consuming TLS, runtime, script requirements, and dist-tag settings.
|
|
704
|
+
*/
|
|
705
|
+
const PNPM_V12_16_MINIMUM_VERSION = [
|
|
706
|
+
12,
|
|
707
|
+
16,
|
|
708
|
+
0
|
|
709
|
+
];
|
|
710
|
+
/**
|
|
711
|
+
* Workspace settings first consumed by pnpm v12 in 12.16.0.
|
|
712
|
+
*/
|
|
713
|
+
const PNPM_V12_16_WORKSPACE_SETTINGS = [
|
|
714
|
+
"localAddress",
|
|
715
|
+
"requiredScripts",
|
|
716
|
+
"runtime",
|
|
717
|
+
"strictSsl",
|
|
718
|
+
"tag"
|
|
719
|
+
];
|
|
720
|
+
/**
|
|
686
721
|
* Workspace settings first consumed by pnpm v12 in 12.11.0.
|
|
687
722
|
*/
|
|
688
723
|
const PNPM_V12_11_WORKSPACE_SETTINGS = [
|
|
@@ -708,6 +743,11 @@ const PNPM_V12_7_WORKSPACE_SETTINGS = [
|
|
|
708
743
|
* @see https://github.com/pnpm/pnpm/blob/v12.4.0/pnpm/crates/config/src/workspace_yaml.rs
|
|
709
744
|
*/
|
|
710
745
|
const PNPM_SETTINGS_CAPABILITIES = [
|
|
746
|
+
{
|
|
747
|
+
minimumVersion: PNPM_V12_16_MINIMUM_VERSION,
|
|
748
|
+
workspaceFields: PNPM_V12_16_WORKSPACE_SETTINGS,
|
|
749
|
+
taskFields: []
|
|
750
|
+
},
|
|
711
751
|
{
|
|
712
752
|
minimumVersion: PNPM_V11_28_7_MINIMUM_VERSION,
|
|
713
753
|
workspaceFields: [
|
|
@@ -817,7 +857,8 @@ const PNPM_V12_ONLY_WORKSPACE_SETTINGS = [
|
|
|
817
857
|
"globalShims"
|
|
818
858
|
];
|
|
819
859
|
/**
|
|
820
|
-
* Legacy fields absent from pnpm v12's workspace reader
|
|
860
|
+
* Legacy fields absent from pnpm v12's base workspace reader.
|
|
861
|
+
* Version capabilities restore fields consumed by later releases.
|
|
821
862
|
* Removed aliases with explicit migration replacements are kept separately.
|
|
822
863
|
*
|
|
823
864
|
* @see https://github.com/pnpm/pnpm/blob/v12.15.0/pnpm/crates/config/src/workspace_yaml/settings.rs
|
|
@@ -829,6 +870,7 @@ const PNPM_V12_IGNORED_WORKSPACE_SETTINGS = [
|
|
|
829
870
|
"auditIgnorePrune",
|
|
830
871
|
"autoConfirmAllPrompts",
|
|
831
872
|
"binLinks",
|
|
873
|
+
"ci",
|
|
832
874
|
"commitHooks",
|
|
833
875
|
"cpu",
|
|
834
876
|
"depth",
|
|
@@ -1304,6 +1346,52 @@ function isRegistryUrl(value) {
|
|
|
1304
1346
|
return isString(value) && URL.canParse(value.startsWith("//") ? `https:${value}` : value);
|
|
1305
1347
|
}
|
|
1306
1348
|
/**
|
|
1349
|
+
* Normalize a registry only for comparing scope routes, as the target does.
|
|
1350
|
+
* Prefix uniqueness deliberately uses the original declarations instead.
|
|
1351
|
+
*
|
|
1352
|
+
* @param registry - Registry URL already checked for valid syntax
|
|
1353
|
+
* @param target - Confirmed target controlling URL normalization
|
|
1354
|
+
*
|
|
1355
|
+
* @returns Registry identity used by the target's scope lookup
|
|
1356
|
+
*/
|
|
1357
|
+
function normalizeScopeRegistry(registry, target) {
|
|
1358
|
+
let normalized = registry;
|
|
1359
|
+
if (target.compatibility === "v11" && URL.canParse(registry)) normalized = new URL(registry).toString();
|
|
1360
|
+
else if (supportsMinimumVersion(target.version, PNPM_V12_11_1_MINIMUM_VERSION)) normalized = registry.replace(/^(?<protocol>https:\/\/|\/\/)registry\.npmjs\.com\/?$/u, "$<protocol>registry.npmjs.org/");
|
|
1361
|
+
return normalized.endsWith("/") ? normalized : `${normalized}/`;
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Detect duplicate prefixes and scope routes pointing at different registries.
|
|
1365
|
+
* Called during source selection and final workspace validation before writes.
|
|
1366
|
+
*
|
|
1367
|
+
* @param key - Canonical setting name
|
|
1368
|
+
* @param value - Untrusted registry map
|
|
1369
|
+
* @param target - Release whose declaration routing rules apply
|
|
1370
|
+
*
|
|
1371
|
+
* @returns Whether declarations conflict with another route in the same map
|
|
1372
|
+
*/
|
|
1373
|
+
function hasConflictingRegistryRoutes(key, value, target) {
|
|
1374
|
+
if (key !== "registries" || target.compatibility === "v10" || !isPlainObject(value)) return false;
|
|
1375
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
1376
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
1377
|
+
return Object.entries(value).some(([registry, declaration]) => {
|
|
1378
|
+
if (!isPlainObject(declaration)) return false;
|
|
1379
|
+
const { prefix } = declaration;
|
|
1380
|
+
if (isString(prefix)) {
|
|
1381
|
+
if (prefixes.has(prefix)) return true;
|
|
1382
|
+
prefixes.add(prefix);
|
|
1383
|
+
}
|
|
1384
|
+
if (!isArray(declaration.scopes)) return false;
|
|
1385
|
+
const normalized = normalizeScopeRegistry(registry, target);
|
|
1386
|
+
return declaration.scopes.filter(isString).some((scope) => {
|
|
1387
|
+
const existing = scopes.get(scope);
|
|
1388
|
+
if (existing !== void 0 && existing !== normalized) return true;
|
|
1389
|
+
scopes.set(scope, normalized);
|
|
1390
|
+
return false;
|
|
1391
|
+
});
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1307
1395
|
* Validate locations in legacy registry maps and URL-keyed declarations.
|
|
1308
1396
|
*
|
|
1309
1397
|
* @param key - Canonical workspace setting name
|
|
@@ -1317,6 +1405,73 @@ function hasInvalidRegistryLocations(key, value) {
|
|
|
1317
1405
|
if (key === "namedRegistries") return Object.values(value).some((url) => !isRegistryUrl(url));
|
|
1318
1406
|
return Object.entries(value).some(([url, entry]) => isString(entry) ? !isRegistryUrl(entry) : !isRegistryUrl(url));
|
|
1319
1407
|
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Check the declared type of a non-null registry option.
|
|
1410
|
+
*
|
|
1411
|
+
* @param field - Registry declaration field name
|
|
1412
|
+
* @param value - Untrusted option value
|
|
1413
|
+
* @param target - Release controlling registry server types
|
|
1414
|
+
*
|
|
1415
|
+
* @returns Whether a nested option is invalid for pnpm 12.9
|
|
1416
|
+
*/
|
|
1417
|
+
function hasInvalidRegistryOptionValue(field, value, target) {
|
|
1418
|
+
switch (field) {
|
|
1419
|
+
case "networkConcurrency": return !isNumber(value) || !Number.isSafeInteger(value) || value <= 0;
|
|
1420
|
+
case "ecosystem": return !isString(value) || ![
|
|
1421
|
+
"npm",
|
|
1422
|
+
"cargo",
|
|
1423
|
+
"pypi"
|
|
1424
|
+
].includes(value);
|
|
1425
|
+
case "packages": return !isArray(value) || !value.every(isString);
|
|
1426
|
+
case "scopes": return !isArray(value) || value.some((scope) => !isString(scope) || !scope.startsWith("@"));
|
|
1427
|
+
case "prefix": return !isString(value);
|
|
1428
|
+
case "serverType": return !isString(value) || ![
|
|
1429
|
+
"npm",
|
|
1430
|
+
"artifactory",
|
|
1431
|
+
...supportsMinimumVersion(target.version, PNPM_V12_15_MINIMUM_VERSION) || supportsMinimumVersion(target.version, PNPM_V11_28_9_MINIMUM_VERSION) ? ["pnpr"] : []
|
|
1432
|
+
].includes(value);
|
|
1433
|
+
case "supportsTimeField": return !isBoolean(value);
|
|
1434
|
+
default: return true;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Validate every field in a registry declaration for the selected release.
|
|
1439
|
+
*
|
|
1440
|
+
* @param declaration - Registry options with or without versioned fields
|
|
1441
|
+
* @param target - Release controlling registry server types
|
|
1442
|
+
*
|
|
1443
|
+
* @returns Whether a nested option is unknown or invalid for the target
|
|
1444
|
+
*/
|
|
1445
|
+
function hasInvalidRegistryDeclaration(declaration, target) {
|
|
1446
|
+
const ecosystem = Reflect.get(declaration, "ecosystem") ?? "npm";
|
|
1447
|
+
return Object.entries(declaration).some(([field, value]) => {
|
|
1448
|
+
if (!PNPM_V12_REGISTRY_DECLARATION_FIELDS.includes(field)) return true;
|
|
1449
|
+
if (value === null) return false;
|
|
1450
|
+
if (field === "packages" && ecosystem !== "pypi") return true;
|
|
1451
|
+
if ([
|
|
1452
|
+
"scopes",
|
|
1453
|
+
"prefix",
|
|
1454
|
+
"serverType",
|
|
1455
|
+
"supportsTimeField"
|
|
1456
|
+
].includes(field) && ecosystem !== "npm") return true;
|
|
1457
|
+
return hasInvalidRegistryOptionValue(field, value, target);
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Validate registry representation before release-specific declaration fields.
|
|
1462
|
+
* Pure legacy scope maps must use scope keys rather than registry URLs.
|
|
1463
|
+
*
|
|
1464
|
+
* @param value - Registry map already checked for valid locations
|
|
1465
|
+
* @param target - Release controlling declaration fields and enum values
|
|
1466
|
+
*
|
|
1467
|
+
* @returns Whether any entry prevents migration of the complete map
|
|
1468
|
+
*/
|
|
1469
|
+
function hasIncompatibleRegistryValue(value, target) {
|
|
1470
|
+
const entries = Object.values(value);
|
|
1471
|
+
if (entries.every(isString)) return Object.keys(value).some((key) => key.includes("://") || key.startsWith("//"));
|
|
1472
|
+
if (!entries.every(isPlainObject)) return true;
|
|
1473
|
+
return entries.some((entry) => Object.hasOwn(entry, "networkConcurrency") && !supportsMinimumVersion(target.version, PNPM_V12_9_MINIMUM_VERSION) || hasInvalidRegistryDeclaration(entry, target));
|
|
1474
|
+
}
|
|
1320
1475
|
//#endregion
|
|
1321
1476
|
//#region src/features/settings/permissions.ts
|
|
1322
1477
|
/**
|
|
@@ -1630,58 +1785,6 @@ function hasInvalidNodeLinkerOptions(value) {
|
|
|
1630
1785
|
return Object.hasOwn(value, "excluded") && (!isArray(excluded) || !excluded.every(isString));
|
|
1631
1786
|
}
|
|
1632
1787
|
/**
|
|
1633
|
-
* Check the declared type of a non-null registry option.
|
|
1634
|
-
*
|
|
1635
|
-
* @param field - Registry declaration field name
|
|
1636
|
-
* @param value - Untrusted option value
|
|
1637
|
-
* @param target - Release controlling registry server types
|
|
1638
|
-
*
|
|
1639
|
-
* @returns Whether a nested option is invalid for pnpm 12.9
|
|
1640
|
-
*/
|
|
1641
|
-
function hasInvalidRegistryOptionValue(field, value, target) {
|
|
1642
|
-
switch (field) {
|
|
1643
|
-
case "networkConcurrency": return !isNumber(value) || !Number.isSafeInteger(value) || value <= 0;
|
|
1644
|
-
case "ecosystem": return !isString(value) || ![
|
|
1645
|
-
"npm",
|
|
1646
|
-
"cargo",
|
|
1647
|
-
"pypi"
|
|
1648
|
-
].includes(value);
|
|
1649
|
-
case "packages": return !isArray(value) || !value.every(isString);
|
|
1650
|
-
case "scopes": return !isArray(value) || value.some((scope) => !isString(scope) || !scope.startsWith("@"));
|
|
1651
|
-
case "prefix": return !isString(value);
|
|
1652
|
-
case "serverType": return !isString(value) || ![
|
|
1653
|
-
"npm",
|
|
1654
|
-
"artifactory",
|
|
1655
|
-
...supportsMinimumVersion(target.version, PNPM_V12_15_MINIMUM_VERSION) ? ["pnpr"] : []
|
|
1656
|
-
].includes(value);
|
|
1657
|
-
case "supportsTimeField": return !isBoolean(value);
|
|
1658
|
-
default: return true;
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
/**
|
|
1662
|
-
* Validate a registry declaration that opts into per-registry concurrency.
|
|
1663
|
-
*
|
|
1664
|
-
* @param declaration - Registry options containing versioned fields
|
|
1665
|
-
* @param target - Release controlling registry server types
|
|
1666
|
-
*
|
|
1667
|
-
* @returns Whether a nested option is invalid for pnpm 12.9
|
|
1668
|
-
*/
|
|
1669
|
-
function hasInvalidRegistryConcurrencyDeclaration(declaration, target) {
|
|
1670
|
-
const ecosystem = Reflect.get(declaration, "ecosystem") ?? "npm";
|
|
1671
|
-
return Object.entries(declaration).some(([field, value]) => {
|
|
1672
|
-
if (!PNPM_V12_REGISTRY_DECLARATION_FIELDS.includes(field)) return true;
|
|
1673
|
-
if (value === null) return false;
|
|
1674
|
-
if (field === "packages" && ecosystem !== "pypi") return true;
|
|
1675
|
-
if ([
|
|
1676
|
-
"scopes",
|
|
1677
|
-
"prefix",
|
|
1678
|
-
"serverType",
|
|
1679
|
-
"supportsTimeField"
|
|
1680
|
-
].includes(field) && ecosystem !== "npm") return true;
|
|
1681
|
-
return hasInvalidRegistryOptionValue(field, value, target);
|
|
1682
|
-
});
|
|
1683
|
-
}
|
|
1684
|
-
/**
|
|
1685
1788
|
* Validate scalar alternatives of the pnpm 12.10 linker and lockfile unions.
|
|
1686
1789
|
*
|
|
1687
1790
|
* @param key - Canonical setting name
|
|
@@ -1737,14 +1840,7 @@ function hasIncompatibleV12SettingValue(key, value, target) {
|
|
|
1737
1840
|
if (!value || typeof value !== "object") return false;
|
|
1738
1841
|
if (key === "nodeLinker") return !supportsV12_10 || hasInvalidNodeLinkerOptions(value);
|
|
1739
1842
|
if (key === "lockfile") return hasInvalidLockfileOptions(value, target);
|
|
1740
|
-
if (key === "registries")
|
|
1741
|
-
const entries = Object.values(value);
|
|
1742
|
-
return entries.some((entry) => {
|
|
1743
|
-
if (!entry || typeof entry !== "object" || !Object.hasOwn(entry, "networkConcurrency") && !(Object.hasOwn(entry, "serverType") && (Reflect.get(entry, "serverType") === "pnpr" || supportsMinimumVersion(target.version, PNPM_V12_15_MINIMUM_VERSION)))) return false;
|
|
1744
|
-
if (Object.hasOwn(entry, "networkConcurrency") && !supportsMinimumVersion(target.version, PNPM_V12_9_MINIMUM_VERSION)) return true;
|
|
1745
|
-
return entries.some(isString) || hasInvalidRegistryConcurrencyDeclaration(entry, target);
|
|
1746
|
-
});
|
|
1747
|
-
}
|
|
1843
|
+
if (key === "registries") return hasIncompatibleRegistryValue(value, target);
|
|
1748
1844
|
return false;
|
|
1749
1845
|
}
|
|
1750
1846
|
//#endregion
|
|
@@ -1793,6 +1889,23 @@ function hasInvalidV11PatchValue(key, value, target) {
|
|
|
1793
1889
|
return false;
|
|
1794
1890
|
}
|
|
1795
1891
|
/**
|
|
1892
|
+
* Validate the fields newly consumed or ignored by pnpm 12.16.
|
|
1893
|
+
*
|
|
1894
|
+
* @param key - Canonical setting name
|
|
1895
|
+
* @param value - Untrusted source value
|
|
1896
|
+
* @param target - Release controlling the workspace reader
|
|
1897
|
+
*
|
|
1898
|
+
* @returns Whether the setting cannot be represented for this target
|
|
1899
|
+
*/
|
|
1900
|
+
function hasInvalidV12_16Value(key, value, target) {
|
|
1901
|
+
if (!supportsMinimumVersion(target.version, PNPM_V12_16_MINIMUM_VERSION)) return false;
|
|
1902
|
+
if (key === "scriptsPrependNodePath") return true;
|
|
1903
|
+
if (["strictSsl", "runtime"].includes(key)) return value !== null && !isBoolean(value);
|
|
1904
|
+
if (["localAddress", "tag"].includes(key)) return value !== null && !isString(value);
|
|
1905
|
+
if (key === "requiredScripts") return value !== null && (!isArray(value) || !value.every(isString));
|
|
1906
|
+
return false;
|
|
1907
|
+
}
|
|
1908
|
+
/**
|
|
1796
1909
|
* Validate scalar settings introduced or first consumed in pnpm 12.6.
|
|
1797
1910
|
*
|
|
1798
1911
|
* @param key - Canonical setting name
|
|
@@ -1838,6 +1951,7 @@ function hasInvalidScalarValue(key, value, target) {
|
|
|
1838
1951
|
* @returns Whether the value cannot be used by the target release
|
|
1839
1952
|
*/
|
|
1840
1953
|
function hasInvalidReleaseValue(key, value, target) {
|
|
1954
|
+
if (hasInvalidV12_16Value(key, value, target)) return true;
|
|
1841
1955
|
if (key === "globalShims" && supportsMinimumVersion(target.version, PNPM_V12_8_2_MINIMUM_VERSION)) return true;
|
|
1842
1956
|
if (key === "userAgent" && isString(value) && value.includes("${") && (supportsMinimumVersion(target.version, PNPM_V11_28_MINIMUM_VERSION) || supportsMinimumVersion(target.version, PNPM_V12_7_MINIMUM_VERSION))) return true;
|
|
1843
1957
|
if (key === "packages" && value !== null && value !== void 0 && supportsMinimumVersion(target.version, PNPM_V11_28_1_MINIMUM_VERSION)) return !isArray(value) || value.some((entry) => !isString(entry) || !entry);
|
|
@@ -2025,7 +2139,7 @@ function resolveSettingIssue({ allowedFields, target, key, npmrc, value }) {
|
|
|
2025
2139
|
if (compatibility !== "v10" && PROJECT_REFUSED_SETTINGS.has(key)) return "refused";
|
|
2026
2140
|
if (REGISTRY_SETTINGS.has(key) && containsUnsafeRegistryValue(value)) return "unsafe";
|
|
2027
2141
|
if (PROXY_SETTINGS.has(key) && isString(value) && value.includes("${")) return "unsafe";
|
|
2028
|
-
if (hasInvalidRegistryLocations(key, value) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
2142
|
+
if (hasInvalidRegistryLocations(key, value) || hasConflictingRegistryRoutes(key, value, target) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
2029
2143
|
if (key === "tasks" && hasUnsupportedTaskFields(value, taskSettings)) return "incompatible";
|
|
2030
2144
|
if (!workspaceSettings.has(key)) return isSettingFromAnotherVersion(key, compatibility) ? "incompatible" : "unknown";
|
|
2031
2145
|
if (allowedFields && !allowedFields.has(key)) return "unsupported";
|
|
@@ -4288,6 +4402,7 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
4288
4402
|
yarnResolutions: sources.packageJson.yarnResolutions
|
|
4289
4403
|
})) return result;
|
|
4290
4404
|
const pnpmWorkspaceResult = mergeIncludedWorkspace(workspace, incomingSettings, options.strategy);
|
|
4405
|
+
assertCompatibleWorkspaceSettings(pnpmWorkspaceResult, target);
|
|
4291
4406
|
const workspaceFiles = workspace.include === void 0 ? [] : renderIncludedWorkspace(workspace, pnpmWorkspaceResult, {
|
|
4292
4407
|
...options,
|
|
4293
4408
|
target
|
|
@@ -4368,7 +4483,7 @@ const bold = getColor("bold");
|
|
|
4368
4483
|
//#endregion
|
|
4369
4484
|
//#region package.json
|
|
4370
4485
|
var name = "pnpm-settings-migrator";
|
|
4371
|
-
var version = "0.
|
|
4486
|
+
var version = "0.20.0";
|
|
4372
4487
|
//#endregion
|
|
4373
4488
|
//#region src/cli/options.ts
|
|
4374
4489
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -193,7 +193,7 @@ interface PnpmCargoSettings {
|
|
|
193
193
|
*/
|
|
194
194
|
interface PnpmRegistryDeclaration extends Omit<RegistryDeclaration, 'serverType'> {
|
|
195
195
|
/**
|
|
196
|
-
* Registry protocol; pnpr
|
|
196
|
+
* Registry protocol; pnpr requires pnpm 11.28.9+ or 12.15+.
|
|
197
197
|
*/
|
|
198
198
|
serverType?: 'npm' | 'artifactory' | 'pnpr' | null;
|
|
199
199
|
/**
|
|
@@ -268,6 +268,28 @@ interface PnpmSkillsSettings {
|
|
|
268
268
|
* Settings supported by pnpm v12, subject to the resolved version capabilities.
|
|
269
269
|
*/
|
|
270
270
|
interface PnpmSettingsV12 {
|
|
271
|
+
/**
|
|
272
|
+
* Verify registry TLS certificates (consumed by v12 from 12.16).
|
|
273
|
+
* Null leaves the upstream default effective.
|
|
274
|
+
*/
|
|
275
|
+
strictSsl?: boolean | null;
|
|
276
|
+
/**
|
|
277
|
+
* Source IP for registry connections (consumed by v12 from 12.16).
|
|
278
|
+
* Invalid addresses are treated as unset by pnpm.
|
|
279
|
+
*/
|
|
280
|
+
localAddress?: string | null;
|
|
281
|
+
/**
|
|
282
|
+
* Install declared runtimes unless false (consumed by v12 from 12.16).
|
|
283
|
+
*/
|
|
284
|
+
runtime?: boolean | null;
|
|
285
|
+
/**
|
|
286
|
+
* Scripts every selected workspace project must define (v12.16+).
|
|
287
|
+
*/
|
|
288
|
+
requiredScripts?: string[] | null;
|
|
289
|
+
/**
|
|
290
|
+
* Default distribution tag used when adding packages (v12.16+).
|
|
291
|
+
*/
|
|
292
|
+
tag?: string | null;
|
|
271
293
|
/**
|
|
272
294
|
* Settings delegated to local YAML files or config dependencies (12.15+).
|
|
273
295
|
* Wildcards are allowed only for local files.
|
|
@@ -648,7 +670,7 @@ interface PnpmSettingsV11 {
|
|
|
648
670
|
/**
|
|
649
671
|
* `pnpm-workspace.yaml` types.
|
|
650
672
|
*/
|
|
651
|
-
type PnpmWorkspace = Omit<PnpmSettings, 'autoInstallPeersFromHighestMatch' | 'externalDependencies' | 'httpProxy' | 'httpsProxy' | 'lockfile' | 'nodeLinker' | 'provenance' | 'registries' | 'sideEffectsCache' | 'supportedArchitectures' | 'tasks'> & PnpmSettingsDeprecated & PnpmSettingsV11 & PnpmSettingsV12 & PnpmWorkspaceLegacy;
|
|
673
|
+
type PnpmWorkspace = Omit<PnpmSettings, 'autoInstallPeersFromHighestMatch' | 'externalDependencies' | 'httpProxy' | 'httpsProxy' | 'lockfile' | 'nodeLinker' | 'provenance' | 'registries' | 'requiredScripts' | 'sideEffectsCache' | 'supportedArchitectures' | 'tasks'> & PnpmSettingsDeprecated & PnpmSettingsV11 & PnpmSettingsV12 & PnpmWorkspaceLegacy;
|
|
652
674
|
//#endregion
|
|
653
675
|
//#region src/types/package-json.d.ts
|
|
654
676
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -333,6 +333,7 @@ const PNPM_PROJECT_REFUSED_SETTINGS = [
|
|
|
333
333
|
"scope",
|
|
334
334
|
"selectedProjectsGraph",
|
|
335
335
|
"stateDir",
|
|
336
|
+
"symlinkedLockfileTargetDirs",
|
|
336
337
|
"tools",
|
|
337
338
|
"userConfig",
|
|
338
339
|
"userconfig",
|
|
@@ -633,6 +634,14 @@ const PNPM_V12_11_MINIMUM_VERSION = [
|
|
|
633
634
|
0
|
|
634
635
|
];
|
|
635
636
|
/**
|
|
637
|
+
* Release canonicalizing npm's HTTPS and protocol-relative registry aliases.
|
|
638
|
+
*/
|
|
639
|
+
const PNPM_V12_11_1_MINIMUM_VERSION = [
|
|
640
|
+
12,
|
|
641
|
+
11,
|
|
642
|
+
1
|
|
643
|
+
];
|
|
644
|
+
/**
|
|
636
645
|
* Release introducing remote cache configuration and missing peer types.
|
|
637
646
|
*/
|
|
638
647
|
const PNPM_V12_12_MINIMUM_VERSION = [
|
|
@@ -681,6 +690,32 @@ const PNPM_V12_15_MINIMUM_VERSION = [
|
|
|
681
690
|
0
|
|
682
691
|
];
|
|
683
692
|
/**
|
|
693
|
+
* Release introducing pnpr registry declarations in v11.
|
|
694
|
+
*/
|
|
695
|
+
const PNPM_V11_28_9_MINIMUM_VERSION = [
|
|
696
|
+
11,
|
|
697
|
+
28,
|
|
698
|
+
9
|
|
699
|
+
];
|
|
700
|
+
/**
|
|
701
|
+
* Release consuming TLS, runtime, script requirements, and dist-tag settings.
|
|
702
|
+
*/
|
|
703
|
+
const PNPM_V12_16_MINIMUM_VERSION = [
|
|
704
|
+
12,
|
|
705
|
+
16,
|
|
706
|
+
0
|
|
707
|
+
];
|
|
708
|
+
/**
|
|
709
|
+
* Workspace settings first consumed by pnpm v12 in 12.16.0.
|
|
710
|
+
*/
|
|
711
|
+
const PNPM_V12_16_WORKSPACE_SETTINGS = [
|
|
712
|
+
"localAddress",
|
|
713
|
+
"requiredScripts",
|
|
714
|
+
"runtime",
|
|
715
|
+
"strictSsl",
|
|
716
|
+
"tag"
|
|
717
|
+
];
|
|
718
|
+
/**
|
|
684
719
|
* Workspace settings first consumed by pnpm v12 in 12.11.0.
|
|
685
720
|
*/
|
|
686
721
|
const PNPM_V12_11_WORKSPACE_SETTINGS = [
|
|
@@ -706,6 +741,11 @@ const PNPM_V12_7_WORKSPACE_SETTINGS = [
|
|
|
706
741
|
* @see https://github.com/pnpm/pnpm/blob/v12.4.0/pnpm/crates/config/src/workspace_yaml.rs
|
|
707
742
|
*/
|
|
708
743
|
const PNPM_SETTINGS_CAPABILITIES = [
|
|
744
|
+
{
|
|
745
|
+
minimumVersion: PNPM_V12_16_MINIMUM_VERSION,
|
|
746
|
+
workspaceFields: PNPM_V12_16_WORKSPACE_SETTINGS,
|
|
747
|
+
taskFields: []
|
|
748
|
+
},
|
|
709
749
|
{
|
|
710
750
|
minimumVersion: PNPM_V11_28_7_MINIMUM_VERSION,
|
|
711
751
|
workspaceFields: [
|
|
@@ -815,7 +855,8 @@ const PNPM_V12_ONLY_WORKSPACE_SETTINGS = [
|
|
|
815
855
|
"globalShims"
|
|
816
856
|
];
|
|
817
857
|
/**
|
|
818
|
-
* Legacy fields absent from pnpm v12's workspace reader
|
|
858
|
+
* Legacy fields absent from pnpm v12's base workspace reader.
|
|
859
|
+
* Version capabilities restore fields consumed by later releases.
|
|
819
860
|
* Removed aliases with explicit migration replacements are kept separately.
|
|
820
861
|
*
|
|
821
862
|
* @see https://github.com/pnpm/pnpm/blob/v12.15.0/pnpm/crates/config/src/workspace_yaml/settings.rs
|
|
@@ -827,6 +868,7 @@ const PNPM_V12_IGNORED_WORKSPACE_SETTINGS = [
|
|
|
827
868
|
"auditIgnorePrune",
|
|
828
869
|
"autoConfirmAllPrompts",
|
|
829
870
|
"binLinks",
|
|
871
|
+
"ci",
|
|
830
872
|
"commitHooks",
|
|
831
873
|
"cpu",
|
|
832
874
|
"depth",
|
|
@@ -1297,6 +1339,52 @@ function isRegistryUrl(value) {
|
|
|
1297
1339
|
return isString(value) && URL.canParse(value.startsWith("//") ? `https:${value}` : value);
|
|
1298
1340
|
}
|
|
1299
1341
|
/**
|
|
1342
|
+
* Normalize a registry only for comparing scope routes, as the target does.
|
|
1343
|
+
* Prefix uniqueness deliberately uses the original declarations instead.
|
|
1344
|
+
*
|
|
1345
|
+
* @param registry - Registry URL already checked for valid syntax
|
|
1346
|
+
* @param target - Confirmed target controlling URL normalization
|
|
1347
|
+
*
|
|
1348
|
+
* @returns Registry identity used by the target's scope lookup
|
|
1349
|
+
*/
|
|
1350
|
+
function normalizeScopeRegistry(registry, target) {
|
|
1351
|
+
let normalized = registry;
|
|
1352
|
+
if (target.compatibility === "v11" && URL.canParse(registry)) normalized = new URL(registry).toString();
|
|
1353
|
+
else if (supportsMinimumVersion(target.version, PNPM_V12_11_1_MINIMUM_VERSION)) normalized = registry.replace(/^(?<protocol>https:\/\/|\/\/)registry\.npmjs\.com\/?$/u, "$<protocol>registry.npmjs.org/");
|
|
1354
|
+
return normalized.endsWith("/") ? normalized : `${normalized}/`;
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Detect duplicate prefixes and scope routes pointing at different registries.
|
|
1358
|
+
* Called during source selection and final workspace validation before writes.
|
|
1359
|
+
*
|
|
1360
|
+
* @param key - Canonical setting name
|
|
1361
|
+
* @param value - Untrusted registry map
|
|
1362
|
+
* @param target - Release whose declaration routing rules apply
|
|
1363
|
+
*
|
|
1364
|
+
* @returns Whether declarations conflict with another route in the same map
|
|
1365
|
+
*/
|
|
1366
|
+
function hasConflictingRegistryRoutes(key, value, target) {
|
|
1367
|
+
if (key !== "registries" || target.compatibility === "v10" || !isPlainObject(value)) return false;
|
|
1368
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
1369
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
1370
|
+
return Object.entries(value).some(([registry, declaration]) => {
|
|
1371
|
+
if (!isPlainObject(declaration)) return false;
|
|
1372
|
+
const { prefix } = declaration;
|
|
1373
|
+
if (isString(prefix)) {
|
|
1374
|
+
if (prefixes.has(prefix)) return true;
|
|
1375
|
+
prefixes.add(prefix);
|
|
1376
|
+
}
|
|
1377
|
+
if (!isArray(declaration.scopes)) return false;
|
|
1378
|
+
const normalized = normalizeScopeRegistry(registry, target);
|
|
1379
|
+
return declaration.scopes.filter(isString).some((scope) => {
|
|
1380
|
+
const existing = scopes.get(scope);
|
|
1381
|
+
if (existing !== void 0 && existing !== normalized) return true;
|
|
1382
|
+
scopes.set(scope, normalized);
|
|
1383
|
+
return false;
|
|
1384
|
+
});
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1300
1388
|
* Validate locations in legacy registry maps and URL-keyed declarations.
|
|
1301
1389
|
*
|
|
1302
1390
|
* @param key - Canonical workspace setting name
|
|
@@ -1310,6 +1398,73 @@ function hasInvalidRegistryLocations(key, value) {
|
|
|
1310
1398
|
if (key === "namedRegistries") return Object.values(value).some((url) => !isRegistryUrl(url));
|
|
1311
1399
|
return Object.entries(value).some(([url, entry]) => isString(entry) ? !isRegistryUrl(entry) : !isRegistryUrl(url));
|
|
1312
1400
|
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Check the declared type of a non-null registry option.
|
|
1403
|
+
*
|
|
1404
|
+
* @param field - Registry declaration field name
|
|
1405
|
+
* @param value - Untrusted option value
|
|
1406
|
+
* @param target - Release controlling registry server types
|
|
1407
|
+
*
|
|
1408
|
+
* @returns Whether a nested option is invalid for pnpm 12.9
|
|
1409
|
+
*/
|
|
1410
|
+
function hasInvalidRegistryOptionValue(field, value, target) {
|
|
1411
|
+
switch (field) {
|
|
1412
|
+
case "networkConcurrency": return !isNumber(value) || !Number.isSafeInteger(value) || value <= 0;
|
|
1413
|
+
case "ecosystem": return !isString(value) || ![
|
|
1414
|
+
"npm",
|
|
1415
|
+
"cargo",
|
|
1416
|
+
"pypi"
|
|
1417
|
+
].includes(value);
|
|
1418
|
+
case "packages": return !isArray(value) || !value.every(isString);
|
|
1419
|
+
case "scopes": return !isArray(value) || value.some((scope) => !isString(scope) || !scope.startsWith("@"));
|
|
1420
|
+
case "prefix": return !isString(value);
|
|
1421
|
+
case "serverType": return !isString(value) || ![
|
|
1422
|
+
"npm",
|
|
1423
|
+
"artifactory",
|
|
1424
|
+
...supportsMinimumVersion(target.version, PNPM_V12_15_MINIMUM_VERSION) || supportsMinimumVersion(target.version, PNPM_V11_28_9_MINIMUM_VERSION) ? ["pnpr"] : []
|
|
1425
|
+
].includes(value);
|
|
1426
|
+
case "supportsTimeField": return !isBoolean(value);
|
|
1427
|
+
default: return true;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Validate every field in a registry declaration for the selected release.
|
|
1432
|
+
*
|
|
1433
|
+
* @param declaration - Registry options with or without versioned fields
|
|
1434
|
+
* @param target - Release controlling registry server types
|
|
1435
|
+
*
|
|
1436
|
+
* @returns Whether a nested option is unknown or invalid for the target
|
|
1437
|
+
*/
|
|
1438
|
+
function hasInvalidRegistryDeclaration(declaration, target) {
|
|
1439
|
+
const ecosystem = Reflect.get(declaration, "ecosystem") ?? "npm";
|
|
1440
|
+
return Object.entries(declaration).some(([field, value]) => {
|
|
1441
|
+
if (!PNPM_V12_REGISTRY_DECLARATION_FIELDS.includes(field)) return true;
|
|
1442
|
+
if (value === null) return false;
|
|
1443
|
+
if (field === "packages" && ecosystem !== "pypi") return true;
|
|
1444
|
+
if ([
|
|
1445
|
+
"scopes",
|
|
1446
|
+
"prefix",
|
|
1447
|
+
"serverType",
|
|
1448
|
+
"supportsTimeField"
|
|
1449
|
+
].includes(field) && ecosystem !== "npm") return true;
|
|
1450
|
+
return hasInvalidRegistryOptionValue(field, value, target);
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Validate registry representation before release-specific declaration fields.
|
|
1455
|
+
* Pure legacy scope maps must use scope keys rather than registry URLs.
|
|
1456
|
+
*
|
|
1457
|
+
* @param value - Registry map already checked for valid locations
|
|
1458
|
+
* @param target - Release controlling declaration fields and enum values
|
|
1459
|
+
*
|
|
1460
|
+
* @returns Whether any entry prevents migration of the complete map
|
|
1461
|
+
*/
|
|
1462
|
+
function hasIncompatibleRegistryValue(value, target) {
|
|
1463
|
+
const entries = Object.values(value);
|
|
1464
|
+
if (entries.every(isString)) return Object.keys(value).some((key) => key.includes("://") || key.startsWith("//"));
|
|
1465
|
+
if (!entries.every(isPlainObject)) return true;
|
|
1466
|
+
return entries.some((entry) => Object.hasOwn(entry, "networkConcurrency") && !supportsMinimumVersion(target.version, PNPM_V12_9_MINIMUM_VERSION) || hasInvalidRegistryDeclaration(entry, target));
|
|
1467
|
+
}
|
|
1313
1468
|
//#endregion
|
|
1314
1469
|
//#region src/features/settings/permissions.ts
|
|
1315
1470
|
/**
|
|
@@ -1623,58 +1778,6 @@ function hasInvalidNodeLinkerOptions(value) {
|
|
|
1623
1778
|
return Object.hasOwn(value, "excluded") && (!isArray(excluded) || !excluded.every(isString));
|
|
1624
1779
|
}
|
|
1625
1780
|
/**
|
|
1626
|
-
* Check the declared type of a non-null registry option.
|
|
1627
|
-
*
|
|
1628
|
-
* @param field - Registry declaration field name
|
|
1629
|
-
* @param value - Untrusted option value
|
|
1630
|
-
* @param target - Release controlling registry server types
|
|
1631
|
-
*
|
|
1632
|
-
* @returns Whether a nested option is invalid for pnpm 12.9
|
|
1633
|
-
*/
|
|
1634
|
-
function hasInvalidRegistryOptionValue(field, value, target) {
|
|
1635
|
-
switch (field) {
|
|
1636
|
-
case "networkConcurrency": return !isNumber(value) || !Number.isSafeInteger(value) || value <= 0;
|
|
1637
|
-
case "ecosystem": return !isString(value) || ![
|
|
1638
|
-
"npm",
|
|
1639
|
-
"cargo",
|
|
1640
|
-
"pypi"
|
|
1641
|
-
].includes(value);
|
|
1642
|
-
case "packages": return !isArray(value) || !value.every(isString);
|
|
1643
|
-
case "scopes": return !isArray(value) || value.some((scope) => !isString(scope) || !scope.startsWith("@"));
|
|
1644
|
-
case "prefix": return !isString(value);
|
|
1645
|
-
case "serverType": return !isString(value) || ![
|
|
1646
|
-
"npm",
|
|
1647
|
-
"artifactory",
|
|
1648
|
-
...supportsMinimumVersion(target.version, PNPM_V12_15_MINIMUM_VERSION) ? ["pnpr"] : []
|
|
1649
|
-
].includes(value);
|
|
1650
|
-
case "supportsTimeField": return !isBoolean(value);
|
|
1651
|
-
default: return true;
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
/**
|
|
1655
|
-
* Validate a registry declaration that opts into per-registry concurrency.
|
|
1656
|
-
*
|
|
1657
|
-
* @param declaration - Registry options containing versioned fields
|
|
1658
|
-
* @param target - Release controlling registry server types
|
|
1659
|
-
*
|
|
1660
|
-
* @returns Whether a nested option is invalid for pnpm 12.9
|
|
1661
|
-
*/
|
|
1662
|
-
function hasInvalidRegistryConcurrencyDeclaration(declaration, target) {
|
|
1663
|
-
const ecosystem = Reflect.get(declaration, "ecosystem") ?? "npm";
|
|
1664
|
-
return Object.entries(declaration).some(([field, value]) => {
|
|
1665
|
-
if (!PNPM_V12_REGISTRY_DECLARATION_FIELDS.includes(field)) return true;
|
|
1666
|
-
if (value === null) return false;
|
|
1667
|
-
if (field === "packages" && ecosystem !== "pypi") return true;
|
|
1668
|
-
if ([
|
|
1669
|
-
"scopes",
|
|
1670
|
-
"prefix",
|
|
1671
|
-
"serverType",
|
|
1672
|
-
"supportsTimeField"
|
|
1673
|
-
].includes(field) && ecosystem !== "npm") return true;
|
|
1674
|
-
return hasInvalidRegistryOptionValue(field, value, target);
|
|
1675
|
-
});
|
|
1676
|
-
}
|
|
1677
|
-
/**
|
|
1678
1781
|
* Validate scalar alternatives of the pnpm 12.10 linker and lockfile unions.
|
|
1679
1782
|
*
|
|
1680
1783
|
* @param key - Canonical setting name
|
|
@@ -1730,14 +1833,7 @@ function hasIncompatibleV12SettingValue(key, value, target) {
|
|
|
1730
1833
|
if (!value || typeof value !== "object") return false;
|
|
1731
1834
|
if (key === "nodeLinker") return !supportsV12_10 || hasInvalidNodeLinkerOptions(value);
|
|
1732
1835
|
if (key === "lockfile") return hasInvalidLockfileOptions(value, target);
|
|
1733
|
-
if (key === "registries")
|
|
1734
|
-
const entries = Object.values(value);
|
|
1735
|
-
return entries.some((entry) => {
|
|
1736
|
-
if (!entry || typeof entry !== "object" || !Object.hasOwn(entry, "networkConcurrency") && !(Object.hasOwn(entry, "serverType") && (Reflect.get(entry, "serverType") === "pnpr" || supportsMinimumVersion(target.version, PNPM_V12_15_MINIMUM_VERSION)))) return false;
|
|
1737
|
-
if (Object.hasOwn(entry, "networkConcurrency") && !supportsMinimumVersion(target.version, PNPM_V12_9_MINIMUM_VERSION)) return true;
|
|
1738
|
-
return entries.some(isString) || hasInvalidRegistryConcurrencyDeclaration(entry, target);
|
|
1739
|
-
});
|
|
1740
|
-
}
|
|
1836
|
+
if (key === "registries") return hasIncompatibleRegistryValue(value, target);
|
|
1741
1837
|
return false;
|
|
1742
1838
|
}
|
|
1743
1839
|
//#endregion
|
|
@@ -1786,6 +1882,23 @@ function hasInvalidV11PatchValue(key, value, target) {
|
|
|
1786
1882
|
return false;
|
|
1787
1883
|
}
|
|
1788
1884
|
/**
|
|
1885
|
+
* Validate the fields newly consumed or ignored by pnpm 12.16.
|
|
1886
|
+
*
|
|
1887
|
+
* @param key - Canonical setting name
|
|
1888
|
+
* @param value - Untrusted source value
|
|
1889
|
+
* @param target - Release controlling the workspace reader
|
|
1890
|
+
*
|
|
1891
|
+
* @returns Whether the setting cannot be represented for this target
|
|
1892
|
+
*/
|
|
1893
|
+
function hasInvalidV12_16Value(key, value, target) {
|
|
1894
|
+
if (!supportsMinimumVersion(target.version, PNPM_V12_16_MINIMUM_VERSION)) return false;
|
|
1895
|
+
if (key === "scriptsPrependNodePath") return true;
|
|
1896
|
+
if (["strictSsl", "runtime"].includes(key)) return value !== null && !isBoolean(value);
|
|
1897
|
+
if (["localAddress", "tag"].includes(key)) return value !== null && !isString(value);
|
|
1898
|
+
if (key === "requiredScripts") return value !== null && (!isArray(value) || !value.every(isString));
|
|
1899
|
+
return false;
|
|
1900
|
+
}
|
|
1901
|
+
/**
|
|
1789
1902
|
* Validate scalar settings introduced or first consumed in pnpm 12.6.
|
|
1790
1903
|
*
|
|
1791
1904
|
* @param key - Canonical setting name
|
|
@@ -1831,6 +1944,7 @@ function hasInvalidScalarValue(key, value, target) {
|
|
|
1831
1944
|
* @returns Whether the value cannot be used by the target release
|
|
1832
1945
|
*/
|
|
1833
1946
|
function hasInvalidReleaseValue(key, value, target) {
|
|
1947
|
+
if (hasInvalidV12_16Value(key, value, target)) return true;
|
|
1834
1948
|
if (key === "globalShims" && supportsMinimumVersion(target.version, PNPM_V12_8_2_MINIMUM_VERSION)) return true;
|
|
1835
1949
|
if (key === "userAgent" && isString(value) && value.includes("${") && (supportsMinimumVersion(target.version, PNPM_V11_28_MINIMUM_VERSION) || supportsMinimumVersion(target.version, PNPM_V12_7_MINIMUM_VERSION))) return true;
|
|
1836
1950
|
if (key === "packages" && value !== null && value !== void 0 && supportsMinimumVersion(target.version, PNPM_V11_28_1_MINIMUM_VERSION)) return !isArray(value) || value.some((entry) => !isString(entry) || !entry);
|
|
@@ -2018,7 +2132,7 @@ function resolveSettingIssue({ allowedFields, target, key, npmrc, value }) {
|
|
|
2018
2132
|
if (compatibility !== "v10" && PROJECT_REFUSED_SETTINGS.has(key)) return "refused";
|
|
2019
2133
|
if (REGISTRY_SETTINGS.has(key) && containsUnsafeRegistryValue(value)) return "unsafe";
|
|
2020
2134
|
if (PROXY_SETTINGS.has(key) && isString(value) && value.includes("${")) return "unsafe";
|
|
2021
|
-
if (hasInvalidRegistryLocations(key, value) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
2135
|
+
if (hasInvalidRegistryLocations(key, value) || hasConflictingRegistryRoutes(key, value, target) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
|
|
2022
2136
|
if (key === "tasks" && hasUnsupportedTaskFields(value, taskSettings)) return "incompatible";
|
|
2023
2137
|
if (!workspaceSettings.has(key)) return isSettingFromAnotherVersion(key, compatibility) ? "incompatible" : "unknown";
|
|
2024
2138
|
if (allowedFields && !allowedFields.has(key)) return "unsupported";
|
|
@@ -4281,6 +4395,7 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
4281
4395
|
yarnResolutions: sources.packageJson.yarnResolutions
|
|
4282
4396
|
})) return result;
|
|
4283
4397
|
const pnpmWorkspaceResult = mergeIncludedWorkspace(workspace, incomingSettings, options.strategy);
|
|
4398
|
+
assertCompatibleWorkspaceSettings(pnpmWorkspaceResult, target);
|
|
4284
4399
|
const workspaceFiles = workspace.include === void 0 ? [] : renderIncludedWorkspace(workspace, pnpmWorkspaceResult, {
|
|
4285
4400
|
...options,
|
|
4286
4401
|
target
|