From 5087636b66a0653421ed76f45d98710ee4574f7f Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Tue, 4 Apr 2023 00:18:43 +0300 Subject: [PATCH] fix: installation of packages with () in scope name (#6349) close #6348 --- .changeset/healthy-goats-tell.md | 6 ++++++ .../inlineSpecifiersLockfileConverters.ts | 18 ++++++++++-------- packages/dependency-path/test/index.ts | 8 ++++++++ pkg-manager/core/test/lockfile.ts | 7 +++++++ 4 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 .changeset/healthy-goats-tell.md diff --git a/.changeset/healthy-goats-tell.md b/.changeset/healthy-goats-tell.md new file mode 100644 index 00000000000..ae90943736a --- /dev/null +++ b/.changeset/healthy-goats-tell.md @@ -0,0 +1,6 @@ +--- +"@pnpm/lockfile-file": patch +"pnpm": patch +--- + +Repeat installation should work on a project that has a dependency with () chars in the scope name [#6348](https://github.com/pnpm/pnpm/issues/6348). diff --git a/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts b/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts index 3bba90bcdfa..b38e1a1b0a0 100644 --- a/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts +++ b/lockfile/lockfile-file/src/experiments/inlineSpecifiersLockfileConverters.ts @@ -140,7 +140,7 @@ export function revertFromInlineSpecifiersFormat (lockfile: InlineSpecifiersLock if (newSnapshot.optionalDependencies != null) { newSnapshot.optionalDependencies = mapValues(newSnapshot.optionalDependencies, convertNewRefToOldRef) } - return [convertNewDepPathToOldDepPath(depPath), newSnapshot] + return [convertLockfileV6DepPathToV5DepPath(depPath), newSnapshot] }) ) } @@ -153,17 +153,19 @@ export function revertFromInlineSpecifiersFormat (lockfile: InlineSpecifiersLock if (originalVersion === 6 && newLockfile.time) { newLockfile.time = Object.fromEntries( Object.entries(newLockfile.time) - .map(([depPath, time]) => [convertNewDepPathToOldDepPath(depPath), time]) + .map(([depPath, time]) => [convertLockfileV6DepPathToV5DepPath(depPath), time]) ) } return newLockfile } -export function convertNewDepPathToOldDepPath (oldDepPath: string) { - if (!oldDepPath.includes('@', 2)) return oldDepPath - const index = oldDepPath.indexOf('@', oldDepPath.indexOf('/@') + 2) - if (oldDepPath.includes('(') && index > oldDepPath.indexOf('(')) return oldDepPath - return `${oldDepPath.substring(0, index)}/${oldDepPath.substring(index + 1)}` +const PEERS_SUFFIX_REGEX = /(\([^)]+\))+$/ + +export function convertLockfileV6DepPathToV5DepPath (newDepPath: string) { + if (!newDepPath.includes('@', 2)) return newDepPath + const index = newDepPath.indexOf('@', newDepPath.indexOf('/@') + 2) + if (newDepPath.includes('(') && index > newDepPath.search(PEERS_SUFFIX_REGEX)) return newDepPath + return `${newDepPath.substring(0, index)}/${newDepPath.substring(index + 1)}` } function convertNewRefToOldRef (oldRef: string) { @@ -171,7 +173,7 @@ function convertNewRefToOldRef (oldRef: string) { return oldRef } if (oldRef.includes('@')) { - return convertNewDepPathToOldDepPath(oldRef) + return convertLockfileV6DepPathToV5DepPath(oldRef) } return oldRef } diff --git a/packages/dependency-path/test/index.ts b/packages/dependency-path/test/index.ts index ef1aa3de687..9f0a196e1fb 100644 --- a/packages/dependency-path/test/index.ts +++ b/packages/dependency-path/test/index.ts @@ -98,6 +98,14 @@ test('parse()', () => { version: '1.0.0', }) + expect(parse('/@(-.-)/foo/1.0.0(@types/babel__core@7.1.14)(foo@1.0.0)')).toStrictEqual({ + host: undefined, + isAbsolute: false, + name: '@(-.-)/foo', + peersSuffix: '(@types/babel__core@7.1.14)(foo@1.0.0)', + version: '1.0.0', + }) + expect(() => parse('/foo/bar')).toThrow(/\/foo\/bar is an invalid relative dependency path/) expect(parse('file:project(foo@1.0.0)')).toStrictEqual({ diff --git a/pkg-manager/core/test/lockfile.ts b/pkg-manager/core/test/lockfile.ts index d3209b6921d..15da8aede62 100644 --- a/pkg-manager/core/test/lockfile.ts +++ b/pkg-manager/core/test/lockfile.ts @@ -1596,3 +1596,10 @@ test('lockfile is not written when it has no changes', async () => { await install(manifest, await testDefaults()) expect(await fs.stat(WANTED_LOCKFILE)).toHaveProperty('mtimeMs', initialMtime) }) + +test('installation should work with packages that have () in the scope name', async () => { + prepareEmpty() + const opts = await testDefaults() + const manifest = await addDependenciesToPackage({}, ['@(-.-)/env@0.3.1'], opts) + await install(manifest, opts) +})