mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-31 05:22:00 -04:00
5
.changeset/lucky-moons-drum.md
Normal file
5
.changeset/lucky-moons-drum.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/workspace.spec-parser": major
|
||||
---
|
||||
|
||||
Initial release.
|
||||
7
.changeset/wicked-queens-bow.md
Normal file
7
.changeset/wicked-queens-bow.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@pnpm/resolve-dependencies": patch
|
||||
"@pnpm/npm-resolver": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
`pnpm update` should not fail when there's an aliased local workspace dependency [#7975](https://github.com/pnpm/pnpm/issues/7975).
|
||||
@@ -4,6 +4,7 @@ import { type Lockfile } from '@pnpm/lockfile-types'
|
||||
import { readModulesManifest } from '@pnpm/modules-yaml'
|
||||
import { install, update } from '@pnpm/plugin-commands-installation'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { readProjectManifestOnly } from '@pnpm/read-project-manifest'
|
||||
import { addDistTag } from '@pnpm/registry-mock'
|
||||
import { sync as readYamlFile } from 'read-yaml-file'
|
||||
import { DEFAULT_OPTS } from '../utils'
|
||||
@@ -415,3 +416,33 @@ test('recursive update in workspace should not add new dependencies', async () =
|
||||
projects['project-1'].hasNot('is-positive')
|
||||
projects['project-2'].hasNot('is-positive')
|
||||
})
|
||||
|
||||
test('recursive update with aliased workspace dependency (#7975)', async () => {
|
||||
const projects = preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
pkg: 'workspace:project-2@^',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
},
|
||||
])
|
||||
|
||||
await update.handler({
|
||||
...DEFAULT_OPTS,
|
||||
...await readProjects(process.cwd(), []),
|
||||
depth: 0,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
projects['project-1'].has('pkg')
|
||||
|
||||
const manifest = await readProjectManifestOnly('project-1')
|
||||
expect(manifest).toHaveProperty(['dependencies', 'pkg'], 'workspace:project-2@^')
|
||||
})
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"@pnpm/store-controller-types": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"@pnpm/which-version-is-pinned": "workspace:*",
|
||||
"@pnpm/workspace.spec-parser": "workspace:*",
|
||||
"@yarnpkg/core": "4.0.3",
|
||||
"filenamify": "^4.3.0",
|
||||
"get-npm-tarball-url": "^2.1.0",
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
type ProjectManifest,
|
||||
} from '@pnpm/types'
|
||||
import { whichVersionIsPinned } from '@pnpm/which-version-is-pinned'
|
||||
import { WorkspaceSpec } from '@pnpm/workspace.spec-parser'
|
||||
|
||||
export type PinnedVersion = 'major' | 'minor' | 'patch' | 'none'
|
||||
|
||||
@@ -54,7 +55,10 @@ export function getWantedDependencies (
|
||||
}
|
||||
|
||||
function updateWorkspacePref (pref: string): string {
|
||||
return pref.startsWith('workspace:') ? 'workspace:*' : pref
|
||||
const spec = WorkspaceSpec.parse(pref)
|
||||
if (!spec) return pref
|
||||
spec.version = '*'
|
||||
return spec.toString()
|
||||
}
|
||||
|
||||
function getWantedDependenciesFromGivenSet (
|
||||
|
||||
@@ -123,6 +123,7 @@ function resolvedDirectDepToSpecObject (
|
||||
shouldUseWorkspaceProtocol &&
|
||||
!pref.startsWith('workspace:')
|
||||
) {
|
||||
pref = pref.replace(/^npm:/, '')
|
||||
pref = `workspace:${pref}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../store/store-controller-types"
|
||||
},
|
||||
{
|
||||
"path": "../../workspace/spec-parser"
|
||||
}
|
||||
],
|
||||
"composite": true
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -4289,6 +4289,9 @@ importers:
|
||||
'@pnpm/which-version-is-pinned':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/which-version-is-pinned
|
||||
'@pnpm/workspace.spec-parser':
|
||||
specifier: workspace:*
|
||||
version: link:../../workspace/spec-parser
|
||||
'@yarnpkg/core':
|
||||
specifier: 4.0.3
|
||||
version: 4.0.3(typanion@3.14.0)
|
||||
@@ -5191,6 +5194,9 @@ importers:
|
||||
'@pnpm/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/types
|
||||
'@pnpm/workspace.spec-parser':
|
||||
specifier: workspace:*
|
||||
version: link:../../workspace/spec-parser
|
||||
'@zkochan/retry':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
@@ -6556,6 +6562,12 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
workspace/spec-parser:
|
||||
devDependencies:
|
||||
'@pnpm/workspace.spec-parser':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
packages:
|
||||
|
||||
'@aashutoshrathi/word-wrap@1.2.6':
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"@pnpm/resolve-workspace-range": "workspace:*",
|
||||
"@pnpm/resolver-base": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"@pnpm/workspace.spec-parser": "workspace:*",
|
||||
"@zkochan/retry": "^0.2.0",
|
||||
"encode-registry": "^3.0.1",
|
||||
"load-json-file": "^6.2.0",
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
export function workspacePrefToNpm (workspacePref: string): string {
|
||||
const prefParts = /^workspace:([^._/][^@]*@)?(.*)$/.exec(workspacePref)
|
||||
import { WorkspaceSpec } from '@pnpm/workspace.spec-parser'
|
||||
|
||||
if (prefParts == null) {
|
||||
export function workspacePrefToNpm (workspacePref: string): string {
|
||||
const parseResult = WorkspaceSpec.parse(workspacePref)
|
||||
if (parseResult == 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}`
|
||||
const { alias, version } = parseResult
|
||||
const versionPart = version === '^' || version === '~' ? '*' : version
|
||||
return alias
|
||||
? `npm:${alias}@${versionPart}`
|
||||
: versionPart
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
{
|
||||
"path": "../../workspace/resolve-workspace-range"
|
||||
},
|
||||
{
|
||||
"path": "../../workspace/spec-parser"
|
||||
},
|
||||
{
|
||||
"path": "../resolver-base"
|
||||
}
|
||||
|
||||
15
workspace/spec-parser/README.md
Normal file
15
workspace/spec-parser/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# @pnpm/workspace.spec-parser
|
||||
|
||||
> Parse and stringify workspace specs
|
||||
|
||||
[](https://www.npmjs.com/package/@pnpm/workspace.spec-parser)
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @pnpm/workspace.spec-parser
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
3
workspace/spec-parser/jest.config.js
Normal file
3
workspace/spec-parser/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const config = require('../../jest.config.js');
|
||||
|
||||
module.exports = config
|
||||
38
workspace/spec-parser/package.json
Normal file
38
workspace/spec-parser/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@pnpm/workspace.spec-parser",
|
||||
"version": "0.0.0",
|
||||
"description": "Parse and stringify workspace pref",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"!*.map"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.12"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"_test": "jest",
|
||||
"test": "pnpm run compile && pnpm run _test",
|
||||
"prepublishOnly": "pnpm run compile",
|
||||
"compile": "tsc --build && pnpm run lint --fix"
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/main/workspace/spec-parser",
|
||||
"keywords": [
|
||||
"pnpm9",
|
||||
"pnpm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/main/workspace/spec-parser#readme",
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"devDependencies": {
|
||||
"@pnpm/workspace.spec-parser": "workspace:*"
|
||||
},
|
||||
"exports": {
|
||||
".": "./lib/index.js"
|
||||
}
|
||||
}
|
||||
22
workspace/spec-parser/src/index.ts
Normal file
22
workspace/spec-parser/src/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
const WORKSPACE_PREF_REGEX = /^workspace:((?<alias>[^._/][^@]*)@)?(?<version>.*)$/
|
||||
|
||||
export class WorkspaceSpec {
|
||||
alias?: string
|
||||
version: string
|
||||
|
||||
constructor (version: string, alias?: string) {
|
||||
this.version = version
|
||||
this.alias = alias
|
||||
}
|
||||
|
||||
static parse (pref: string): WorkspaceSpec | null {
|
||||
const parts = WORKSPACE_PREF_REGEX.exec(pref)
|
||||
if (!parts?.groups) return null
|
||||
return new WorkspaceSpec(parts.groups.version, parts.groups.alias)
|
||||
}
|
||||
|
||||
toString (): `workspace:${string}` {
|
||||
const { alias, version } = this
|
||||
return alias ? `workspace:${alias}@${version}` : `workspace:${version}`
|
||||
}
|
||||
}
|
||||
47
workspace/spec-parser/test/workspace-spec.test.ts
Normal file
47
workspace/spec-parser/test/workspace-spec.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { WorkspaceSpec } from '../src/index'
|
||||
|
||||
test('parse valid workspace spec', () => {
|
||||
expect(WorkspaceSpec.parse('workspace:*')).toStrictEqual(new WorkspaceSpec('*'))
|
||||
expect(WorkspaceSpec.parse('workspace:^')).toStrictEqual(new WorkspaceSpec('^'))
|
||||
expect(WorkspaceSpec.parse('workspace:~')).toStrictEqual(new WorkspaceSpec('~'))
|
||||
expect(WorkspaceSpec.parse('workspace:0.1.2')).toStrictEqual(new WorkspaceSpec('0.1.2'))
|
||||
expect(WorkspaceSpec.parse('workspace:foo@*')).toStrictEqual(new WorkspaceSpec('*', 'foo'))
|
||||
expect(WorkspaceSpec.parse('workspace:foo@^')).toStrictEqual(new WorkspaceSpec('^', 'foo'))
|
||||
expect(WorkspaceSpec.parse('workspace:foo@~')).toStrictEqual(new WorkspaceSpec('~', 'foo'))
|
||||
expect(WorkspaceSpec.parse('workspace:foo@0.1.2')).toStrictEqual(new WorkspaceSpec('0.1.2', 'foo'))
|
||||
expect(WorkspaceSpec.parse('workspace:@foo/bar@*')).toStrictEqual(new WorkspaceSpec('*', '@foo/bar'))
|
||||
expect(WorkspaceSpec.parse('workspace:@foo/bar@^')).toStrictEqual(new WorkspaceSpec('^', '@foo/bar'))
|
||||
expect(WorkspaceSpec.parse('workspace:@foo/bar@~')).toStrictEqual(new WorkspaceSpec('~', '@foo/bar'))
|
||||
expect(WorkspaceSpec.parse('workspace:@foo/bar@0.1.2')).toStrictEqual(new WorkspaceSpec('0.1.2', '@foo/bar'))
|
||||
})
|
||||
|
||||
test('parse invalid workspace spec', () => {
|
||||
expect(WorkspaceSpec.parse('npm:foo@0.1.2')).toBe(null)
|
||||
expect(WorkspaceSpec.parse('*')).toBe(null)
|
||||
})
|
||||
|
||||
test('to string', () => {
|
||||
expect(new WorkspaceSpec('*').toString()).toBe('workspace:*')
|
||||
expect(new WorkspaceSpec('^').toString()).toBe('workspace:^')
|
||||
expect(new WorkspaceSpec('~').toString()).toBe('workspace:~')
|
||||
expect(new WorkspaceSpec('0.1.2').toString()).toBe('workspace:0.1.2')
|
||||
expect(new WorkspaceSpec('*', 'foo').toString()).toBe('workspace:foo@*')
|
||||
expect(new WorkspaceSpec('^', 'foo').toString()).toBe('workspace:foo@^')
|
||||
expect(new WorkspaceSpec('~', 'foo').toString()).toBe('workspace:foo@~')
|
||||
expect(new WorkspaceSpec('0.1.2', 'foo').toString()).toBe('workspace:foo@0.1.2')
|
||||
expect(new WorkspaceSpec('*', '@foo/bar').toString()).toBe('workspace:@foo/bar@*')
|
||||
expect(new WorkspaceSpec('^', '@foo/bar').toString()).toBe('workspace:@foo/bar@^')
|
||||
expect(new WorkspaceSpec('~', '@foo/bar').toString()).toBe('workspace:@foo/bar@~')
|
||||
expect(new WorkspaceSpec('0.1.2', '@foo/bar').toString()).toBe('workspace:@foo/bar@0.1.2')
|
||||
})
|
||||
|
||||
test('mutate alias and version', () => {
|
||||
const spec = WorkspaceSpec.parse('workspace:*')!
|
||||
expect(spec.toString()).toBe('workspace:*')
|
||||
spec.version = '^'
|
||||
expect(spec.toString()).toBe('workspace:^')
|
||||
spec.alias = 'foo'
|
||||
expect(spec.toString()).toBe('workspace:foo@^')
|
||||
delete spec.alias
|
||||
expect(spec.toString()).toBe('workspace:^')
|
||||
})
|
||||
13
workspace/spec-parser/tsconfig.json
Normal file
13
workspace/spec-parser/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "@pnpm/tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"../../__typings__/**/*.d.ts"
|
||||
],
|
||||
"references": [],
|
||||
"composite": true
|
||||
}
|
||||
8
workspace/spec-parser/tsconfig.lint.json
Normal file
8
workspace/spec-parser/tsconfig.lint.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts",
|
||||
"../../__typings__/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user