Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 398472c

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

File tree

Expand file treeCollapse file tree

3 files changed

+67
-20
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+67
-20
lines changed

‎.changeset/slimy-geese-knock.md

Copy file name to clipboard
+6Lines 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

Copy file name to clipboardExpand all lines: pkg-manifest/exportable-manifest/src/index.ts
+53-20Lines 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

Copy file name to clipboardExpand all lines: pkg-manifest/exportable-manifest/test/index.test.ts
+8Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.