mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-31 13:32:18 -04:00
feat: specifying a path to workspace packages in version specs
close #2959 PR #2972
This commit is contained in:
5
.changeset/flat-socks-dance.md
Normal file
5
.changeset/flat-socks-dance.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/local-resolver": minor
|
||||
---
|
||||
|
||||
Support relative path to workspace directory.
|
||||
5
.changeset/mighty-kangaroos-brake.md
Normal file
5
.changeset/mighty-kangaroos-brake.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/exportable-manifest": minor
|
||||
---
|
||||
|
||||
Convert relative workspace paths to version specs.
|
||||
5
.changeset/quiet-papayas-tie.md
Normal file
5
.changeset/quiet-papayas-tie.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/npm-resolver": minor
|
||||
---
|
||||
|
||||
Skip workspace protocol specs that use relative path.
|
||||
@@ -76,6 +76,18 @@ async function makePublishDependency (depName: string, depSpec: string, dir: str
|
||||
}
|
||||
return manifest.version
|
||||
}
|
||||
if (depSpec.startsWith('workspace:./') || depSpec.startsWith('workspace:../')) {
|
||||
const { manifest } = await tryReadProjectManifest(path.join(dir, depSpec.substr(10)))
|
||||
if (!manifest || !manifest.name || !manifest.version) {
|
||||
throw new PnpmError(
|
||||
'CANNOT_RESOLVE_WORKSPACE_PROTOCOL',
|
||||
`Cannot resolve workspace protocol of dependency "${depName}" ` +
|
||||
'because this dependency is not installed. Try running "pnpm install".'
|
||||
)
|
||||
}
|
||||
if (manifest.name === depName) return `${manifest.version}`
|
||||
return `npm:${manifest.name}@${manifest.version}`
|
||||
}
|
||||
depSpec = depSpec.substr(10)
|
||||
if (depSpec.includes('@')) {
|
||||
return `npm:${depSpec}`
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function parsePref (
|
||||
projectDir: string,
|
||||
lockfileDir: string
|
||||
): LocalPackageSpec | null {
|
||||
if (pref.startsWith('link:')) {
|
||||
if (pref.startsWith('link:') || pref.startsWith('workspace:')) {
|
||||
return fromLocal(pref, projectDir, lockfileDir, 'directory')
|
||||
}
|
||||
if (pref.endsWith('.tgz') ||
|
||||
@@ -54,8 +54,8 @@ function fromLocal (
|
||||
type: 'file' | 'directory'
|
||||
): LocalPackageSpec {
|
||||
const spec = pref.replace(/\\/g, '/')
|
||||
.replace(/^(file|link):[/]*([A-Za-z]:)/, '$2') // drive name paths on windows
|
||||
.replace(/^(file|link):(?:[/]*([~./]))?/, '$2')
|
||||
.replace(/^(file|link|workspace):[/]*([A-Za-z]:)/, '$2') // drive name paths on windows
|
||||
.replace(/^(file|link|workspace):(?:[/]*([~./]))?/, '$2')
|
||||
|
||||
const protocol = type === 'directory' ? 'link:' : 'file:'
|
||||
let fetchSpec!: string
|
||||
|
||||
@@ -11,6 +11,15 @@ test('resolve directory', async () => {
|
||||
expect(resolveResult!.resolution['type']).toEqual('directory')
|
||||
})
|
||||
|
||||
test('resolve workspace directory', async () => {
|
||||
const resolveResult = await resolveFromLocal({ pref: 'workspace:..' }, { projectDir: __dirname })
|
||||
expect(resolveResult!.id).toEqual('link:..')
|
||||
expect(resolveResult!.normalizedPref).toEqual('link:..')
|
||||
expect(resolveResult!['manifest']!.name).toEqual('@pnpm/local-resolver')
|
||||
expect(resolveResult!.resolution['directory']).toEqual('..')
|
||||
expect(resolveResult!.resolution['type']).toEqual('directory')
|
||||
})
|
||||
|
||||
test('resolve directory specified using the file: protocol', async () => {
|
||||
const resolveResult = await resolveFromLocal({ pref: 'file:..' }, { projectDir: __dirname })
|
||||
expect(resolveResult!.id).toEqual('link:..')
|
||||
|
||||
@@ -114,14 +114,17 @@ async function resolveNpm (
|
||||
opts: ResolveFromNpmOptions
|
||||
): Promise<ResolveResult | null> {
|
||||
const defaultTag = opts.defaultTag ?? 'latest'
|
||||
const resolvedFromWorkspace = tryResolveFromWorkspace(wantedDependency, {
|
||||
defaultTag,
|
||||
projectDir: opts.projectDir,
|
||||
registry: opts.registry,
|
||||
workspacePackages: opts.workspacePackages,
|
||||
})
|
||||
if (resolvedFromWorkspace) {
|
||||
return resolvedFromWorkspace
|
||||
if (wantedDependency.pref?.startsWith('workspace:')) {
|
||||
if (wantedDependency.pref.startsWith('workspace:.')) return null
|
||||
const resolvedFromWorkspace = tryResolveFromWorkspace(wantedDependency, {
|
||||
defaultTag,
|
||||
projectDir: opts.projectDir,
|
||||
registry: opts.registry,
|
||||
workspacePackages: opts.workspacePackages,
|
||||
})
|
||||
if (resolvedFromWorkspace) {
|
||||
return resolvedFromWorkspace
|
||||
}
|
||||
}
|
||||
const workspacePackages = opts.alwaysTryWorkspacePackages !== false ? opts.workspacePackages : undefined
|
||||
const spec = wantedDependency.pref
|
||||
|
||||
@@ -73,6 +73,19 @@ test('resolveFromNpm()', async () => {
|
||||
expect(meta['dist-tags']).toBeTruthy()
|
||||
})
|
||||
|
||||
test('relative workspace protocol is skipped', async () => {
|
||||
const storeDir = tempy.directory()
|
||||
const resolve = createResolveFromNpm({
|
||||
storeDir,
|
||||
})
|
||||
const resolveResult = await resolve({ pref: 'workspace:../is-positive' }, {
|
||||
projectDir: '/home/istvan/src',
|
||||
registry,
|
||||
})
|
||||
|
||||
expect(resolveResult).toBe(null)
|
||||
})
|
||||
|
||||
test('dry run', async (done) => {
|
||||
nock(registry)
|
||||
.get('/is-positive')
|
||||
|
||||
@@ -444,6 +444,92 @@ because this dependency is not installed. Try running "pnpm install".'
|
||||
})
|
||||
})
|
||||
|
||||
test('convert specs with relative workspace protocols to regular version ranges', async () => {
|
||||
preparePackages(undefined, [
|
||||
{
|
||||
name: 'relative-workspace-protocol-package',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'file-type': 'workspace:../file-type',
|
||||
'is-neg': 'workspace:../is-negative',
|
||||
'is-positive': '1.0.0',
|
||||
'lodash.delay': '~4.1.0',
|
||||
},
|
||||
devDependencies: {
|
||||
'random-package': 'workspace:../random-package',
|
||||
},
|
||||
optionalDependencies: {
|
||||
'lodash.deburr': 'workspace:../lodash.deburr',
|
||||
},
|
||||
peerDependencies: {
|
||||
'random-package': 'workspace:../random-package',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'is-negative',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
name: 'file-type',
|
||||
version: '12.0.1',
|
||||
},
|
||||
{
|
||||
name: 'lodash.deburr',
|
||||
version: '4.1.0',
|
||||
},
|
||||
{
|
||||
name: 'lodash.delay',
|
||||
version: '4.1.0',
|
||||
},
|
||||
{
|
||||
name: 'random-package',
|
||||
version: '1.2.3',
|
||||
},
|
||||
{
|
||||
name: 'target',
|
||||
version: '1.0.0',
|
||||
},
|
||||
])
|
||||
|
||||
await writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
|
||||
|
||||
process.chdir('relative-workspace-protocol-package')
|
||||
|
||||
await publish.handler({
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
}, [])
|
||||
|
||||
process.chdir('../target')
|
||||
|
||||
crossSpawn.sync(pnpmBin, [
|
||||
'add',
|
||||
'--store-dir=../store',
|
||||
'relative-workspace-protocol-package',
|
||||
'--no-link-workspace-packages',
|
||||
`--registry=http://localhost:${REGISTRY_MOCK_PORT}`,
|
||||
])
|
||||
|
||||
const { default: publishedManifest } = await import(path.resolve('node_modules/relative-workspace-protocol-package/package.json'))
|
||||
expect(publishedManifest.dependencies).toStrictEqual({
|
||||
'file-type': '12.0.1',
|
||||
'is-neg': 'npm:is-negative@1.0.0',
|
||||
'is-positive': '1.0.0',
|
||||
'lodash.delay': '~4.1.0',
|
||||
})
|
||||
expect(publishedManifest.devDependencies).toStrictEqual({
|
||||
'random-package': '1.2.3',
|
||||
})
|
||||
expect(publishedManifest.optionalDependencies).toStrictEqual({
|
||||
'lodash.deburr': '4.1.0',
|
||||
})
|
||||
expect(publishedManifest.peerDependencies).toStrictEqual({
|
||||
'random-package': '1.2.3',
|
||||
})
|
||||
})
|
||||
|
||||
test('publish: runs all the lifecycle scripts', async () => {
|
||||
prepare(undefined, {
|
||||
name: 'test-publish-with-scripts',
|
||||
|
||||
Reference in New Issue
Block a user