mirror of
https://github.com/pnpm/pnpm.git
synced 2026-02-02 03:02:49 -05:00
feat(matcher): ignore patterns (#5336)
Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
16
.changeset/calm-phones-jog.md
Normal file
16
.changeset/calm-phones-jog.md
Normal 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)
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user