fix(filtering): support linked-workspace- packages=false in workspace filtering

Pass the linkedWorkspacePackages config to the pkg-graph,
only select packages that are explicitly referred to as workspace:...
when this setting is on.

Fixes #2625
PR #2626
This commit is contained in:
Josiah Grace
2020-06-11 01:33:46 -07:00
committed by GitHub
parent 561f389554
commit e37a5a1755
7 changed files with 191 additions and 10 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/filter-workspace-packages": minor
"pkgs-graph": minor
---
Support linkedWorkspacePackages=false.

View File

@@ -0,0 +1,5 @@
---
"pnpm": patch
---
When `link-workspace-packages` is `false`, filtering by dependencies/dependents should ignore any packages that are not specified via `workspace:` ranges.

View File

@@ -18,13 +18,17 @@ interface Graph {
export async function readProjects (
workspaceDir: string,
pkgSelectors: PackageSelector[]
pkgSelectors: PackageSelector[],
opts?: {
linkWorkspacePackages?: boolean,
}
) {
const allProjects = await findWorkspacePackages(workspaceDir, {})
const { selectedProjectsGraph } = await filterPkgsBySelectorObjects(
allProjects,
pkgSelectors,
{
linkWorkspacePackages: opts?.linkWorkspacePackages,
workspaceDir,
}
)
@@ -35,6 +39,7 @@ export async function filterPackages<T> (
pkgs: Array<Package & T>,
filter: string[],
opts: {
linkWorkspacePackages?: boolean,
prefix: string,
workspaceDir: string,
}
@@ -52,13 +57,14 @@ export async function filterPkgsBySelectorObjects<T> (
pkgs: Array<Package & T>,
packageSelectors: PackageSelector[],
opts: {
linkWorkspacePackages?: boolean,
workspaceDir: string,
}
): Promise<{
selectedProjectsGraph: PackageGraph<T>,
unmatchedFilters: string[],
}> {
const { graph } = createPkgGraph<T>(pkgs)
const { graph } = createPkgGraph<T>(pkgs, { linkWorkspacePackages: opts.linkWorkspacePackages })
if (packageSelectors && packageSelectors.length) {
return filterGraph(graph, packageSelectors, {
workspaceDir: opts.workspaceDir,

View File

@@ -27,7 +27,9 @@ export type PackageNode<T> = {
dependencies: string[],
}
export default function<T> (pkgs: Array<Package & T>): {
export default function<T> (pkgs: Array<Package & T>, opts?: {
linkWorkspacePackages?: boolean
}): {
graph: {[id: string]: PackageNode<T>},
unmatched: Array<{pkgName: string, range: string}>,
} {
@@ -54,8 +56,9 @@ export default function<T> (pkgs: Array<Package & T>): {
.map(depName => {
let spec!: { fetchSpec: string, type: string }
let rawSpec = dependencies[depName]
const isWorkspaceSpec = rawSpec.startsWith('workspace:')
try {
if (rawSpec.startsWith('workspace:')) {
if (isWorkspaceSpec) {
rawSpec = rawSpec.substr(10)
}
spec = npa.resolve(depName, rawSpec, pkg.dir)
@@ -77,6 +80,13 @@ export default function<T> (pkgs: Array<Package & T>): {
if (!pkgs.length) return ''
const versions = pkgs.filter(({ manifest }) => manifest.version)
.map(pkg => pkg.manifest.version) as string[]
// explicitly check if false, backwards-compatibility (can be undefined)
const strictWorkspaceMatching = opts?.linkWorkspacePackages === false && !isWorkspaceSpec
if (strictWorkspaceMatching) {
unmatched.push({ pkgName: depName, range: rawSpec })
return ''
}
if (versions.includes(rawSpec)) {
const matchedPkg = pkgs.find(pkg => pkg.manifest.name === depName && pkg.manifest.version === rawSpec)
return matchedPkg!.dir

View File

@@ -7,6 +7,7 @@ const BAR1_PATH = pathResolve('/zkochan/src/bar')
const FOO1_PATH = pathResolve('/zkochan/src/foo')
const BAR2_PATH = pathResolve('/zkochan/src/bar@2')
const FOO2_PATH = pathResolve('/zkochan/src/foo@2')
const BAR3_PATH = pathResolve('/zkochan/src/bar@3')
test('create package graph', t => {
const result = createPkgGraph([
@@ -319,17 +320,119 @@ test('create package graph ignoring the workspace protocol', t => {
t.end()
})
test('create package graph respects linked-workspace-packages = false', t => {
const result = createPkgGraph([
{
dir: BAR1_PATH,
manifest: {
dependencies: {
'foo': 'workspace:*',
},
name: 'bar',
version: '1.0.0',
},
},
{
dir: FOO1_PATH,
manifest: {
dependencies: {
bar: '^10.0.0',
},
name: 'foo',
version: '1.0.1',
},
},
{
dir: BAR2_PATH,
manifest: {
dependencies: {
foo: '1.0.1',
},
name: 'bar',
version: '2.0.0',
},
},
{
dir: BAR3_PATH,
manifest: {
dependencies: {
'foo': 'workspace:~1.0.0',
},
name: 'bar',
version: '3.0.0',
},
},
], { linkWorkspacePackages: false })
t.deepEqual(result.unmatched, [{ pkgName: 'bar', range: '^10.0.0' }, { pkgName: 'foo', range: '1.0.1' }])
t.deepEqual(result.graph, {
[BAR1_PATH]: {
dependencies: [FOO1_PATH],
package: {
dir: BAR1_PATH,
manifest: {
dependencies: {
'foo': 'workspace:*',
},
name: 'bar',
version: '1.0.0',
},
},
},
[FOO1_PATH]: {
dependencies: [],
package: {
dir: FOO1_PATH,
manifest: {
dependencies: {
bar: '^10.0.0',
},
name: 'foo',
version: '1.0.1',
},
},
},
[BAR2_PATH]: {
// no workspace range, so this shouldn't have any
// workspace dependencies
dependencies: [],
package: {
dir: BAR2_PATH,
manifest: {
dependencies: {
foo: '1.0.1',
},
name: 'bar',
version: '2.0.0',
},
},
},
[BAR3_PATH]: {
dependencies: [FOO1_PATH],
package: {
dir: BAR3_PATH,
manifest: {
dependencies: {
foo: 'workspace:~1.0.0',
},
name: 'bar',
version: '3.0.0',
},
},
},
})
t.end()
})
test('* matches prerelease versions', t => {
const result = createPkgGraph([
{
dir: BAR1_PATH,
manifest: {
name: 'bar',
version: '1.0.0',
dependencies: {
'foo': '*',
},
name: 'bar',
version: '1.0.0',
},
},
{
@@ -347,12 +450,11 @@ test('* matches prerelease versions', t => {
package: {
dir: BAR1_PATH,
manifest: {
name: 'bar',
version: '1.0.0',
dependencies: {
'foo': '*',
},
name: 'bar',
version: '1.0.0',
},
},
},

View File

@@ -186,6 +186,57 @@ test('recursive list --filter', async (t) => {
]),
}, [])
test('recursive list --filter link-workspace-packages=false', async (t) => {
const projects = preparePackages(t, [
{
dependencies: {
'is-positive': '1.0.0',
'project-2': 'workspace:*',
},
name: 'project-1',
version: '1.0.0',
},
{
name: 'project-2',
version: '1.0.0',
},
{
name: 'is-positive',
version: '1.0.0',
},
])
await install.handler({
...DEFAULT_OPTS,
...await readProjects(process.cwd(), [], { linkWorkspacePackages: false }),
dir: process.cwd(),
linkWorkspacePackages: false,
recursive: true,
workspaceDir: process.cwd(),
})
const output = await list.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
recursive: true,
...await readProjects(process.cwd(), [
{ includeDependencies: true, namePattern: 'project-1' },
], { linkWorkspacePackages: false }),
}, [])
t.equal(stripAnsi(output as unknown as string), stripIndent`
Legend: production dependency, optional only, dev only
project-1@1.0.0 ${path.resolve('project-1')}
dependencies:
is-positive 1.0.0
project-2 link:../project-2
`)
t.end()
})
t.equal(stripAnsi(output as unknown as string), stripIndent`
Legend: production dependency, optional only, dev only

View File

@@ -146,6 +146,7 @@ export default async function run (inputArgv: string[]) {
return
}
const filterResults = await filterPackages(allProjects, config.filter ?? [], {
linkWorkspacePackages: !!config.linkWorkspacePackages,
prefix: process.cwd(),
workspaceDir: wsDir,
})