fix: add support for npm lockfile v3 in import (#6931)

closes #6233

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Nacho Aldama
2023-08-17 15:51:30 +02:00
committed by GitHub
parent ce3f1712b2
commit bf21c9bf3a
7 changed files with 122 additions and 8 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-installation": patch
"pnpm": patch
---
Add support for npm lockfile v3 in `pnpm import` [#6233](https://github.com/pnpm/pnpm/issues/6233).

View File

@@ -0,0 +1 @@
!package-lock.json

View File

@@ -0,0 +1,22 @@
{
"name": "has-package-lock-json",
"version": "0.0.0",
"lockfileVersion": 3,
"packages": {
"": {
"version": "0.0.0",
"dependencies": {
"@pnpm.e2e/pkg-with-1-dep": "100.0.0"
}
},
"node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep": {
"version": "101.0.0"
},
"node_modules/@pnpm.e2e/pkg-with-1-dep": {
"version": "100.0.0",
"dependencies": {
"@pnpm.e2e/dep-of-pkg-with-1-dep": "100.0.0"
}
}
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "has-package-lock-v3-json",
"version": "0.0.0",
"dependencies": {
"@pnpm.e2e/dep-of-pkg-with-1-dep": "^101.0.0",
"@pnpm.e2e/pkg-with-1-dep": "*"
},
"scripts": {
"prepare": "exit 1"
}
}

View File

@@ -7,6 +7,7 @@ packages:
- '!has-npm-shrinkwrap-json'
- '!has-outdated-deps'
- '!has-package-lock-json'
- '!has-package-lock-v3-json'
- '!hello-world-js-bin'
- '!has-yarn-lock'
- '!has-yarn2-lock'

View File

@@ -28,11 +28,20 @@ import { yarnLockFileKeyNormalizer } from './yarnUtil'
interface NpmPackageLock {
dependencies: LockedPackagesMap
packages: LockedPackagesMap
name?: string
}
interface LockedPackage {
version: string
dependencies?: LockedPackagesMap
lockfileVersion: number
name?: string
dependencies?: LockedPackagesMap | SimpleDependenciesMap
packages?: LockedPackagesMap
}
interface SimpleDependenciesMap {
[name: string]: string
}
interface LockedPackagesMap {
@@ -109,7 +118,11 @@ export async function handler (
await exists(path.join(opts.dir, 'npm-shrinkwrap.json'))
) {
const npmPackageLock = await readNpmLockfile(opts.dir)
getAllVersionsByPackageNames(npmPackageLock, versionsByPackageNames)
if (npmPackageLock.lockfileVersion < 3) {
getAllVersionsByPackageNamesPreV3(npmPackageLock, versionsByPackageNames)
} else {
getAllVersionsByPackageNames(npmPackageLock, versionsByPackageNames)
}
} else {
throw new PnpmError('LOCKFILE_NOT_FOUND', 'No lockfile found')
}
@@ -224,7 +237,7 @@ async function readNpmLockfile (dir: string) {
throw new PnpmError('NPM_LOCKFILE_NOT_FOUND', 'No package-lock.json or npm-shrinkwrap.json found')
}
function getPreferredVersions (versionsByPackageNames: Record<string, Set<string>>) {
function getPreferredVersions (versionsByPackageNames: VersionsByPackageNames) {
const preferredVersions = mapValues(
(versions) => Object.fromEntries(Array.from(versions).map((version) => [version, 'version'])),
versionsByPackageNames
@@ -232,11 +245,11 @@ function getPreferredVersions (versionsByPackageNames: Record<string, Set<string
return preferredVersions
}
function getAllVersionsByPackageNames (
type VersionsByPackageNames = Record<string, Set<string>>
function getAllVersionsByPackageNamesPreV3 (
npmPackageLock: NpmPackageLock | LockedPackage,
versionsByPackageNames: {
[packageName: string]: Set<string>
}
versionsByPackageNames: VersionsByPackageNames
) {
if (npmPackageLock.dependencies == null) return
for (const [packageName, { version }] of Object.entries(npmPackageLock.dependencies)) {
@@ -246,7 +259,48 @@ function getAllVersionsByPackageNames (
versionsByPackageNames[packageName].add(version)
}
for (const dep of Object.values(npmPackageLock.dependencies)) {
getAllVersionsByPackageNames(dep, versionsByPackageNames)
getAllVersionsByPackageNamesPreV3(dep, versionsByPackageNames)
}
}
function getAllVersionsByPackageNames (
pkg: NpmPackageLock | LockedPackage,
versionsByPackageNames: VersionsByPackageNames
): void {
if (pkg.dependencies) {
extractDependencies(versionsByPackageNames, pkg.dependencies as LockedPackagesMap)
}
if ('packages' in pkg && pkg.packages) {
extractDependencies(versionsByPackageNames, pkg.packages)
}
}
function extractDependencies (
versionsByPackageNames: VersionsByPackageNames,
dependencies: LockedPackagesMap
): void {
for (let [pkgName, pkgDetails] of Object.entries(dependencies)) {
if (pkgName.includes('node_modules')) {
pkgName = pkgName.substring(pkgName.lastIndexOf('node_modules/') + 13)
}
if (!versionsByPackageNames[pkgName]) {
versionsByPackageNames[pkgName] = new Set<string>()
}
if (pkgDetails.version) {
versionsByPackageNames[pkgName].add(pkgDetails.version)
}
if (pkgDetails.packages) {
extractDependencies(versionsByPackageNames, pkgDetails.packages)
}
if (pkgDetails.dependencies) {
for (const [pkgName1, version] of Object.entries(pkgDetails.dependencies)) {
if (!versionsByPackageNames[pkgName1]) {
versionsByPackageNames[pkgName1] = new Set<string>()
}
versionsByPackageNames[pkgName1].add(version)
}
}
}
}

View File

@@ -131,3 +131,22 @@ test('import fails when no lockfiles are found', async () => {
new PnpmError('LOCKFILE_NOT_FOUND', 'No lockfile found')
)
})
test('import from package-lock.json v3', async () => {
await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' })
f.prepare('has-package-lock-v3-json')
await importCommand.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, [])
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
expect(lockfile.packages).toHaveProperty(['/@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0'])
expect(lockfile.packages).not.toHaveProperty(['/@pnpm.e2e/dep-of-pkg-with-1-dep@100.1.0'])
// node_modules is not created
await project.hasNot('@pnpm.e2e/dep-of-pkg-with-1-dep')
await project.hasNot('@pnpm.e2e/pkg-with-1-dep')
})