Skip to content

Commit 398472c

Browse files
authored
feat: handle workspace protocol with any semver range specifier (#7633)
close #7578
1 parent 5d1ed94 commit 398472c

File tree

3 files changed

+67
-20
lines changed

3 files changed

+67
-20
lines changed

.changeset/slimy-geese-knock.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@pnpm/exportable-manifest": minor
3+
"pnpm": patch
4+
---
5+
6+
Handle workspace protocol with any semver range specifier, when used in peer dependencies [#7578](https://github.com/pnpm/pnpm/issues/7578).

pkg-manifest/exportable-manifest/src/index.ts

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ export async function createExportableManifest (
2929
if (originalManifest.scripts != null) {
3030
publishManifest.scripts = omit(PREPUBLISH_SCRIPTS, originalManifest.scripts)
3131
}
32-
await Promise.all((['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const).map(async (depsField) => {
33-
const deps = await makePublishDependencies(dir, originalManifest[depsField], opts?.modulesDir)
32+
await Promise.all((['dependencies', 'devDependencies', 'optionalDependencies'] as const).map(async (depsField) => {
33+
const deps = await makePublishDependencies(dir, originalManifest[depsField], { modulesDir: opts?.modulesDir })
3434
if (deps != null) {
3535
publishManifest[depsField] = deps
3636
}
3737
}))
3838

39+
const peerDependencies = originalManifest.peerDependencies
40+
if (peerDependencies) {
41+
publishManifest.peerDependencies = await makePublishDependencies(dir, peerDependencies, { modulesDir: opts?.modulesDir, convertDependencyForPublish: makePublishPeerDependency })
42+
}
43+
3944
overridePublishConfig(publishManifest)
4045

4146
if (opts?.readmeFile) {
@@ -48,16 +53,32 @@ export async function createExportableManifest (
4853
async function makePublishDependencies (
4954
dir: string,
5055
dependencies: Dependencies | undefined,
51-
modulesDir?: string
56+
{ modulesDir, convertDependencyForPublish = makePublishDependency }: {
57+
modulesDir?: string
58+
convertDependencyForPublish?: (depName: string, depSpec: string, dir: string, modulesDir?: string) => Promise<string>
59+
} = {}
5260
): Promise<Dependencies | undefined> {
5361
if (dependencies == null) return dependencies
5462
const publishDependencies = await pMapValues(
55-
(depSpec, depName) => makePublishDependency(depName, depSpec, dir, modulesDir),
63+
(depSpec, depName) => convertDependencyForPublish(depName, depSpec, dir, modulesDir),
5664
dependencies
5765
)
5866
return publishDependencies
5967
}
6068

69+
async function resolveManifest (depName: string, modulesDir: string): Promise<ProjectManifest> {
70+
const { manifest } = await tryReadProjectManifest(path.join(modulesDir, depName))
71+
if (!manifest?.name || !manifest?.version) {
72+
throw new PnpmError(
73+
'CANNOT_RESOLVE_WORKSPACE_PROTOCOL',
74+
`Cannot resolve workspace protocol of dependency "${depName}" ` +
75+
'because this dependency is not installed. Try running "pnpm install".'
76+
)
77+
}
78+
79+
return manifest
80+
}
81+
6182
async function makePublishDependency (depName: string, depSpec: string, dir: string, modulesDir?: string): Promise<string> {
6283
if (!depSpec.startsWith('workspace:')) {
6384
return depSpec
@@ -67,14 +88,7 @@ async function makePublishDependency (depName: string, depSpec: string, dir: str
6788
const versionAliasSpecParts = /^workspace:(.*?)@?([\^~*])$/.exec(depSpec)
6889
if (versionAliasSpecParts != null) {
6990
modulesDir = modulesDir ?? path.join(dir, 'node_modules')
70-
const { manifest } = await tryReadProjectManifest(path.join(modulesDir, depName))
71-
if (!manifest?.version) {
72-
throw new PnpmError(
73-
'CANNOT_RESOLVE_WORKSPACE_PROTOCOL',
74-
`Cannot resolve workspace protocol of dependency "${depName}" ` +
75-
'because this dependency is not installed. Try running "pnpm install".'
76-
)
77-
}
91+
const manifest = await resolveManifest(depName, modulesDir)
7892

7993
const semverRangeToken = versionAliasSpecParts[2] !== '*' ? versionAliasSpecParts[2] : ''
8094
if (depName !== manifest.name) {
@@ -83,14 +97,8 @@ async function makePublishDependency (depName: string, depSpec: string, dir: str
8397
return `${semverRangeToken}${manifest.version}`
8498
}
8599
if (depSpec.startsWith('workspace:./') || depSpec.startsWith('workspace:../')) {
86-
const { manifest } = await tryReadProjectManifest(path.join(dir, depSpec.slice(10)))
87-
if (!manifest?.name || !manifest?.version) {
88-
throw new PnpmError(
89-
'CANNOT_RESOLVE_WORKSPACE_PROTOCOL',
90-
`Cannot resolve workspace protocol of dependency "${depName}" ` +
91-
'because this dependency is not installed. Try running "pnpm install".'
92-
)
93-
}
100+
const manifest = await resolveManifest(depName, path.join(dir, depSpec.slice(10)))
101+
94102
if (manifest.name === depName) return `${manifest.version}`
95103
return `npm:${manifest.name}@${manifest.version}`
96104
}
@@ -100,3 +108,28 @@ async function makePublishDependency (depName: string, depSpec: string, dir: str
100108
}
101109
return depSpec
102110
}
111+
112+
async function makePublishPeerDependency (depName: string, depSpec: string, dir: string, modulesDir?: string) {
113+
if (!depSpec.includes('workspace:')) {
114+
return depSpec
115+
}
116+
117+
// Dependencies with bare "*", "^", "~",">=",">","<=",< versions
118+
const workspaceSemverRegex = /workspace:([\^~*]|>=|>|<=|<)/
119+
const versionAliasSpecParts = workspaceSemverRegex.exec(depSpec)
120+
121+
if (versionAliasSpecParts != null) {
122+
modulesDir = modulesDir ?? path.join(dir, 'node_modules')
123+
const manifest = await resolveManifest(depName, modulesDir)
124+
125+
const [,semverRangGroup] = versionAliasSpecParts
126+
127+
const semverRangeToken = semverRangGroup !== '*' ? semverRangGroup : ''
128+
129+
return depSpec.replace(workspaceSemverRegex, `${semverRangeToken}${manifest.version}`)
130+
}
131+
132+
depSpec = depSpec.replace('workspace:', '')
133+
134+
return depSpec
135+
}

pkg-manifest/exportable-manifest/test/index.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ test('workspace deps are replaced', async () => {
9191
baz: 'workspace:baz@^',
9292
foo: 'workspace:*',
9393
},
94+
peerDependencies: {
95+
foo: 'workspace:>= || ^3.9.0',
96+
baz: '^1.0.0 || workspace:>',
97+
},
9498
}
9599

96100
preparePackages([
@@ -123,5 +127,9 @@ test('workspace deps are replaced', async () => {
123127
baz: '^1.2.3',
124128
foo: '4.5.6',
125129
},
130+
peerDependencies: {
131+
baz: '^1.0.0 || >1.2.3',
132+
foo: '>=4.5.6 || ^3.9.0',
133+
},
126134
})
127135
})

0 commit comments

Comments
 (0)