mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-24 16:46:06 -04:00
feat: handle workspace protocol with any semver range specifier (#7633)
close #7578
This commit is contained in:
6
.changeset/slimy-geese-knock.md
Normal file
6
.changeset/slimy-geese-knock.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/exportable-manifest": minor
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Handle workspace protocol with any semver range specifier, when used in peer dependencies [#7578](https://github.com/pnpm/pnpm/issues/7578).
|
||||
@@ -29,13 +29,18 @@ export async function createExportableManifest (
|
||||
if (originalManifest.scripts != null) {
|
||||
publishManifest.scripts = omit(PREPUBLISH_SCRIPTS, originalManifest.scripts)
|
||||
}
|
||||
await Promise.all((['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const).map(async (depsField) => {
|
||||
const deps = await makePublishDependencies(dir, originalManifest[depsField], opts?.modulesDir)
|
||||
await Promise.all((['dependencies', 'devDependencies', 'optionalDependencies'] as const).map(async (depsField) => {
|
||||
const deps = await makePublishDependencies(dir, originalManifest[depsField], { modulesDir: opts?.modulesDir })
|
||||
if (deps != null) {
|
||||
publishManifest[depsField] = deps
|
||||
}
|
||||
}))
|
||||
|
||||
const peerDependencies = originalManifest.peerDependencies
|
||||
if (peerDependencies) {
|
||||
publishManifest.peerDependencies = await makePublishDependencies(dir, peerDependencies, { modulesDir: opts?.modulesDir, convertDependencyForPublish: makePublishPeerDependency })
|
||||
}
|
||||
|
||||
overridePublishConfig(publishManifest)
|
||||
|
||||
if (opts?.readmeFile) {
|
||||
@@ -48,16 +53,32 @@ export async function createExportableManifest (
|
||||
async function makePublishDependencies (
|
||||
dir: string,
|
||||
dependencies: Dependencies | undefined,
|
||||
modulesDir?: string
|
||||
{ modulesDir, convertDependencyForPublish = makePublishDependency }: {
|
||||
modulesDir?: string
|
||||
convertDependencyForPublish?: (depName: string, depSpec: string, dir: string, modulesDir?: string) => Promise<string>
|
||||
} = {}
|
||||
): Promise<Dependencies | undefined> {
|
||||
if (dependencies == null) return dependencies
|
||||
const publishDependencies = await pMapValues(
|
||||
(depSpec, depName) => makePublishDependency(depName, depSpec, dir, modulesDir),
|
||||
(depSpec, depName) => convertDependencyForPublish(depName, depSpec, dir, modulesDir),
|
||||
dependencies
|
||||
)
|
||||
return publishDependencies
|
||||
}
|
||||
|
||||
async function resolveManifest (depName: string, modulesDir: string): Promise<ProjectManifest> {
|
||||
const { manifest } = await tryReadProjectManifest(path.join(modulesDir, depName))
|
||||
if (!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".'
|
||||
)
|
||||
}
|
||||
|
||||
return manifest
|
||||
}
|
||||
|
||||
async function makePublishDependency (depName: string, depSpec: string, dir: string, modulesDir?: string): Promise<string> {
|
||||
if (!depSpec.startsWith('workspace:')) {
|
||||
return depSpec
|
||||
@@ -67,14 +88,7 @@ async function makePublishDependency (depName: string, depSpec: string, dir: str
|
||||
const versionAliasSpecParts = /^workspace:(.*?)@?([\^~*])$/.exec(depSpec)
|
||||
if (versionAliasSpecParts != null) {
|
||||
modulesDir = modulesDir ?? path.join(dir, 'node_modules')
|
||||
const { manifest } = await tryReadProjectManifest(path.join(modulesDir, depName))
|
||||
if (!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".'
|
||||
)
|
||||
}
|
||||
const manifest = await resolveManifest(depName, modulesDir)
|
||||
|
||||
const semverRangeToken = versionAliasSpecParts[2] !== '*' ? versionAliasSpecParts[2] : ''
|
||||
if (depName !== manifest.name) {
|
||||
@@ -83,14 +97,8 @@ async function makePublishDependency (depName: string, depSpec: string, dir: str
|
||||
return `${semverRangeToken}${manifest.version}`
|
||||
}
|
||||
if (depSpec.startsWith('workspace:./') || depSpec.startsWith('workspace:../')) {
|
||||
const { manifest } = await tryReadProjectManifest(path.join(dir, depSpec.slice(10)))
|
||||
if (!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".'
|
||||
)
|
||||
}
|
||||
const manifest = await resolveManifest(depName, path.join(dir, depSpec.slice(10)))
|
||||
|
||||
if (manifest.name === depName) return `${manifest.version}`
|
||||
return `npm:${manifest.name}@${manifest.version}`
|
||||
}
|
||||
@@ -100,3 +108,28 @@ async function makePublishDependency (depName: string, depSpec: string, dir: str
|
||||
}
|
||||
return depSpec
|
||||
}
|
||||
|
||||
async function makePublishPeerDependency (depName: string, depSpec: string, dir: string, modulesDir?: string) {
|
||||
if (!depSpec.includes('workspace:')) {
|
||||
return depSpec
|
||||
}
|
||||
|
||||
// Dependencies with bare "*", "^", "~",">=",">","<=",< versions
|
||||
const workspaceSemverRegex = /workspace:([\^~*]|>=|>|<=|<)/
|
||||
const versionAliasSpecParts = workspaceSemverRegex.exec(depSpec)
|
||||
|
||||
if (versionAliasSpecParts != null) {
|
||||
modulesDir = modulesDir ?? path.join(dir, 'node_modules')
|
||||
const manifest = await resolveManifest(depName, modulesDir)
|
||||
|
||||
const [,semverRangGroup] = versionAliasSpecParts
|
||||
|
||||
const semverRangeToken = semverRangGroup !== '*' ? semverRangGroup : ''
|
||||
|
||||
return depSpec.replace(workspaceSemverRegex, `${semverRangeToken}${manifest.version}`)
|
||||
}
|
||||
|
||||
depSpec = depSpec.replace('workspace:', '')
|
||||
|
||||
return depSpec
|
||||
}
|
||||
|
||||
@@ -91,6 +91,10 @@ test('workspace deps are replaced', async () => {
|
||||
baz: 'workspace:baz@^',
|
||||
foo: 'workspace:*',
|
||||
},
|
||||
peerDependencies: {
|
||||
foo: 'workspace:>= || ^3.9.0',
|
||||
baz: '^1.0.0 || workspace:>',
|
||||
},
|
||||
}
|
||||
|
||||
preparePackages([
|
||||
@@ -123,5 +127,9 @@ test('workspace deps are replaced', async () => {
|
||||
baz: '^1.2.3',
|
||||
foo: '4.5.6',
|
||||
},
|
||||
peerDependencies: {
|
||||
baz: '^1.0.0 || >1.2.3',
|
||||
foo: '>=4.5.6 || ^3.9.0',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user