fix: init-package-manager (#9104)

* fix: init-package-manager

* fix: don't add packageManager=false to package.json

* refactor: create object.key-sorting

* test: fix

* fix: sort fields in package.json
This commit is contained in:
Zoltan Kochan
2025-02-16 02:13:07 +01:00
committed by GitHub
parent aa29bde199
commit fee898fdcb
20 changed files with 226 additions and 40 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/config": patch
"pnpm": patch
---
Setting `init-package-manager` should work.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/object.key-sorting": major
---
Initial version.

View File

@@ -44,6 +44,7 @@ export const types = Object.assign({
'ignore-workspace-root-check': Boolean,
'optimistic-repeat-install': Boolean,
'include-workspace-root': Boolean,
'init-package-manager': Boolean,
'inject-workspace-packages': Boolean,
'legacy-dir-filtering': Boolean,
'link-workspace-packages': [Boolean, 'deep'],

View File

@@ -54,8 +54,8 @@
"@pnpm/lockfile.merger": "workspace:*",
"@pnpm/lockfile.types": "workspace:*",
"@pnpm/lockfile.utils": "workspace:*",
"@pnpm/object.key-sorting": "workspace:*",
"@pnpm/types": "workspace:*",
"@pnpm/util.lex-comparator": "catalog:",
"@zkochan/rimraf": "catalog:",
"comver-to-semver": "catalog:",
"js-yaml": "catalog:",

View File

@@ -1,6 +1,5 @@
import { lexCompare } from '@pnpm/util.lex-comparator'
import sortKeys from 'sort-keys'
import { type LockfileFile } from '@pnpm/lockfile.types'
import { sortKeysByPriority, sortDirectKeys, sortDeepKeys } from '@pnpm/object.key-sorting'
const ORDERED_KEYS = {
resolution: 1,
@@ -45,56 +44,43 @@ const ROOT_KEYS: readonly RootKey[] = [
]
const ROOT_KEYS_ORDER = Object.fromEntries(ROOT_KEYS.map((key, index) => [key, index]))
function compareWithPriority (priority: Record<string, number>, left: string, right: string): number {
const leftPriority = priority[left]
const rightPriority = priority[right]
if (leftPriority != null && rightPriority != null) return leftPriority - rightPriority
if (leftPriority != null) return -1
if (rightPriority != null) return 1
return lexCompare(left, right)
}
export function sortLockfileKeys (lockfile: LockfileFile): LockfileFile {
const compareRootKeys = compareWithPriority.bind(null, ROOT_KEYS_ORDER)
if (lockfile.importers != null) {
lockfile.importers = sortKeys(lockfile.importers)
lockfile.importers = sortDirectKeys(lockfile.importers)
for (const [importerId, importer] of Object.entries(lockfile.importers)) {
lockfile.importers[importerId] = sortKeys(importer, {
compare: compareRootKeys,
lockfile.importers[importerId] = sortKeysByPriority({
priority: ROOT_KEYS_ORDER,
deep: true,
})
}, importer)
}
}
if (lockfile.packages != null) {
lockfile.packages = sortKeys(lockfile.packages)
lockfile.packages = sortDirectKeys(lockfile.packages)
for (const [pkgId, pkg] of Object.entries(lockfile.packages)) {
lockfile.packages[pkgId] = sortKeys(pkg, {
compare: compareWithPriority.bind(null, ORDERED_KEYS),
lockfile.packages[pkgId] = sortKeysByPriority({
priority: ORDERED_KEYS,
deep: true,
})
}, pkg)
}
}
if (lockfile.snapshots != null) {
lockfile.snapshots = sortKeys(lockfile.snapshots)
lockfile.snapshots = sortDirectKeys(lockfile.snapshots)
for (const [pkgId, pkg] of Object.entries(lockfile.snapshots)) {
lockfile.snapshots[pkgId] = sortKeys(pkg, {
compare: compareWithPriority.bind(null, ORDERED_KEYS),
lockfile.snapshots[pkgId] = sortKeysByPriority({
priority: ORDERED_KEYS,
deep: true,
})
}, pkg)
}
}
if (lockfile.catalogs != null) {
lockfile.catalogs = sortKeys(lockfile.catalogs)
lockfile.catalogs = sortDirectKeys(lockfile.catalogs)
for (const [catalogName, catalog] of Object.entries(lockfile.catalogs)) {
lockfile.catalogs[catalogName] = sortKeys(catalog, {
compare: lexCompare,
deep: true,
})
lockfile.catalogs[catalogName] = sortDeepKeys(catalog)
}
}
for (const key of ['time', 'patchedDependencies'] as const) {
if (!lockfile[key]) continue
lockfile[key] = sortKeys<any>(lockfile[key]) // eslint-disable-line @typescript-eslint/no-explicit-any
lockfile[key] = sortDirectKeys<any>(lockfile[key]) // eslint-disable-line @typescript-eslint/no-explicit-any
}
return sortKeys(lockfile, { compare: compareRootKeys })
return sortKeysByPriority({ priority: ROOT_KEYS_ORDER }, lockfile)
}

View File

@@ -9,6 +9,9 @@
"../../__typings__/**/*.d.ts"
],
"references": [
{
"path": "../../object/key-sorting"
},
{
"path": "../../packages/constants"
},

View File

@@ -0,0 +1,4 @@
# @pnpm/object.key-sorting
> Sorting the keys of an object

View File

@@ -0,0 +1,46 @@
{
"name": "@pnpm/object.key-sorting",
"version": "1000.0.0-0",
"description": "Sorting the keys of an object",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"engines": {
"node": ">=18.12"
},
"files": [
"lib",
"!*.map"
],
"scripts": {
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
"_test": "jest",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "tsc --build && pnpm run lint --fix",
"start": "tsc --watch"
},
"repository": "https://github.com/pnpm/pnpm/blob/main/object/key-sorting",
"keywords": [
"pnpm10",
"pnpm"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"homepage": "https://github.com/pnpm/pnpm/blob/main/object/key-sorting#readme",
"devDependencies": {
"@pnpm/object.key-sorting": "workspace:*"
},
"dependencies": {
"@pnpm/util.lex-comparator": "catalog:",
"sort-keys": "catalog:"
},
"funding": "https://opencollective.com/pnpm",
"exports": {
".": "./lib/index.js"
},
"jest": {
"preset": "@pnpm/jest-config"
}
}

View File

@@ -0,0 +1,46 @@
import { lexCompare } from '@pnpm/util.lex-comparator'
import _sortKeys from 'sort-keys'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function sortDirectKeys<T extends { [key: string]: any }> (
obj: T
): T {
return _sortKeys<T>(obj, {
compare: lexCompare,
deep: false,
})
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function sortDeepKeys<T extends { [key: string]: any }> (
obj: T
): T {
return _sortKeys<T>(obj, {
compare: lexCompare,
deep: true,
})
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function sortKeysByPriority<T extends { [key: string]: any }> (
opts: {
priority: Record<string, number>
deep?: boolean
},
obj: T
): T {
const compare = compareWithPriority.bind(null, opts.priority)
return _sortKeys(obj, {
compare,
deep: opts.deep,
})
}
function compareWithPriority (priority: Record<string, number>, left: string, right: string): number {
const leftPriority = priority[left]
const rightPriority = priority[right]
if (leftPriority != null && rightPriority != null) return leftPriority - rightPriority
if (leftPriority != null) return -1
if (rightPriority != null) return 1
return lexCompare(left, right)
}

View File

@@ -0,0 +1,18 @@
import { sortKeysByPriority } from '@pnpm/object.key-sorting'
test('sortKeysByPriority', () => {
expect(Object.keys(sortKeysByPriority({
priority: {
foo: 1,
bar: 2,
qar: 3,
},
}, {
a: 'a',
qar: 'qar',
b: 'b',
foo: 'foo',
c: 'c',
bar: 'bar',
}))).toStrictEqual(['foo', 'bar', 'qar', 'a', 'b', 'c'])
})

View File

@@ -0,0 +1,17 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": false,
"outDir": "../test.lib",
"rootDir": "."
},
"include": [
"**/*.ts",
"../../../__typings__/**/*.d.ts"
],
"references": [
{
"path": ".."
}
]
}

View File

@@ -0,0 +1,12 @@
{
"extends": "@pnpm/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src"
},
"include": [
"src/**/*.ts",
"../../__typings__/**/*.d.ts"
],
"references": []
}

View File

@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": [
"src/**/*.ts",
"test/**/*.ts",
"../../__typings__/**/*.d.ts"
]
}

View File

@@ -41,6 +41,7 @@
"@pnpm/cli-utils": "workspace:*",
"@pnpm/config": "workspace:*",
"@pnpm/error": "workspace:*",
"@pnpm/object.key-sorting": "workspace:*",
"@pnpm/types": "workspace:*",
"@pnpm/write-project-manifest": "workspace:*",
"camelcase-keys": "catalog:",

View File

@@ -4,6 +4,7 @@ import { docsUrl } from '@pnpm/cli-utils'
import { packageManager } from '@pnpm/cli-meta'
import { type Config, type UniversalOptions } from '@pnpm/config'
import { PnpmError } from '@pnpm/error'
import { sortKeysByPriority } from '@pnpm/object.key-sorting'
import { type ProjectManifest } from '@pnpm/types'
import { writeProjectManifest } from '@pnpm/write-project-manifest'
import renderHelp from 'render-help'
@@ -59,7 +60,19 @@ export async function handler (
if (opts.initPackageManager) {
packageJson.packageManager = `pnpm@${packageManager.version}`
}
await writeProjectManifest(manifestPath, packageJson, {
const priority = Object.fromEntries([
'name',
'version',
'description',
'main',
'scripts',
'keywords',
'author',
'license',
'packageManager',
].map((key, index) => [key, index]))
const sortedPackageJson = sortKeysByPriority({ priority }, packageJson)
await writeProjectManifest(manifestPath, sortedPackageJson, {
indent: 2,
})
return `Wrote to ${manifestPath}

View File

@@ -38,7 +38,7 @@ export function workWithInitConfig (localConfig: Record<string, string>): Record
const packageJson: Record<string, string> = {}
const authorInfo: Record<string, string> = {}
for (const localConfigKey in localConfig) {
if (localConfigKey.startsWith('init')) {
if (localConfigKey.startsWith('init') && localConfigKey !== 'initPackageManager') {
const pureKey = localConfigKey.replace('init', '')
const value = localConfig[localConfigKey]
if (pureKey.startsWith('Author')) {

View File

@@ -71,7 +71,7 @@ test('init a new package.json if a package.json exists in the current directory
test('init a new package.json with init-package-manager=true', async () => {
prepareEmpty()
await init.handler({ rawConfig: {}, cliOptions: {}, initPackageManager: true })
await init.handler({ rawConfig: { 'init-package-manager': true }, cliOptions: {}, initPackageManager: true })
const manifest = loadJsonFile<ProjectManifest>(path.resolve('package.json'))
expect(manifest).toBeTruthy()
expect(manifest.packageManager).toBeTruthy()
@@ -79,8 +79,8 @@ test('init a new package.json with init-package-manager=true', async () => {
test('init a new package.json with init-package-manager=false', async () => {
prepareEmpty()
await init.handler({ rawConfig: {}, cliOptions: {}, initPackageManager: false })
await init.handler({ rawConfig: { 'init-package-manager': false }, cliOptions: {}, initPackageManager: false })
const manifest = loadJsonFile<ProjectManifest>(path.resolve('package.json'))
expect(manifest).toBeTruthy()
expect(manifest.packageManager).toBeFalsy()
expect(manifest).not.toHaveProperty('packageManager')
})

View File

@@ -24,6 +24,9 @@
{
"path": "../../config/config"
},
{
"path": "../../object/key-sorting"
},
{
"path": "../../pkg-manifest/write-project-manifest"
},

22
pnpm-lock.yaml generated
View File

@@ -3225,12 +3225,12 @@ importers:
'@pnpm/lockfile.utils':
specifier: workspace:*
version: link:../utils
'@pnpm/object.key-sorting':
specifier: workspace:*
version: link:../../object/key-sorting
'@pnpm/types':
specifier: workspace:*
version: link:../../packages/types
'@pnpm/util.lex-comparator':
specifier: 'catalog:'
version: 3.0.0
'@zkochan/rimraf':
specifier: 'catalog:'
version: 3.0.2
@@ -3752,6 +3752,19 @@ importers:
specifier: workspace:*
version: 'link:'
object/key-sorting:
dependencies:
'@pnpm/util.lex-comparator':
specifier: 'catalog:'
version: 3.0.0
sort-keys:
specifier: 'catalog:'
version: 4.2.0
devDependencies:
'@pnpm/object.key-sorting':
specifier: workspace:*
version: 'link:'
packages/calc-dep-state:
dependencies:
'@pnpm/constants':
@@ -3952,6 +3965,9 @@ importers:
'@pnpm/error':
specifier: workspace:*
version: link:../error
'@pnpm/object.key-sorting':
specifier: workspace:*
version: link:../../object/key-sorting
'@pnpm/types':
specifier: workspace:*
version: link:../types

View File

@@ -20,6 +20,7 @@ packages:
- lockfile/*
- network/*
- modules-mounter/*
- object/*
- packages/*
- pkg-manager/*
- pkg-manifest/*