mirror of
https://github.com/pnpm/pnpm.git
synced 2026-02-09 22:52:12 -05:00
22
.changeset/bright-chefs-drop.md
Normal file
22
.changeset/bright-chefs-drop.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-installation": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
It is possible now to update all dependencies except the listed ones using `!`. For instance, update all dependencies, except `lodash`:
|
||||
|
||||
```
|
||||
pnpm update !lodash
|
||||
```
|
||||
|
||||
It also works with pattends, for instance:
|
||||
|
||||
```
|
||||
pnpm update !@babel/*
|
||||
```
|
||||
|
||||
And it may be combined with other patterns:
|
||||
|
||||
```
|
||||
pnpm update @babel/* !@babel/core
|
||||
```
|
||||
5
.changeset/eleven-cups-provide.md
Normal file
5
.changeset/eleven-cups-provide.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/matcher": minor
|
||||
---
|
||||
|
||||
New function added that returns the number of the matched pattern: matcherWithIndex().
|
||||
@@ -1,14 +1,24 @@
|
||||
import escapeStringRegexp from 'escape-string-regexp'
|
||||
|
||||
type Matcher = (input: string) => boolean
|
||||
type MatcherWithIndex = (input: string) => number
|
||||
|
||||
export default function matcher (patterns: string[] | string): Matcher {
|
||||
if (typeof patterns === 'string') return matcherWhenOnlyOnePattern(patterns)
|
||||
const m = matcherWithIndex(Array.isArray(patterns) ? patterns : [patterns])
|
||||
return (input) => m(input) !== -1
|
||||
}
|
||||
|
||||
interface MatcherFunction {
|
||||
match: Matcher
|
||||
ignore: boolean
|
||||
}
|
||||
|
||||
export function matcherWithIndex (patterns: string[]): MatcherWithIndex {
|
||||
switch (patterns.length) {
|
||||
case 0: return () => false
|
||||
case 1: return matcherWhenOnlyOnePattern(patterns[0])
|
||||
case 0: return () => -1
|
||||
case 1: return matcherWhenOnlyOnePatternWithIndex(patterns[0])
|
||||
}
|
||||
const matchArr: Array<{ match: Matcher, ignore: boolean }> = []
|
||||
const matchArr: MatcherFunction[] = []
|
||||
let hasIgnore = false
|
||||
for (const pattern of patterns) {
|
||||
if (isIgnorePattern(pattern)) {
|
||||
@@ -19,19 +29,33 @@ export default function matcher (patterns: string[] | string): Matcher {
|
||||
}
|
||||
}
|
||||
if (!hasIgnore) {
|
||||
return (input: string) => matchArr.some(({ match }) => match(input))
|
||||
return matchInputWithNonIgnoreMatchers.bind(null, matchArr)
|
||||
}
|
||||
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 matchInputWithMatchersArray.bind(null, matchArr)
|
||||
}
|
||||
|
||||
function matchInputWithNonIgnoreMatchers (matchArr: MatcherFunction[], input: string): number {
|
||||
for (let i = 0; i < matchArr.length; i++) {
|
||||
if (matchArr[i].match(input)) return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
function matchInputWithMatchersArray (matchArr: MatcherFunction[], input: string): number {
|
||||
let matchedPatternIndex = -1
|
||||
for (let i = 0; i < matchArr.length; i++) {
|
||||
const { ignore, match } = matchArr[i]
|
||||
if (ignore) {
|
||||
if (match(input)) {
|
||||
matchedPatternIndex = -1
|
||||
} else if (matchedPatternIndex === -1) {
|
||||
matchedPatternIndex = i
|
||||
}
|
||||
} else if (matchedPatternIndex === -1 && match(input)) {
|
||||
matchedPatternIndex = i
|
||||
}
|
||||
return isMatched
|
||||
}
|
||||
return matchedPatternIndex
|
||||
}
|
||||
|
||||
function matcherFromPattern (pattern: string): Matcher {
|
||||
@@ -52,8 +76,16 @@ function isIgnorePattern (pattern: string): boolean {
|
||||
return pattern.startsWith('!')
|
||||
}
|
||||
|
||||
function matcherWhenOnlyOnePattern (pattern: string): Matcher {
|
||||
return isIgnorePattern(pattern)
|
||||
? () => false
|
||||
: matcherFromPattern(pattern)
|
||||
function matcherWhenOnlyOnePatternWithIndex (pattern: string): MatcherWithIndex {
|
||||
const m = matcherWhenOnlyOnePattern(pattern)
|
||||
return (input) => m(input) ? 0 : -1
|
||||
}
|
||||
|
||||
function matcherWhenOnlyOnePattern (pattern: string): Matcher {
|
||||
if (!isIgnorePattern(pattern)) {
|
||||
return matcherFromPattern(pattern)
|
||||
}
|
||||
const ignorePattern = pattern.substring(1)
|
||||
const m = matcherFromPattern(ignorePattern)
|
||||
return (input) => !m(input)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import matcher from '@pnpm/matcher'
|
||||
import matcher, { matcherWithIndex } from '@pnpm/matcher'
|
||||
|
||||
test('matcher()', () => {
|
||||
{
|
||||
@@ -47,3 +47,55 @@ test('matcher()', () => {
|
||||
expect(match('eslint-plugin-bar')).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
test('matcherWithIndex()', () => {
|
||||
{
|
||||
const match = matcherWithIndex(['*'])
|
||||
expect(match('@eslint/plugin-foo')).toBe(0)
|
||||
expect(match('express')).toBe(0)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['eslint-*'])
|
||||
expect(match('eslint-plugin-foo')).toBe(0)
|
||||
expect(match('express')).toBe(-1)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['*plugin*'])
|
||||
expect(match('@eslint/plugin-foo')).toBe(0)
|
||||
expect(match('express')).toBe(-1)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['a*c'])
|
||||
expect(match('abc')).toBe(0)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['*-positive'])
|
||||
expect(match('is-positive')).toBe(0)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['foo', 'bar'])
|
||||
expect(match('foo')).toBe(0)
|
||||
expect(match('bar')).toBe(1)
|
||||
expect(match('express')).toBe(-1)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['eslint-*', '!eslint-plugin-bar'])
|
||||
expect(match('eslint-plugin-foo')).toBe(0)
|
||||
expect(match('eslint-plugin-bar')).toBe(-1)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['!eslint-plugin-bar', 'eslint-*'])
|
||||
expect(match('eslint-plugin-foo')).toBe(0)
|
||||
expect(match('eslint-plugin-bar')).toBe(1)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['eslint-*', '!eslint-plugin-*', 'eslint-plugin-bar'])
|
||||
expect(match('eslint-config-foo')).toBe(0)
|
||||
expect(match('eslint-plugin-foo')).toBe(-1)
|
||||
expect(match('eslint-plugin-bar')).toBe(2)
|
||||
}
|
||||
{
|
||||
const match = matcherWithIndex(['!@pnpm.e2e/peer-*'])
|
||||
expect(match('@pnpm.e2e/foo')).toBe(0)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ import PnpmError from '@pnpm/error'
|
||||
import { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages'
|
||||
import logger from '@pnpm/logger'
|
||||
import { filterDependenciesByType } from '@pnpm/manifest-utils'
|
||||
import matcher from '@pnpm/matcher'
|
||||
import { matcherWithIndex } from '@pnpm/matcher'
|
||||
import { rebuild } from '@pnpm/plugin-commands-rebuild'
|
||||
import { requireHooks } from '@pnpm/pnpmfile'
|
||||
import sortPackages from '@pnpm/sort-packages'
|
||||
@@ -500,26 +500,22 @@ export function matchDependencies (
|
||||
}
|
||||
|
||||
export function createMatcher (params: string[]) {
|
||||
const matchers = params.map((param) => {
|
||||
const atIndex = param.indexOf('@', 1)
|
||||
let pattern!: string
|
||||
let spec!: string
|
||||
const patterns: string[] = []
|
||||
const specs: string[] = []
|
||||
for (const param of params) {
|
||||
const atIndex = param.indexOf('@', param[0] === '!' ? 2 : 1)
|
||||
if (atIndex === -1) {
|
||||
pattern = param
|
||||
spec = ''
|
||||
patterns.push(param)
|
||||
specs.push('')
|
||||
} else {
|
||||
pattern = param.slice(0, atIndex)
|
||||
spec = param.slice(atIndex + 1)
|
||||
patterns.push(param.slice(0, atIndex))
|
||||
specs.push(param.slice(atIndex + 1))
|
||||
}
|
||||
return {
|
||||
match: matcher(pattern),
|
||||
spec,
|
||||
}
|
||||
})
|
||||
}
|
||||
const matcher = matcherWithIndex(patterns)
|
||||
return (depName: string) => {
|
||||
for (const { spec, match } of matchers) {
|
||||
if (match(depName)) return spec
|
||||
}
|
||||
return null
|
||||
const index = matcher(depName)
|
||||
if (index === -1) return null
|
||||
return specs[index]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,39 @@ test('update with "*" pattern', async () => {
|
||||
expect(lockfile.packages['/@pnpm.e2e/foo/1.0.0']).toBeTruthy()
|
||||
})
|
||||
|
||||
test('update with negation pattern', async () => {
|
||||
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.1', distTag: 'latest' })
|
||||
await addDistTag({ package: '@pnpm.e2e/peer-c', version: '2.0.0', distTag: 'latest' })
|
||||
await addDistTag({ package: '@pnpm.e2e/foo', version: '2.0.0', distTag: 'latest' })
|
||||
|
||||
const project = prepare({
|
||||
dependencies: {
|
||||
'@pnpm.e2e/peer-a': '1.0.0',
|
||||
'@pnpm.e2e/peer-c': '1.0.0',
|
||||
'@pnpm.e2e/foo': '1.0.0',
|
||||
},
|
||||
})
|
||||
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
await update.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
latest: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, ['!@pnpm.e2e/peer-*'])
|
||||
|
||||
const lockfile = await project.readLockfile()
|
||||
|
||||
expect(lockfile.packages['/@pnpm.e2e/peer-a/1.0.0']).toBeTruthy()
|
||||
expect(lockfile.packages['/@pnpm.e2e/peer-c/1.0.0']).toBeTruthy()
|
||||
expect(lockfile.packages['/@pnpm.e2e/foo/2.0.0']).toBeTruthy()
|
||||
})
|
||||
|
||||
test('update: fail when both "latest" and "workspace" are true', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user