pnpm-settings-migrator 0.3.0 → 0.4.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 +12 -0
- package/dist/cli.mjs +136 -6
- package/dist/index.d.mts +27 -2
- package/dist/index.mjs +134 -4
- package/package.json +16 -14
package/README.md
CHANGED
|
@@ -31,6 +31,18 @@ Current working directory.
|
|
|
31
31
|
|
|
32
32
|
Sort keys when write `pnpm-workspace.yaml`.
|
|
33
33
|
|
|
34
|
+
### `--compatibility`
|
|
35
|
+
|
|
36
|
+
- **Type**: `'auto' | 'v10' | 'v11'`
|
|
37
|
+
- **Default**: `'auto'`
|
|
38
|
+
|
|
39
|
+
Compatibility target for migrated settings:
|
|
40
|
+
|
|
41
|
+
- `auto`: infer from `packageManager` (`pnpm@11+` => `v11`, otherwise `v10`)
|
|
42
|
+
- `v10`: keep legacy settings as-is
|
|
43
|
+
- `v11`: normalize to v11-compatible settings (`allowBuilds`, `allowUnusedPatches`, etc.)
|
|
44
|
+
and migrate all non auth/registry `.npmrc` entries to `pnpm-workspace.yaml`
|
|
45
|
+
|
|
34
46
|
### `--strategy`
|
|
35
47
|
|
|
36
48
|
- **Type**: `'discard' | 'merge' | 'overwrite'`
|
package/dist/cli.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { readIniFile } from "read-ini-file";
|
|
|
13
13
|
import { kebabCase } from "uncase";
|
|
14
14
|
//#region package.json
|
|
15
15
|
var name = "pnpm-settings-migrator";
|
|
16
|
-
var version = "0.
|
|
16
|
+
var version = "0.4.0";
|
|
17
17
|
//#endregion
|
|
18
18
|
//#region src/constants.ts
|
|
19
19
|
const NPMRC = ".npmrc";
|
|
@@ -50,6 +50,14 @@ const PNPM_SETTINGS_FIELDS = [
|
|
|
50
50
|
"supportedArchitectures",
|
|
51
51
|
"updateConfig"
|
|
52
52
|
];
|
|
53
|
+
const PNPM_V11_REMOVED_SETTINGS = [
|
|
54
|
+
"allowNonAppliedPatches",
|
|
55
|
+
"ignorePatchFailures",
|
|
56
|
+
"ignoredBuiltDependencies",
|
|
57
|
+
"neverBuiltDependencies",
|
|
58
|
+
"onlyBuiltDependencies",
|
|
59
|
+
"onlyBuiltDependenciesFile"
|
|
60
|
+
];
|
|
53
61
|
//#endregion
|
|
54
62
|
//#region src/options.ts
|
|
55
63
|
/**
|
|
@@ -58,6 +66,7 @@ const PNPM_SETTINGS_FIELDS = [
|
|
|
58
66
|
const DEFAULT_OPTIONS = {
|
|
59
67
|
cleanNpmrc: true,
|
|
60
68
|
cleanPackageJson: true,
|
|
69
|
+
compatibility: "auto",
|
|
61
70
|
cwd: process.cwd(),
|
|
62
71
|
newlineBetween: true,
|
|
63
72
|
sortKeys: false,
|
|
@@ -69,6 +78,11 @@ const VALID_STRATEGIES = [
|
|
|
69
78
|
"merge",
|
|
70
79
|
"overwrite"
|
|
71
80
|
];
|
|
81
|
+
const VALID_COMPATIBILITIES = [
|
|
82
|
+
"auto",
|
|
83
|
+
"v10",
|
|
84
|
+
"v11"
|
|
85
|
+
];
|
|
72
86
|
/**
|
|
73
87
|
* Resolve and normalize migration options with defaults.
|
|
74
88
|
*
|
|
@@ -93,6 +107,7 @@ const VALID_STRATEGIES = [
|
|
|
93
107
|
function resolveOptions(options = {}) {
|
|
94
108
|
return {
|
|
95
109
|
cleanNpmrc: options.cleanNpmrc ?? DEFAULT_OPTIONS.cleanNpmrc,
|
|
110
|
+
compatibility: resolveCompatibility(options.compatibility),
|
|
96
111
|
cwd: options.cwd ?? DEFAULT_OPTIONS.cwd,
|
|
97
112
|
newlineBetween: options.newlineBetween ?? DEFAULT_OPTIONS.newlineBetween,
|
|
98
113
|
sortKeys: options.sortKeys ?? DEFAULT_OPTIONS.sortKeys,
|
|
@@ -101,6 +116,11 @@ function resolveOptions(options = {}) {
|
|
|
101
116
|
cleanPackageJson: options.cleanPackageJson ?? DEFAULT_OPTIONS.cleanPackageJson
|
|
102
117
|
};
|
|
103
118
|
}
|
|
119
|
+
function resolveCompatibility(compatibility) {
|
|
120
|
+
if (!compatibility) return DEFAULT_OPTIONS.compatibility;
|
|
121
|
+
if (VALID_COMPATIBILITIES.includes(compatibility)) return compatibility;
|
|
122
|
+
throw new Error(`Invalid compatibility: ${compatibility}. Expected one of: ${VALID_COMPATIBILITIES.join(", ")}`);
|
|
123
|
+
}
|
|
104
124
|
function resolveStrategy(strategy) {
|
|
105
125
|
if (!strategy) return DEFAULT_OPTIONS.strategy;
|
|
106
126
|
if (VALID_STRATEGIES.includes(strategy)) return strategy;
|
|
@@ -216,6 +236,22 @@ function mergeWithArrayDedupe(existing, incoming) {
|
|
|
216
236
|
}
|
|
217
237
|
//#endregion
|
|
218
238
|
//#region src/utils/npmrc.ts
|
|
239
|
+
const NPMRC_AUTH_OR_REGISTRY_KEYS = [
|
|
240
|
+
"_auth",
|
|
241
|
+
"_authtoken",
|
|
242
|
+
"_password",
|
|
243
|
+
"always-auth",
|
|
244
|
+
"ca",
|
|
245
|
+
"cafile",
|
|
246
|
+
"cert",
|
|
247
|
+
"certfile",
|
|
248
|
+
"email",
|
|
249
|
+
"key",
|
|
250
|
+
"keyfile",
|
|
251
|
+
"otp",
|
|
252
|
+
"tokenhelper",
|
|
253
|
+
"username"
|
|
254
|
+
];
|
|
219
255
|
/**
|
|
220
256
|
* Remove pnpm-related settings from `.npmrc` file.
|
|
221
257
|
*
|
|
@@ -234,9 +270,15 @@ function mergeWithArrayDedupe(existing, incoming) {
|
|
|
234
270
|
* await pruneNpmrc('/path/to/.npmrc')
|
|
235
271
|
* ```
|
|
236
272
|
*/
|
|
237
|
-
async function pruneNpmrc(path) {
|
|
273
|
+
async function pruneNpmrc(path, compatibility = "v10", migratedKeys = []) {
|
|
238
274
|
const pnpmSettingsFields = PNPM_SETTINGS_FIELDS.map((v) => kebabCase(v));
|
|
239
|
-
|
|
275
|
+
const migratedKeySet = new Set(migratedKeys.map(normalizeNpmrcKey));
|
|
276
|
+
await fsWriteFile(path, (await fsReadFile(path)).split(/\r?\n/).filter((line) => {
|
|
277
|
+
const key = getNpmrcLineKey(line);
|
|
278
|
+
if (!key) return true;
|
|
279
|
+
if (compatibility === "v11") return !migratedKeySet.has(normalizeNpmrcKey(key));
|
|
280
|
+
return !pnpmSettingsFields.some((v) => key.startsWith(v));
|
|
281
|
+
}).join("\n"));
|
|
240
282
|
}
|
|
241
283
|
/**
|
|
242
284
|
* Read and parse `.npmrc` file with camelCase key conversion.
|
|
@@ -257,9 +299,57 @@ async function pruneNpmrc(path) {
|
|
|
257
299
|
* // config.allowBuilds, config.packageExtensions, etc.
|
|
258
300
|
* ```
|
|
259
301
|
*/
|
|
302
|
+
/**
|
|
303
|
+
* Read `.npmrc` and return settings that should be migrated into workspace config.
|
|
304
|
+
*
|
|
305
|
+
* - `v10`: returns all keys (legacy whitelist filtering happens in caller).
|
|
306
|
+
* - `v11`: excludes auth/registry keys because pnpm still reads them from `.npmrc`.
|
|
307
|
+
*/
|
|
308
|
+
async function readMigratableNpmrc(path, compatibility) {
|
|
309
|
+
const raw = await readIniFile(path);
|
|
310
|
+
if (compatibility === "v10") return {
|
|
311
|
+
keys: Object.keys(raw),
|
|
312
|
+
settings: camelcaseKeys(raw)
|
|
313
|
+
};
|
|
314
|
+
const migratable = {};
|
|
315
|
+
const keys = [];
|
|
316
|
+
for (const [key, value] of Object.entries(raw)) if (!isNpmrcAuthOrRegistryKey(key)) {
|
|
317
|
+
migratable[key] = value;
|
|
318
|
+
keys.push(key);
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
keys,
|
|
322
|
+
settings: camelcaseKeys(migratable)
|
|
323
|
+
};
|
|
324
|
+
}
|
|
260
325
|
async function readNpmrc(path) {
|
|
261
326
|
return camelcaseKeys(await readIniFile(path));
|
|
262
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Extract key name from a raw `.npmrc` line.
|
|
330
|
+
*/
|
|
331
|
+
function getNpmrcLineKey(line) {
|
|
332
|
+
const trimmed = line.trim();
|
|
333
|
+
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) return;
|
|
334
|
+
const index = trimmed.indexOf("=");
|
|
335
|
+
if (index <= 0) return;
|
|
336
|
+
return normalizeNpmrcKey(trimmed.slice(0, index));
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Check whether a `.npmrc` key is auth/registry-related and should stay in v11.
|
|
340
|
+
*/
|
|
341
|
+
function isNpmrcAuthOrRegistryKey(key) {
|
|
342
|
+
const normalized = normalizeNpmrcKey(key);
|
|
343
|
+
if (normalized.startsWith("//")) return true;
|
|
344
|
+
if (normalized === "registry" || normalized.endsWith(":registry")) return true;
|
|
345
|
+
return NPMRC_AUTH_OR_REGISTRY_KEYS.some((v) => normalized === v || normalized.endsWith(`:${v}`));
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Normalize `.npmrc` key for case-insensitive matching.
|
|
349
|
+
*/
|
|
350
|
+
function normalizeNpmrcKey(key) {
|
|
351
|
+
return key.trim().replace(/\[\]$/, "").toLowerCase();
|
|
352
|
+
}
|
|
263
353
|
//#endregion
|
|
264
354
|
//#region src/core.ts
|
|
265
355
|
/**
|
|
@@ -327,7 +417,15 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
327
417
|
pnpmWorkspaceYamlIndent = detectIndent(content).amount;
|
|
328
418
|
pnpmWorkspaceYamlObject = parse(content);
|
|
329
419
|
}
|
|
330
|
-
const
|
|
420
|
+
const compatibility = resolveCompatibilityTarget(options.compatibility, packageJsonObject.packageManager);
|
|
421
|
+
const npmrcMigratable = npmrcExists ? compatibility === "v10" ? {
|
|
422
|
+
keys: PNPM_SETTINGS_FIELDS,
|
|
423
|
+
settings: pick(await readNpmrc(npmrcPath), PNPM_SETTINGS_FIELDS)
|
|
424
|
+
} : await readMigratableNpmrc(npmrcPath, compatibility) : {
|
|
425
|
+
keys: [],
|
|
426
|
+
settings: {}
|
|
427
|
+
};
|
|
428
|
+
const pnpmSettingsInNpmrc = npmrcMigratable.settings;
|
|
331
429
|
const hasPnpmInPackageJson = !!packageJsonObject.pnpm;
|
|
332
430
|
const hasResolutions = options.yarnResolutions && !!packageJsonObject.resolutions;
|
|
333
431
|
const hasNpmrcSettings = Object.keys(pnpmSettingsInNpmrc).length > 0;
|
|
@@ -344,6 +442,7 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
344
442
|
...pnpmSettingsInNpmrc,
|
|
345
443
|
...pnpmSettingsInPackageJson
|
|
346
444
|
};
|
|
445
|
+
normalizeIncomingSettings(incomingSettings, compatibility);
|
|
347
446
|
const pnpmWorkspaceResult = mergeByStrategy(pnpmWorkspaceYamlObject, incomingSettings, options.strategy);
|
|
348
447
|
const yamlDocument = new Document({}, { sortMapEntries: options.sortKeys });
|
|
349
448
|
Object.entries(pnpmWorkspaceResult).forEach(([key, value]) => {
|
|
@@ -354,7 +453,7 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
354
453
|
});
|
|
355
454
|
const yamlContent = yamlDocument.toString({ indent: pnpmWorkspaceYamlIndent });
|
|
356
455
|
await fsWriteFile(pnpmWorkspaceYamlPath, options.newlineBetween ? yamlContent.replace(/\n(?=[^\s#][^:\n]*:)/g, "\n\n") : yamlContent);
|
|
357
|
-
if (npmrcExists && options.cleanNpmrc) await pruneNpmrc(npmrcPath);
|
|
456
|
+
if (npmrcExists && options.cleanNpmrc) await pruneNpmrc(npmrcPath, compatibility, npmrcMigratable.keys);
|
|
358
457
|
if (packageJsonExists && options.cleanPackageJson && (packageJsonObject.pnpm || packageJsonObject.resolutions)) {
|
|
359
458
|
delete packageJsonObject.pnpm;
|
|
360
459
|
if (options.yarnResolutions) delete packageJsonObject.resolutions;
|
|
@@ -365,10 +464,41 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
365
464
|
throw err;
|
|
366
465
|
}
|
|
367
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Build v11 `allowBuilds` map from legacy build-script settings.
|
|
469
|
+
*/
|
|
470
|
+
function collectAllowBuildsFromLegacy(incomingSettings) {
|
|
471
|
+
const allowBuilds = {};
|
|
472
|
+
for (const name of incomingSettings.onlyBuiltDependencies || []) allowBuilds[name] = true;
|
|
473
|
+
for (const name of incomingSettings.ignoredBuiltDependencies || []) allowBuilds[name] = false;
|
|
474
|
+
for (const name of incomingSettings.neverBuiltDependencies || []) allowBuilds[name] = false;
|
|
475
|
+
return Object.keys(allowBuilds).length ? allowBuilds : void 0;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Normalize incoming settings according to compatibility target.
|
|
479
|
+
*/
|
|
480
|
+
function normalizeIncomingSettings(incomingSettings, compatibility) {
|
|
481
|
+
if (compatibility === "v10") return;
|
|
482
|
+
if (incomingSettings.allowNonAppliedPatches !== void 0) incomingSettings.allowUnusedPatches = incomingSettings.allowUnusedPatches ?? incomingSettings.allowNonAppliedPatches;
|
|
483
|
+
const allowBuildsFromLegacy = collectAllowBuildsFromLegacy(incomingSettings);
|
|
484
|
+
if (allowBuildsFromLegacy) incomingSettings.allowBuilds = {
|
|
485
|
+
...allowBuildsFromLegacy,
|
|
486
|
+
...incomingSettings.allowBuilds || {}
|
|
487
|
+
};
|
|
488
|
+
for (const key of PNPM_V11_REMOVED_SETTINGS) delete incomingSettings[key];
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Resolve final compatibility target from user option and package manager hint.
|
|
492
|
+
*/
|
|
493
|
+
function resolveCompatibilityTarget(compatibility, packageManager) {
|
|
494
|
+
if (compatibility !== "auto") return compatibility;
|
|
495
|
+
const [, major] = (packageManager || "").match(/^pnpm@(\d+)(?:\.|$)/) || [];
|
|
496
|
+
return Number(major) >= 11 ? "v11" : "v10";
|
|
497
|
+
}
|
|
368
498
|
//#endregion
|
|
369
499
|
//#region src/cli.ts
|
|
370
500
|
const cli = cac(name);
|
|
371
|
-
cli.version(version).option("--cwd [cwd]", "Current working directory").option("--sort-keys", "Sort keys when write pnpm-workspace.yaml").option("--strategy <strategy>", "Strategy to handle conflicts (discard, merge, overwrite)").option("--no-yarn-resolutions", "Disable migrating resolutions field in package.json").option("--no-newline-between", "Disable adding newlines between each root keys").option("--no-clean-npmrc", "Disable removing pnpm settings in .npmrc file").option("--no-clean-package-json", "Disable removing pnpm fields in package.json").help();
|
|
501
|
+
cli.version(version).option("--cwd [cwd]", "Current working directory").option("--sort-keys", "Sort keys when write pnpm-workspace.yaml").option("--compatibility <compatibility>", "Compatibility target (auto, v10, v11)").option("--strategy <strategy>", "Strategy to handle conflicts (discard, merge, overwrite)").option("--no-yarn-resolutions", "Disable migrating resolutions field in package.json").option("--no-newline-between", "Disable adding newlines between each root keys").option("--no-clean-npmrc", "Disable removing pnpm settings in .npmrc file").option("--no-clean-package-json", "Disable removing pnpm fields in package.json").help();
|
|
372
502
|
cli.command("").action(async (options) => {
|
|
373
503
|
try {
|
|
374
504
|
consola$1.log(`\n${bold(magenta(name))} ${dim(`v${version}`)}`);
|
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,11 @@ import { PnpmSettings } from "@pnpm/types";
|
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Compatibility target used by the migrator.
|
|
6
|
+
*/
|
|
7
|
+
type CompatibilityTarget = 'auto' | 'v10' | 'v11';
|
|
8
|
+
/**
|
|
9
|
+
* Merge strategy for combining pnpm settings.
|
|
6
10
|
*/
|
|
7
11
|
type MergeStrategy = 'discard' | 'merge' | 'overwrite';
|
|
8
12
|
/**
|
|
@@ -21,6 +25,15 @@ interface Options {
|
|
|
21
25
|
* @default true
|
|
22
26
|
*/
|
|
23
27
|
cleanPackageJson?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* pnpm compatibility target.
|
|
30
|
+
* - `auto`: detect from `packageManager` (fallback to `v10`)
|
|
31
|
+
* - `v10`: keep legacy v10 settings
|
|
32
|
+
* - `v11`: normalize to v11-compatible settings
|
|
33
|
+
*
|
|
34
|
+
* @default 'auto'
|
|
35
|
+
*/
|
|
36
|
+
compatibility?: CompatibilityTarget;
|
|
24
37
|
/**
|
|
25
38
|
* Current working directory
|
|
26
39
|
*
|
|
@@ -68,6 +81,10 @@ interface PackageJson {
|
|
|
68
81
|
* @compatibility npm, bun
|
|
69
82
|
*/
|
|
70
83
|
overrides?: Record<string, string>;
|
|
84
|
+
/**
|
|
85
|
+
* Package manager declaration, for example `pnpm@11.0.0`
|
|
86
|
+
*/
|
|
87
|
+
packageManager?: string;
|
|
71
88
|
/**
|
|
72
89
|
* pnpm settings
|
|
73
90
|
*/
|
|
@@ -89,10 +106,18 @@ type NpmRC = Record<string, any>;
|
|
|
89
106
|
* @see {@link https://github.com/pnpm/pnpm/blob/main/core/types/CHANGELOG.md#major-changes}
|
|
90
107
|
*/
|
|
91
108
|
interface PnpmSettingsDeprecated {
|
|
109
|
+
/**
|
|
110
|
+
* @deprecated
|
|
111
|
+
*/
|
|
112
|
+
allowNonAppliedPatches?: boolean;
|
|
92
113
|
/**
|
|
93
114
|
* @deprecated
|
|
94
115
|
*/
|
|
95
116
|
ignoredBuiltDependencies?: string[];
|
|
117
|
+
/**
|
|
118
|
+
* @deprecated
|
|
119
|
+
*/
|
|
120
|
+
ignorePatchFailures?: boolean;
|
|
96
121
|
/**
|
|
97
122
|
* @deprecated
|
|
98
123
|
*/
|
|
@@ -173,4 +198,4 @@ declare function migratePnpmSettings(rawOptions?: Options): Promise<void>;
|
|
|
173
198
|
*/
|
|
174
199
|
declare function resolveOptions(options?: Options): Required<Options>;
|
|
175
200
|
//#endregion
|
|
176
|
-
export { MergeStrategy, NpmRC, Options, PackageJson, PnpmSettingsDeprecated, PnpmWorkspace, PnpmWorkspaceLegacy, migratePnpmSettings, resolveOptions };
|
|
201
|
+
export { CompatibilityTarget, MergeStrategy, NpmRC, Options, PackageJson, PnpmSettingsDeprecated, PnpmWorkspace, PnpmWorkspaceLegacy, migratePnpmSettings, resolveOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -45,6 +45,14 @@ const PNPM_SETTINGS_FIELDS = [
|
|
|
45
45
|
"supportedArchitectures",
|
|
46
46
|
"updateConfig"
|
|
47
47
|
];
|
|
48
|
+
const PNPM_V11_REMOVED_SETTINGS = [
|
|
49
|
+
"allowNonAppliedPatches",
|
|
50
|
+
"ignorePatchFailures",
|
|
51
|
+
"ignoredBuiltDependencies",
|
|
52
|
+
"neverBuiltDependencies",
|
|
53
|
+
"onlyBuiltDependencies",
|
|
54
|
+
"onlyBuiltDependenciesFile"
|
|
55
|
+
];
|
|
48
56
|
//#endregion
|
|
49
57
|
//#region src/options.ts
|
|
50
58
|
/**
|
|
@@ -53,6 +61,7 @@ const PNPM_SETTINGS_FIELDS = [
|
|
|
53
61
|
const DEFAULT_OPTIONS = {
|
|
54
62
|
cleanNpmrc: true,
|
|
55
63
|
cleanPackageJson: true,
|
|
64
|
+
compatibility: "auto",
|
|
56
65
|
cwd: process.cwd(),
|
|
57
66
|
newlineBetween: true,
|
|
58
67
|
sortKeys: false,
|
|
@@ -64,6 +73,11 @@ const VALID_STRATEGIES = [
|
|
|
64
73
|
"merge",
|
|
65
74
|
"overwrite"
|
|
66
75
|
];
|
|
76
|
+
const VALID_COMPATIBILITIES = [
|
|
77
|
+
"auto",
|
|
78
|
+
"v10",
|
|
79
|
+
"v11"
|
|
80
|
+
];
|
|
67
81
|
/**
|
|
68
82
|
* Resolve and normalize migration options with defaults.
|
|
69
83
|
*
|
|
@@ -88,6 +102,7 @@ const VALID_STRATEGIES = [
|
|
|
88
102
|
function resolveOptions(options = {}) {
|
|
89
103
|
return {
|
|
90
104
|
cleanNpmrc: options.cleanNpmrc ?? DEFAULT_OPTIONS.cleanNpmrc,
|
|
105
|
+
compatibility: resolveCompatibility(options.compatibility),
|
|
91
106
|
cwd: options.cwd ?? DEFAULT_OPTIONS.cwd,
|
|
92
107
|
newlineBetween: options.newlineBetween ?? DEFAULT_OPTIONS.newlineBetween,
|
|
93
108
|
sortKeys: options.sortKeys ?? DEFAULT_OPTIONS.sortKeys,
|
|
@@ -96,6 +111,11 @@ function resolveOptions(options = {}) {
|
|
|
96
111
|
cleanPackageJson: options.cleanPackageJson ?? DEFAULT_OPTIONS.cleanPackageJson
|
|
97
112
|
};
|
|
98
113
|
}
|
|
114
|
+
function resolveCompatibility(compatibility) {
|
|
115
|
+
if (!compatibility) return DEFAULT_OPTIONS.compatibility;
|
|
116
|
+
if (VALID_COMPATIBILITIES.includes(compatibility)) return compatibility;
|
|
117
|
+
throw new Error(`Invalid compatibility: ${compatibility}. Expected one of: ${VALID_COMPATIBILITIES.join(", ")}`);
|
|
118
|
+
}
|
|
99
119
|
function resolveStrategy(strategy) {
|
|
100
120
|
if (!strategy) return DEFAULT_OPTIONS.strategy;
|
|
101
121
|
if (VALID_STRATEGIES.includes(strategy)) return strategy;
|
|
@@ -211,6 +231,22 @@ function mergeWithArrayDedupe(existing, incoming) {
|
|
|
211
231
|
}
|
|
212
232
|
//#endregion
|
|
213
233
|
//#region src/utils/npmrc.ts
|
|
234
|
+
const NPMRC_AUTH_OR_REGISTRY_KEYS = [
|
|
235
|
+
"_auth",
|
|
236
|
+
"_authtoken",
|
|
237
|
+
"_password",
|
|
238
|
+
"always-auth",
|
|
239
|
+
"ca",
|
|
240
|
+
"cafile",
|
|
241
|
+
"cert",
|
|
242
|
+
"certfile",
|
|
243
|
+
"email",
|
|
244
|
+
"key",
|
|
245
|
+
"keyfile",
|
|
246
|
+
"otp",
|
|
247
|
+
"tokenhelper",
|
|
248
|
+
"username"
|
|
249
|
+
];
|
|
214
250
|
/**
|
|
215
251
|
* Remove pnpm-related settings from `.npmrc` file.
|
|
216
252
|
*
|
|
@@ -229,9 +265,15 @@ function mergeWithArrayDedupe(existing, incoming) {
|
|
|
229
265
|
* await pruneNpmrc('/path/to/.npmrc')
|
|
230
266
|
* ```
|
|
231
267
|
*/
|
|
232
|
-
async function pruneNpmrc(path) {
|
|
268
|
+
async function pruneNpmrc(path, compatibility = "v10", migratedKeys = []) {
|
|
233
269
|
const pnpmSettingsFields = PNPM_SETTINGS_FIELDS.map((v) => kebabCase(v));
|
|
234
|
-
|
|
270
|
+
const migratedKeySet = new Set(migratedKeys.map(normalizeNpmrcKey));
|
|
271
|
+
await fsWriteFile(path, (await fsReadFile(path)).split(/\r?\n/).filter((line) => {
|
|
272
|
+
const key = getNpmrcLineKey(line);
|
|
273
|
+
if (!key) return true;
|
|
274
|
+
if (compatibility === "v11") return !migratedKeySet.has(normalizeNpmrcKey(key));
|
|
275
|
+
return !pnpmSettingsFields.some((v) => key.startsWith(v));
|
|
276
|
+
}).join("\n"));
|
|
235
277
|
}
|
|
236
278
|
/**
|
|
237
279
|
* Read and parse `.npmrc` file with camelCase key conversion.
|
|
@@ -252,9 +294,57 @@ async function pruneNpmrc(path) {
|
|
|
252
294
|
* // config.allowBuilds, config.packageExtensions, etc.
|
|
253
295
|
* ```
|
|
254
296
|
*/
|
|
297
|
+
/**
|
|
298
|
+
* Read `.npmrc` and return settings that should be migrated into workspace config.
|
|
299
|
+
*
|
|
300
|
+
* - `v10`: returns all keys (legacy whitelist filtering happens in caller).
|
|
301
|
+
* - `v11`: excludes auth/registry keys because pnpm still reads them from `.npmrc`.
|
|
302
|
+
*/
|
|
303
|
+
async function readMigratableNpmrc(path, compatibility) {
|
|
304
|
+
const raw = await readIniFile(path);
|
|
305
|
+
if (compatibility === "v10") return {
|
|
306
|
+
keys: Object.keys(raw),
|
|
307
|
+
settings: camelcaseKeys(raw)
|
|
308
|
+
};
|
|
309
|
+
const migratable = {};
|
|
310
|
+
const keys = [];
|
|
311
|
+
for (const [key, value] of Object.entries(raw)) if (!isNpmrcAuthOrRegistryKey(key)) {
|
|
312
|
+
migratable[key] = value;
|
|
313
|
+
keys.push(key);
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
keys,
|
|
317
|
+
settings: camelcaseKeys(migratable)
|
|
318
|
+
};
|
|
319
|
+
}
|
|
255
320
|
async function readNpmrc(path) {
|
|
256
321
|
return camelcaseKeys(await readIniFile(path));
|
|
257
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Extract key name from a raw `.npmrc` line.
|
|
325
|
+
*/
|
|
326
|
+
function getNpmrcLineKey(line) {
|
|
327
|
+
const trimmed = line.trim();
|
|
328
|
+
if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) return;
|
|
329
|
+
const index = trimmed.indexOf("=");
|
|
330
|
+
if (index <= 0) return;
|
|
331
|
+
return normalizeNpmrcKey(trimmed.slice(0, index));
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Check whether a `.npmrc` key is auth/registry-related and should stay in v11.
|
|
335
|
+
*/
|
|
336
|
+
function isNpmrcAuthOrRegistryKey(key) {
|
|
337
|
+
const normalized = normalizeNpmrcKey(key);
|
|
338
|
+
if (normalized.startsWith("//")) return true;
|
|
339
|
+
if (normalized === "registry" || normalized.endsWith(":registry")) return true;
|
|
340
|
+
return NPMRC_AUTH_OR_REGISTRY_KEYS.some((v) => normalized === v || normalized.endsWith(`:${v}`));
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Normalize `.npmrc` key for case-insensitive matching.
|
|
344
|
+
*/
|
|
345
|
+
function normalizeNpmrcKey(key) {
|
|
346
|
+
return key.trim().replace(/\[\]$/, "").toLowerCase();
|
|
347
|
+
}
|
|
258
348
|
//#endregion
|
|
259
349
|
//#region src/core.ts
|
|
260
350
|
/**
|
|
@@ -322,7 +412,15 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
322
412
|
pnpmWorkspaceYamlIndent = detectIndent(content).amount;
|
|
323
413
|
pnpmWorkspaceYamlObject = parse(content);
|
|
324
414
|
}
|
|
325
|
-
const
|
|
415
|
+
const compatibility = resolveCompatibilityTarget(options.compatibility, packageJsonObject.packageManager);
|
|
416
|
+
const npmrcMigratable = npmrcExists ? compatibility === "v10" ? {
|
|
417
|
+
keys: PNPM_SETTINGS_FIELDS,
|
|
418
|
+
settings: pick(await readNpmrc(npmrcPath), PNPM_SETTINGS_FIELDS)
|
|
419
|
+
} : await readMigratableNpmrc(npmrcPath, compatibility) : {
|
|
420
|
+
keys: [],
|
|
421
|
+
settings: {}
|
|
422
|
+
};
|
|
423
|
+
const pnpmSettingsInNpmrc = npmrcMigratable.settings;
|
|
326
424
|
const hasPnpmInPackageJson = !!packageJsonObject.pnpm;
|
|
327
425
|
const hasResolutions = options.yarnResolutions && !!packageJsonObject.resolutions;
|
|
328
426
|
const hasNpmrcSettings = Object.keys(pnpmSettingsInNpmrc).length > 0;
|
|
@@ -339,6 +437,7 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
339
437
|
...pnpmSettingsInNpmrc,
|
|
340
438
|
...pnpmSettingsInPackageJson
|
|
341
439
|
};
|
|
440
|
+
normalizeIncomingSettings(incomingSettings, compatibility);
|
|
342
441
|
const pnpmWorkspaceResult = mergeByStrategy(pnpmWorkspaceYamlObject, incomingSettings, options.strategy);
|
|
343
442
|
const yamlDocument = new Document({}, { sortMapEntries: options.sortKeys });
|
|
344
443
|
Object.entries(pnpmWorkspaceResult).forEach(([key, value]) => {
|
|
@@ -349,7 +448,7 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
349
448
|
});
|
|
350
449
|
const yamlContent = yamlDocument.toString({ indent: pnpmWorkspaceYamlIndent });
|
|
351
450
|
await fsWriteFile(pnpmWorkspaceYamlPath, options.newlineBetween ? yamlContent.replace(/\n(?=[^\s#][^:\n]*:)/g, "\n\n") : yamlContent);
|
|
352
|
-
if (npmrcExists && options.cleanNpmrc) await pruneNpmrc(npmrcPath);
|
|
451
|
+
if (npmrcExists && options.cleanNpmrc) await pruneNpmrc(npmrcPath, compatibility, npmrcMigratable.keys);
|
|
353
452
|
if (packageJsonExists && options.cleanPackageJson && (packageJsonObject.pnpm || packageJsonObject.resolutions)) {
|
|
354
453
|
delete packageJsonObject.pnpm;
|
|
355
454
|
if (options.yarnResolutions) delete packageJsonObject.resolutions;
|
|
@@ -360,5 +459,36 @@ async function migratePnpmSettings(rawOptions = {}) {
|
|
|
360
459
|
throw err;
|
|
361
460
|
}
|
|
362
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Build v11 `allowBuilds` map from legacy build-script settings.
|
|
464
|
+
*/
|
|
465
|
+
function collectAllowBuildsFromLegacy(incomingSettings) {
|
|
466
|
+
const allowBuilds = {};
|
|
467
|
+
for (const name of incomingSettings.onlyBuiltDependencies || []) allowBuilds[name] = true;
|
|
468
|
+
for (const name of incomingSettings.ignoredBuiltDependencies || []) allowBuilds[name] = false;
|
|
469
|
+
for (const name of incomingSettings.neverBuiltDependencies || []) allowBuilds[name] = false;
|
|
470
|
+
return Object.keys(allowBuilds).length ? allowBuilds : void 0;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Normalize incoming settings according to compatibility target.
|
|
474
|
+
*/
|
|
475
|
+
function normalizeIncomingSettings(incomingSettings, compatibility) {
|
|
476
|
+
if (compatibility === "v10") return;
|
|
477
|
+
if (incomingSettings.allowNonAppliedPatches !== void 0) incomingSettings.allowUnusedPatches = incomingSettings.allowUnusedPatches ?? incomingSettings.allowNonAppliedPatches;
|
|
478
|
+
const allowBuildsFromLegacy = collectAllowBuildsFromLegacy(incomingSettings);
|
|
479
|
+
if (allowBuildsFromLegacy) incomingSettings.allowBuilds = {
|
|
480
|
+
...allowBuildsFromLegacy,
|
|
481
|
+
...incomingSettings.allowBuilds || {}
|
|
482
|
+
};
|
|
483
|
+
for (const key of PNPM_V11_REMOVED_SETTINGS) delete incomingSettings[key];
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Resolve final compatibility target from user option and package manager hint.
|
|
487
|
+
*/
|
|
488
|
+
function resolveCompatibilityTarget(compatibility, packageManager) {
|
|
489
|
+
if (compatibility !== "auto") return compatibility;
|
|
490
|
+
const [, major] = (packageManager || "").match(/^pnpm@(\d+)(?:\.|$)/) || [];
|
|
491
|
+
return Number(major) >= 11 ? "v11" : "v10";
|
|
492
|
+
}
|
|
363
493
|
//#endregion
|
|
364
494
|
export { migratePnpmSettings, resolveOptions };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pnpm-settings-migrator",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"description": "Move pnpm settings from `pnpm` field in `package.json` and `.npmrc` file to `pnpm-workspace.yaml`",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"migrator",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
},
|
|
28
28
|
"main": "./dist/index.mjs",
|
|
29
29
|
"types": "./dist/index.d.mts",
|
|
30
|
-
"bin": "./bin.mjs",
|
|
31
30
|
"files": [
|
|
32
31
|
"bin.mjs",
|
|
33
32
|
"dist"
|
|
@@ -37,8 +36,8 @@
|
|
|
37
36
|
},
|
|
38
37
|
"sideEffects": false,
|
|
39
38
|
"dependencies": {
|
|
40
|
-
"@ntnyq/utils": "^0.
|
|
41
|
-
"@pnpm/types": "^
|
|
39
|
+
"@ntnyq/utils": "^0.13.2",
|
|
40
|
+
"@pnpm/types": "^1101.0.0",
|
|
42
41
|
"cac": "^7.0.0",
|
|
43
42
|
"camelcase-keys": "^10.0.2",
|
|
44
43
|
"consola": "^3.4.2",
|
|
@@ -47,21 +46,21 @@
|
|
|
47
46
|
"pathe": "^2.0.3",
|
|
48
47
|
"read-ini-file": "^5.0.0",
|
|
49
48
|
"uncase": "^0.2.0",
|
|
50
|
-
"yaml": "^2.8.
|
|
49
|
+
"yaml": "^2.8.4"
|
|
51
50
|
},
|
|
52
51
|
"devDependencies": {
|
|
53
|
-
"@ntnyq/eslint-config": "^6.
|
|
54
|
-
"@types/node": "^25.6.
|
|
55
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
56
|
-
"bumpp": "^11.0
|
|
57
|
-
"eslint": "^10.
|
|
52
|
+
"@ntnyq/eslint-config": "^6.1.3",
|
|
53
|
+
"@types/node": "^25.6.2",
|
|
54
|
+
"@typescript/native-preview": "^7.0.0-dev.20260510.1",
|
|
55
|
+
"bumpp": "^11.1.0",
|
|
56
|
+
"eslint": "^10.3.0",
|
|
58
57
|
"husky": "^9.1.7",
|
|
59
58
|
"nano-staged": "^1.0.2",
|
|
60
59
|
"npm-run-all2": "^8.0.4",
|
|
61
|
-
"oxfmt": "^0.
|
|
62
|
-
"tsdown": "^0.
|
|
63
|
-
"typescript": "^6.0.
|
|
64
|
-
"vitest": "^4.1.
|
|
60
|
+
"oxfmt": "^0.48.0",
|
|
61
|
+
"tsdown": "^0.22.0",
|
|
62
|
+
"typescript": "^6.0.3",
|
|
63
|
+
"vitest": "^4.1.5"
|
|
65
64
|
},
|
|
66
65
|
"nano-staged": {
|
|
67
66
|
"*.{js,ts,mjs,tsx,md,yml,yaml,toml,json}": "eslint --fix",
|
|
@@ -78,5 +77,8 @@
|
|
|
78
77
|
"release:version": "bumpp",
|
|
79
78
|
"test": "vitest",
|
|
80
79
|
"typecheck": "tsgo --noEmit"
|
|
80
|
+
},
|
|
81
|
+
"bin": {
|
|
82
|
+
"pnpm-settings-migrator": "./bin.mjs"
|
|
81
83
|
}
|
|
82
84
|
}
|