diff --git a/.changeset/clever-rules-taste.md b/.changeset/clever-rules-taste.md new file mode 100644 index 0000000000..21ef9e4cd1 --- /dev/null +++ b/.changeset/clever-rules-taste.md @@ -0,0 +1,5 @@ +--- +"@pnpm/npm-resolver": patch +--- + +It should be possible to alias scoped packages using the `workspace:` protocol. See https://github.com/pnpm/pnpm/issues/3883 diff --git a/packages/npm-resolver/src/index.ts b/packages/npm-resolver/src/index.ts index 403fa50889..c3b843fccc 100644 --- a/packages/npm-resolver/src/index.ts +++ b/packages/npm-resolver/src/index.ts @@ -29,6 +29,7 @@ import parsePref, { } from './parsePref' import fromRegistry, { RegistryResponseError } from './fetch' import createPkgId from './createNpmPkgId' +import workspacePrefToNpm from './workspacePrefToNpm' export class NoMatchingVersionError extends PnpmError { public readonly packageMeta: PackageMeta @@ -196,23 +197,6 @@ async function resolveNpm ( } } -function workspacePrefToNpm (workspacePref: string): string { - const prefParts = /^workspace:([^@]+@)?(.*)$/.exec(workspacePref) - if (prefParts == null) { - throw new Error(`Invalid workspace spec: ${workspacePref}`) - } - const [workspacePkgAlias, workspaceVersion] = prefParts.slice(1) - - const pkgAliasPart = workspacePkgAlias != null && workspacePkgAlias - ? `npm:${workspacePkgAlias}` - : '' - const versionPart = workspaceVersion === '^' || workspaceVersion === '~' - ? '*' - : workspaceVersion - - return `${pkgAliasPart}${versionPart}` -} - function tryResolveFromWorkspace ( wantedDependency: WantedDependency, opts: { diff --git a/packages/npm-resolver/src/workspacePrefToNpm.ts b/packages/npm-resolver/src/workspacePrefToNpm.ts new file mode 100644 index 0000000000..6939d0272d --- /dev/null +++ b/packages/npm-resolver/src/workspacePrefToNpm.ts @@ -0,0 +1,17 @@ +export default function workspacePrefToNpm (workspacePref: string): string { + const prefParts = /^workspace:([^._/][^@]*@)?(.*)$/.exec(workspacePref) + + if (prefParts == null) { + throw new Error(`Invalid workspace spec: ${workspacePref}`) + } + const [workspacePkgAlias, workspaceVersion] = prefParts.slice(1) + + const pkgAliasPart = workspacePkgAlias != null && workspacePkgAlias + ? `npm:${workspacePkgAlias}` + : '' + const versionPart = workspaceVersion === '^' || workspaceVersion === '~' + ? '*' + : workspaceVersion + + return `${pkgAliasPart}${versionPart}` +} diff --git a/packages/npm-resolver/test/index.ts b/packages/npm-resolver/test/index.ts index 13f70412aa..7b7068436d 100644 --- a/packages/npm-resolver/test/index.ts +++ b/packages/npm-resolver/test/index.ts @@ -1589,70 +1589,6 @@ test('request to a package with malformed metadata', async () => { ) }) -test('resolve workspace:^', async () => { - const cacheDir = tempy.directory() - const resolve = createResolveFromNpm({ - cacheDir, - }) - const resolveResult = await resolve({ alias: 'is-positive', pref: 'workspace:^' }, { - projectDir: '/home/istvan/src', - registry, - workspacePackages: { - 'is-positive': { - '1.0.0': { - dir: '/home/istvan/src/is-positive', - manifest: { - name: 'is-positive', - version: '1.0.0', - }, - }, - }, - }, - }) - - expect(resolveResult!.resolvedVia).toBe('local-filesystem') - expect(resolveResult!.id).toBe('link:is-positive') - expect(resolveResult!.resolution).toStrictEqual({ - directory: '/home/istvan/src/is-positive', - type: 'directory', - }) - expect(resolveResult!.manifest).toBeTruthy() - expect(resolveResult!.manifest!.name).toBe('is-positive') - expect(resolveResult!.manifest!.version).toBe('1.0.0') -}) - -test('resolve workspace:~', async () => { - const cacheDir = tempy.directory() - const resolve = createResolveFromNpm({ - cacheDir, - }) - const resolveResult = await resolve({ alias: 'is-positive', pref: 'workspace:~' }, { - projectDir: '/home/istvan/src', - registry, - workspacePackages: { - 'is-positive': { - '1.0.0': { - dir: '/home/istvan/src/is-positive', - manifest: { - name: 'is-positive', - version: '1.0.0', - }, - }, - }, - }, - }) - - expect(resolveResult!.resolvedVia).toBe('local-filesystem') - expect(resolveResult!.id).toBe('link:is-positive') - expect(resolveResult!.resolution).toStrictEqual({ - directory: '/home/istvan/src/is-positive', - type: 'directory', - }) - expect(resolveResult!.manifest).toBeTruthy() - expect(resolveResult!.manifest!.name).toBe('is-positive') - expect(resolveResult!.manifest!.version).toBe('1.0.0') -}) - test('resolveFromNpm() does not fail if the meta file contains no integrity information', async () => { nock(registry) .get('/is-positive') diff --git a/packages/npm-resolver/test/workspacePrefToNpm.test.ts b/packages/npm-resolver/test/workspacePrefToNpm.test.ts new file mode 100644 index 0000000000..976e0cb98e --- /dev/null +++ b/packages/npm-resolver/test/workspacePrefToNpm.test.ts @@ -0,0 +1,29 @@ +import workspacePrefToNpm from '@pnpm/npm-resolver/lib/workspacePrefToNpm' + +describe('workspacePrefToNpm', () => { + test('resolve workspace only version aliases', async () => { + expect(workspacePrefToNpm('workspace:^')).toStrictEqual('*') + expect(workspacePrefToNpm('workspace:~')).toStrictEqual('*') + }) + + test('resolve package name aliases', async () => { + expect(workspacePrefToNpm('workspace:is-positive@3.0.0')).toStrictEqual('npm:is-positive@3.0.0') + expect(workspacePrefToNpm('workspace:is-positive@*')).toStrictEqual('npm:is-positive@*') + expect(workspacePrefToNpm('workspace:is-positive@^')).toStrictEqual('npm:is-positive@*') + }) + + test('resolve scoped package name aliases', async () => { + expect( + workspacePrefToNpm('workspace:@scope/is-positive@1.2.3') + ).toStrictEqual('npm:@scope/is-positive@1.2.3') + expect( + workspacePrefToNpm('workspace:@scope/is-positive@^1.2.3') + ).toStrictEqual('npm:@scope/is-positive@^1.2.3') + expect( + workspacePrefToNpm('workspace:@scope/is-positive@*') + ).toStrictEqual('npm:@scope/is-positive@*') + expect( + workspacePrefToNpm('workspace:@scope/is-positive@~') + ).toStrictEqual('npm:@scope/is-positive@*') + }) +})