feat: filter packages based on glob pattern when filtering by directories (#3521)

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
David Collins
2021-06-17 17:06:10 -04:00
committed by GitHub
parent 72c0dd7be4
commit c86fad0041
5 changed files with 97 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
---
"pnpm": minor
---
When `use-beta-cli` is `true`, filtering by directories supports globs.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/filter-workspace-packages": minor
---
New option added: `useGlobDirFiltering`. When `true`, directory filtering is done using globs.

View File

@@ -5,6 +5,7 @@ import isSubdir from 'is-subdir'
import difference from 'ramda/src/difference'
import partition from 'ramda/src/partition'
import pick from 'ramda/src/pick'
import * as micromatch from 'micromatch'
import getChangedPkgs from './getChangedPackages'
import parsePackageSelector, { PackageSelector } from './parsePackageSelector'
@@ -55,6 +56,7 @@ export async function filterPackages<T> (
prefix: string
workspaceDir: string
testPattern?: string[]
useGlobDirFiltering?: boolean
}
): Promise<{
selectedProjectsGraph: PackageGraph<T>
@@ -72,6 +74,7 @@ export async function filterPkgsBySelectorObjects<T> (
linkWorkspacePackages?: boolean
workspaceDir: string
testPattern?: string[]
useGlobDirFiltering?: boolean
}
): Promise<{
selectedProjectsGraph: PackageGraph<T>
@@ -87,6 +90,7 @@ export async function filterPkgsBySelectorObjects<T> (
filteredGraph = await filterGraph(graph, allPackageSelectors, {
workspaceDir: opts.workspaceDir,
testPattern: opts.testPattern,
useGlobDirFiltering: opts.useGlobDirFiltering,
})
}
@@ -97,6 +101,7 @@ export async function filterPkgsBySelectorObjects<T> (
prodFilteredGraph = await filterGraph(graph, prodPackageSelectors, {
workspaceDir: opts.workspaceDir,
testPattern: opts.testPattern,
useGlobDirFiltering: opts.useGlobDirFiltering,
})
}
@@ -122,6 +127,7 @@ export default async function filterGraph<T> (
opts: {
workspaceDir: string
testPattern?: string[]
useGlobDirFiltering?: boolean
}
): Promise<{
selectedProjectsGraph: PackageGraph<T>
@@ -150,6 +156,7 @@ async function _filterGraph<T> (
opts: {
workspaceDir: string
testPattern?: string[]
useGlobDirFiltering?: boolean
},
packageSelectors: PackageSelector[]
): Promise<{
@@ -163,6 +170,9 @@ async function _filterGraph<T> (
const graph = pkgGraphToGraph(pkgGraph)
const unmatchedFilters = [] as string[]
let reversedGraph: Graph | undefined
const matchPackagesByPath = opts.useGlobDirFiltering === true
? matchPackagesByGlob
: matchPackagesByExactPath
for (const selector of packageSelectors) {
let entryPackages: string[] | null = null
if (selector.diff) {
@@ -257,13 +267,22 @@ function matchPackages<T> (
return Object.keys(graph).filter((id) => graph[id].package.manifest.name && match(graph[id].package.manifest.name!))
}
function matchPackagesByPath<T> (
function matchPackagesByExactPath<T> (
graph: PackageGraph<T>,
pathStartsWith: string
) {
return Object.keys(graph).filter((parentDir) => isSubdir(pathStartsWith, parentDir))
}
function matchPackagesByGlob<T> (
graph: PackageGraph<T>,
pathStartsWith: string
) {
const format = (str: string) => str.replace(/\/$/, '')
const formattedFilter = pathStartsWith.replace(/\\/g, '/').replace(/\/$/, '')
return Object.keys(graph).filter((parentDir) => micromatch.isMatch(parentDir, formattedFilter, { format }))
}
function pickSubgraph (
graph: Graph,
nextNodeIds: string[],

View File

@@ -96,6 +96,20 @@ const PKGS_GRAPH: PackageGraph<{}> = {
name: 'project-5',
version: '1.0.0',
dependencies: {
'is-positive': '1.0.0',
},
},
},
},
'/project-5/packages/project-6': {
dependencies: [],
package: {
dir: '/project-5/packages/project-6',
manifest: {
name: 'project-6',
version: '1.0.0',
dependencies: {
'is-positive': '1.0.0',
},
@@ -204,6 +218,39 @@ test('select by parentDir', async () => {
expect(Object.keys(selectedProjectsGraph)).toStrictEqual(['/packages/project-0', '/packages/project-1'])
})
test('select by parentDir using glob', async () => {
const { selectedProjectsGraph } = await filterWorkspacePackages(PKGS_GRAPH, [
{
excludeSelf: false,
parentDir: '/packages/*',
},
], { workspaceDir: process.cwd(), useGlobDirFiltering: true })
expect(Object.keys(selectedProjectsGraph)).toStrictEqual(['/packages/project-0', '/packages/project-1'])
})
test('select by parentDir using globstar', async () => {
const { selectedProjectsGraph } = await filterWorkspacePackages(PKGS_GRAPH, [
{
excludeSelf: false,
parentDir: '/project-5/**',
},
], { workspaceDir: process.cwd(), useGlobDirFiltering: true })
expect(Object.keys(selectedProjectsGraph)).toStrictEqual(['/project-5', '/project-5/packages/project-6'])
})
test('select by parentDir with no glob', async () => {
const { selectedProjectsGraph } = await filterWorkspacePackages(PKGS_GRAPH, [
{
excludeSelf: false,
parentDir: '/project-5',
},
], { workspaceDir: process.cwd(), useGlobDirFiltering: true })
expect(Object.keys(selectedProjectsGraph)).toStrictEqual(['/project-5'])
})
test('select changed packages', async () => {
// This test fails on Appveyor due to environmental issues
if (isCI && isWindows()) {
@@ -338,11 +385,11 @@ test('should return unmatched filters', async () => {
{
excludeSelf: true,
includeDependencies: true,
namePattern: 'project-6',
namePattern: 'project-7',
},
], { workspaceDir: process.cwd() })
expect(unmatchedFilters).toStrictEqual(['project-6'])
expect(unmatchedFilters).toStrictEqual(['project-7'])
})
test('select all packages except one', async () => {
@@ -375,3 +422,20 @@ test('select by parentDir and exclude one package by pattern', async () => {
expect(Object.keys(selectedProjectsGraph)).toStrictEqual(['/packages/project-0'])
})
test('select by parentDir with glob and exclude one package by pattern', async () => {
const { selectedProjectsGraph } = await filterWorkspacePackages(PKGS_GRAPH, [
{
excludeSelf: false,
parentDir: '/packages/*',
},
{
exclude: true,
excludeSelf: false,
includeDependents: false,
namePattern: '*-1',
},
], { workspaceDir: process.cwd(), useGlobDirFiltering: true })
expect(Object.keys(selectedProjectsGraph)).toStrictEqual(['/packages/project-0'])
})

View File

@@ -184,6 +184,7 @@ export default async function run (inputArgv: string[]) {
prefix: process.cwd(),
workspaceDir: wsDir,
testPattern: config.testPattern,
useGlobDirFiltering: config.useBetaCli,
})
config.selectedProjectsGraph = filterResults.selectedProjectsGraph
if (isEmpty(config.selectedProjectsGraph)) {