From cdc4c51ba198d166fa83c91bc83db7fc68dd945e Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Mon, 19 Dec 2022 03:56:19 +0200 Subject: [PATCH] feat!: more changes to the new lockfile format --- .../inlineSpecifiersLockfileConverters.ts | 107 +++++++++++++++++- .../test/__snapshots__/write.test.ts.snap | 2 +- .../test/lockfileV6Converters.test.ts | 86 ++++++++++++++ packages/dependency-path/src/index.ts | 2 +- 4 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 lockfile/lockfile-file/test/lockfileV6Converters.test.ts diff --git a/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts b/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts index 28746f73309..f8ff70f9e37 100644 --- a/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts +++ b/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts @@ -1,3 +1,4 @@ +import * as dp from '@pnpm/dependency-path' import type { Lockfile, ProjectSnapshot, ResolvedDependencies } from '@pnpm/lockfile-types' import { INLINE_SPECIFIERS_FORMAT_LOCKFILE_VERSION_SUFFIX, @@ -14,13 +15,69 @@ export function isExperimentalInlineSpecifiersFormat ( } export function convertToInlineSpecifiersFormat (lockfile: Lockfile): InlineSpecifiersLockfile { + const convertedImporters = Object.fromEntries( + Object.entries(lockfile.importers ?? {}) + .map(([importerId, pkgSnapshot]: [string, ProjectSnapshot]) => { + const newSnapshot = { ...pkgSnapshot } + if (newSnapshot.dependencies != null) { + newSnapshot.dependencies = mapValues(newSnapshot.dependencies, convertOldRefToNewRef) + } + if (newSnapshot.optionalDependencies != null) { + newSnapshot.optionalDependencies = mapValues(newSnapshot.optionalDependencies, convertOldRefToNewRef) + } + if (newSnapshot.devDependencies != null) { + newSnapshot.devDependencies = mapValues(newSnapshot.devDependencies, convertOldRefToNewRef) + } + return [importerId, newSnapshot] + }) + ) return { ...lockfile, + packages: Object.fromEntries( + Object.entries(lockfile.packages ?? {}) + .map(([depPath, pkgSnapshot]) => { + const newSnapshot = { ...pkgSnapshot } + if (newSnapshot.dependencies != null) { + newSnapshot.dependencies = mapValues(newSnapshot.dependencies, convertOldRefToNewRef) + } + if (newSnapshot.optionalDependencies != null) { + newSnapshot.optionalDependencies = mapValues(newSnapshot.optionalDependencies, convertOldRefToNewRef) + } + return [convertOldDepPathToNewDepPath(depPath), newSnapshot] + }) + ), lockfileVersion: `${lockfile.lockfileVersion}${INLINE_SPECIFIERS_FORMAT_LOCKFILE_VERSION_SUFFIX}`, - importers: mapValues(lockfile.importers, convertProjectSnapshotToInlineSpecifiersFormat), + importers: mapValues(convertedImporters, convertProjectSnapshotToInlineSpecifiersFormat), } } +function convertOldDepPathToNewDepPath (oldDepPath: string) { + const parsedDepPath = dp.parse(oldDepPath) + if (!parsedDepPath.name || !parsedDepPath.version) return oldDepPath + let newDepPath = `/${parsedDepPath.name}@${parsedDepPath.version}` + if (parsedDepPath.peersSuffix) { + if (parsedDepPath.peersSuffix.startsWith('(')) { + newDepPath += parsedDepPath.peersSuffix + } else { + newDepPath += `_${parsedDepPath.peersSuffix}` + } + } + if (parsedDepPath.host) { + newDepPath = `${parsedDepPath.host}${newDepPath}` + } + return newDepPath +} + +function convertOldRefToNewRef (oldRef: string) { + if (oldRef.startsWith('link:') || oldRef.startsWith('file:')) { + return oldRef + } + if (oldRef.includes('/')) { + return convertOldDepPathToNewDepPath(oldRef) + } + return oldRef +} + export function revertFromInlineSpecifiersFormatIfNecessary (lockfile: Lockfile | InlineSpecifiersLockfile): Lockfile { return isExperimentalInlineSpecifiersFormat(lockfile) ? revertFromInlineSpecifiersFormat(lockfile) @@ -36,11 +93,57 @@ export function revertFromInlineSpecifiersFormat (lockfile: InlineSpecifiersLock throw new Error(`Unable to revert lockfile from inline specifiers format. Invalid version parsed: ${originalVersionStr}`) } + const revertedImporters = mapValues(importers, revertProjectSnapshot) + const convertedImporters = Object.fromEntries( + Object.entries(revertedImporters ?? {}) + .map(([importerId, pkgSnapshot]: [string, ProjectSnapshot]) => { + const newSnapshot = { ...pkgSnapshot } + if (newSnapshot.dependencies != null) { + newSnapshot.dependencies = mapValues(newSnapshot.dependencies, convertNewRefToOldRef) + } + if (newSnapshot.optionalDependencies != null) { + newSnapshot.optionalDependencies = mapValues(newSnapshot.optionalDependencies, convertNewRefToOldRef) + } + if (newSnapshot.devDependencies != null) { + newSnapshot.devDependencies = mapValues(newSnapshot.devDependencies, convertNewRefToOldRef) + } + return [importerId, newSnapshot] + }) + ) return { ...rest, + packages: Object.fromEntries( + Object.entries(lockfile.packages ?? {}) + .map(([depPath, pkgSnapshot]) => { + const newSnapshot = { ...pkgSnapshot } + if (newSnapshot.dependencies != null) { + newSnapshot.dependencies = mapValues(newSnapshot.dependencies, convertNewRefToOldRef) + } + if (newSnapshot.optionalDependencies != null) { + newSnapshot.optionalDependencies = mapValues(newSnapshot.optionalDependencies, convertNewRefToOldRef) + } + return [convertNewDepPathToOldDepPath(depPath), newSnapshot] + }) + ), lockfileVersion: originalVersion, - importers: mapValues(importers, revertProjectSnapshot), + importers: convertedImporters, + } +} + +function convertNewDepPathToOldDepPath (oldDepPath: string) { + if (!oldDepPath.includes('@', 1)) return oldDepPath + const index = oldDepPath.indexOf('@', oldDepPath.indexOf('/@') + 2) + return `${oldDepPath.substring(0, index)}/${oldDepPath.substring(index + 1)}` +} + +function convertNewRefToOldRef (oldRef: string) { + if (oldRef.startsWith('link:') || oldRef.startsWith('file:')) { + return oldRef + } + if (oldRef.includes('@')) { + return convertNewDepPathToOldDepPath(oldRef) } + return oldRef } function convertProjectSnapshotToInlineSpecifiersFormat ( diff --git a/lockfile/lockfile-file/test/__snapshots__/write.test.ts.snap b/lockfile/lockfile-file/test/__snapshots__/write.test.ts.snap index 56c1d1c2721..ae11ef0054e 100644 --- a/lockfile/lockfile-file/test/__snapshots__/write.test.ts.snap +++ b/lockfile/lockfile-file/test/__snapshots__/write.test.ts.snap @@ -40,7 +40,7 @@ dependencies: packages: - /foo/1.0.0: + /foo@1.0.0: resolution: {integrity: sha1-ChbBDewTLAqLCzb793Fo5VDvg/g=} " `; diff --git a/lockfile/lockfile-file/test/lockfileV6Converters.test.ts b/lockfile/lockfile-file/test/lockfileV6Converters.test.ts new file mode 100644 index 00000000000..64bc86640a7 --- /dev/null +++ b/lockfile/lockfile-file/test/lockfileV6Converters.test.ts @@ -0,0 +1,86 @@ +import { convertToInlineSpecifiersFormat, revertFromInlineSpecifiersFormat } from '../lib/experiments/inlineSpecifiersLockfileConverters' + +test('convertToInlineSpecifiersFormat()', () => { + const lockfileV5 = { + lockfileVersion: 5.0, + importers: { + project1: { + specifiers: { + foo: '^1.0.0', + bar: '^1.0.0', + qar: '^1.0.0', + tarball: '^1.0.0', + }, + dependencies: { + foo: '1.0.0', + tarball: '@registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz', + }, + devDependencies: { + bar: '/@bar/bar/1.0.0_@babel+core@2.0.0', + }, + optionalDependencies: { + qar: 'reg.com/qar/1.0.0', + }, + }, + }, + packages: { + '/foo/1.0.0': { + resolution: { integrity: '' }, + }, + '/@bar/bar/1.0.0_@babel+core@2.0.0': { + resolution: { integrity: '' }, + }, + 'reg.com/qar/1.0.0': { + resolution: { integrity: '' }, + }, + '@registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz': { + resolution: { integrity: '' }, + }, + }, + } + const lockfileV6 = { + lockfileVersion: '5-inlineSpecifiers', + importers: { + project1: { + dependencies: { + foo: { + specifier: '^1.0.0', + version: '1.0.0', + }, + tarball: { + specifier: '^1.0.0', + version: '@registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz', + }, + }, + devDependencies: { + bar: { + specifier: '^1.0.0', + version: '/@bar/bar@1.0.0_@babel+core@2.0.0', + }, + }, + optionalDependencies: { + qar: { + specifier: '^1.0.0', + version: 'reg.com/qar@1.0.0', + }, + }, + }, + }, + packages: { + '/foo@1.0.0': { + resolution: { integrity: '' }, + }, + '/@bar/bar@1.0.0_@babel+core@2.0.0': { + resolution: { integrity: '' }, + }, + 'reg.com/qar@1.0.0': { + resolution: { integrity: '' }, + }, + '@registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz': { + resolution: { integrity: '' }, + }, + }, + } + expect(convertToInlineSpecifiersFormat(lockfileV5)).toEqual(lockfileV6) + expect(revertFromInlineSpecifiersFormat(lockfileV6)).toEqual(lockfileV5) +}) diff --git a/packages/dependency-path/src/index.ts b/packages/dependency-path/src/index.ts index 97c38e60a2c..4e88829be5a 100644 --- a/packages/dependency-path/src/index.ts +++ b/packages/dependency-path/src/index.ts @@ -155,7 +155,7 @@ function depPathToFilenameUnescaped (depPath: string) { if (depPath.startsWith('/')) { depPath = depPath.substring(1) } - const index = depPath.lastIndexOf('/') + const index = depPath.lastIndexOf('/', depPath.lastIndexOf('(') - 1) return `${depPath.substring(0, index)}@${depPath.slice(index + 1)}` } return depPath.replace(':', '+')