feat(matcher): ignore patterns (#5336)

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
子瞻 Luci
2022-09-14 16:52:16 +08:00
committed by GitHub
parent 3f0137077e
commit 9b44d38a47
3 changed files with 72 additions and 6 deletions

View File

@@ -0,0 +1,16 @@
---
"@pnpm/matcher": minor
"pnpm": minor
---
Now it is possible to exclude packages from hoisting by prepending a `!` to the pattern. This works with both the `hoist-pattern` and `public-hoist-pattern` settings. For instance:
```
public-hoist-pattern[]='*types*'
public-hoist-pattern[]='!@types/react'
hoist-pattern[]='*eslint*'
hoist-pattern[]='!*eslint-plugin*'
```
Ref [#5272](https://github.com/pnpm/pnpm/issues/5272)

View File

@@ -1,16 +1,40 @@
import escapeStringRegexp from 'escape-string-regexp'
export default function matcher (patterns: string[] | string) {
if (typeof patterns === 'string') return matcherFromPattern(patterns)
type Matcher = (input: string) => boolean
export default function matcher (patterns: string[] | string): Matcher {
if (typeof patterns === 'string') return matcherWhenOnlyOnePattern(patterns)
switch (patterns.length) {
case 0: return () => false
case 1: return matcherFromPattern(patterns[0])
case 1: return matcherWhenOnlyOnePattern(patterns[0])
}
const matchArr: Array<{ match: Matcher, ignore: boolean }> = []
let hasIgnore = false
for (const pattern of patterns) {
if (isIgnorePattern(pattern)) {
hasIgnore = true
matchArr.push({ ignore: true, match: matcherFromPattern(pattern.substring(1)) })
} else {
matchArr.push({ ignore: false, match: matcherFromPattern(pattern) })
}
}
if (!hasIgnore) {
return (input: string) => matchArr.some(({ match }) => match(input))
}
return (input: string) => {
let isMatched = false
for (const { ignore, match } of matchArr) {
if (ignore) {
isMatched = !match(input)
} else if (!isMatched && match(input)) {
isMatched = true
}
}
return isMatched
}
const matchArr = patterns.map(matcherFromPattern)
return (input: string) => matchArr.some((match) => match(input))
}
function matcherFromPattern (pattern: string) {
function matcherFromPattern (pattern: string): Matcher {
if (pattern === '*') {
return () => true
}
@@ -23,3 +47,13 @@ function matcherFromPattern (pattern: string) {
const regexp = new RegExp(`^${escapedPattern}$`)
return (input: string) => regexp.test(input)
}
function isIgnorePattern (pattern: string): boolean {
return pattern.startsWith('!')
}
function matcherWhenOnlyOnePattern (pattern: string): Matcher {
return isIgnorePattern(pattern)
? () => false
: matcherFromPattern(pattern)
}

View File

@@ -30,4 +30,20 @@ test('matcher()', () => {
expect(match('bar')).toBe(true)
expect(match('express')).toBe(false)
}
{
const match = matcher(['eslint-*', '!eslint-plugin-bar'])
expect(match('eslint-plugin-foo')).toBe(true)
expect(match('eslint-plugin-bar')).toBe(false)
}
{
const match = matcher(['!eslint-plugin-bar', 'eslint-*'])
expect(match('eslint-plugin-foo')).toBe(true)
expect(match('eslint-plugin-bar')).toBe(true)
}
{
const match = matcher(['eslint-*', '!eslint-plugin-*', 'eslint-plugin-bar'])
expect(match('eslint-config-foo')).toBe(true)
expect(match('eslint-plugin-foo')).toBe(false)
expect(match('eslint-plugin-bar')).toBe(true)
}
})