mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-06 22:18:17 -05:00
feat: support to run multi script commands specified with regex selector (#5871)
This commit is contained in:
6
.changeset/silly-flies-care.md
Normal file
6
.changeset/silly-flies-care.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-script-runners": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Support script selector with RegExp such as `pnpm run /build:.*/` and execute the matched scripts with the RegExp [#5871](https://github.com/pnpm/pnpm/pull/5871).
|
||||
23
exec/plugin-commands-script-runners/src/regexpCommand.ts
Normal file
23
exec/plugin-commands-script-runners/src/regexpCommand.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
|
||||
export function tryBuildRegExpFromCommand (command: string): RegExp | null {
|
||||
// https://github.com/stdlib-js/regexp-regexp/blob/6428051ac9ef7c9d03468b19bdbb1dc6fc2a5509/lib/regexp.js
|
||||
const regExpDetectRegExpScriptCommand = /^\/((?:\\\/|[^/])+)\/([dgimuys]*)$/
|
||||
const match = command.match(regExpDetectRegExpScriptCommand)
|
||||
|
||||
// if the passed script selector is not in the format of RegExp literal like /build:.*/, return null and handle it as a string script command
|
||||
if (!match) {
|
||||
return null
|
||||
}
|
||||
|
||||
// if the passed RegExp script selector includes flag, report the error because RegExp flag is not useful for script selector and pnpm does not support this.
|
||||
if (match[2]) {
|
||||
throw new PnpmError('UNSUPPORTED_SCRIPT_COMMAND_FORMAT', 'RegExp flags are not supported in script command selector')
|
||||
}
|
||||
|
||||
try {
|
||||
return new RegExp(match[1])
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import path from 'path'
|
||||
import pLimit from 'p-limit'
|
||||
import {
|
||||
docsUrl,
|
||||
readProjectManifestOnly,
|
||||
@@ -13,11 +14,11 @@ import {
|
||||
makeNodeRequireOption,
|
||||
RunLifecycleHookOptions,
|
||||
} from '@pnpm/lifecycle'
|
||||
import { ProjectManifest } from '@pnpm/types'
|
||||
import { PackageScripts, ProjectManifest } from '@pnpm/types'
|
||||
import pick from 'ramda/src/pick'
|
||||
import realpathMissing from 'realpath-missing'
|
||||
import renderHelp from 'render-help'
|
||||
import { runRecursive, RecursiveRunOpts } from './runRecursive'
|
||||
import { runRecursive, RecursiveRunOpts, getSpecifiedScripts as getSpecifiedScriptWithoutStartCommand } from './runRecursive'
|
||||
import { existsInDir } from './existsInDir'
|
||||
import { handler as exec } from './exec'
|
||||
|
||||
@@ -43,6 +44,11 @@ export const RESUME_FROM_OPTION_HELP = {
|
||||
name: '--resume-from',
|
||||
}
|
||||
|
||||
export const SEQUENTIAL_OPTION_HELP = {
|
||||
description: 'Run the specified scripts one by one',
|
||||
name: '--sequential',
|
||||
}
|
||||
|
||||
export const shorthands = {
|
||||
parallel: [
|
||||
'--workspace-concurrency=Infinity',
|
||||
@@ -50,6 +56,9 @@ export const shorthands = {
|
||||
'--stream',
|
||||
'--recursive',
|
||||
],
|
||||
sequential: [
|
||||
'--workspace-concurrency=1',
|
||||
],
|
||||
}
|
||||
|
||||
export function rcOptionsTypes () {
|
||||
@@ -112,6 +121,7 @@ For options that may be used with `-r`, see "pnpm help recursive"',
|
||||
PARALLEL_OPTION_HELP,
|
||||
RESUME_FROM_OPTION_HELP,
|
||||
...UNIVERSAL_OPTIONS,
|
||||
SEQUENTIAL_OPTION_HELP,
|
||||
],
|
||||
},
|
||||
FILTERING,
|
||||
@@ -159,7 +169,10 @@ export async function handler (
|
||||
: undefined
|
||||
return printProjectCommands(manifest, rootManifest ?? undefined)
|
||||
}
|
||||
if (scriptName !== 'start' && !manifest.scripts?.[scriptName]) {
|
||||
|
||||
const specifiedScripts = getSpecifiedScripts(manifest.scripts ?? {}, scriptName)
|
||||
|
||||
if (specifiedScripts.length < 1) {
|
||||
if (opts.ifPresent) return
|
||||
if (opts.fallbackCommandUsed) {
|
||||
if (opts.argv == null) throw new Error('Could not fallback because opts.argv.original was not passed to the script runner')
|
||||
@@ -170,9 +183,9 @@ export async function handler (
|
||||
}
|
||||
if (opts.workspaceDir) {
|
||||
const { manifest: rootManifest } = await tryReadProjectManifest(opts.workspaceDir, opts)
|
||||
if (rootManifest?.scripts?.[scriptName]) {
|
||||
if (getSpecifiedScripts(rootManifest?.scripts ?? {}, scriptName).length > 0 && specifiedScripts.length < 1) {
|
||||
throw new PnpmError('NO_SCRIPT', `Missing script: ${scriptName}`, {
|
||||
hint: `But ${scriptName} is present in the root of the workspace,
|
||||
hint: `But script matched with ${scriptName} is present in the root of the workspace,
|
||||
so you may run "pnpm -w run ${scriptName}"`,
|
||||
})
|
||||
}
|
||||
@@ -203,21 +216,11 @@ so you may run "pnpm -w run ${scriptName}"`,
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (
|
||||
opts.enablePrePostScripts &&
|
||||
manifest.scripts?.[`pre${scriptName}`] &&
|
||||
!manifest.scripts[scriptName].includes(`pre${scriptName}`)
|
||||
) {
|
||||
await runLifecycleHook(`pre${scriptName}`, manifest, lifecycleOpts)
|
||||
}
|
||||
await runLifecycleHook(scriptName, manifest, { ...lifecycleOpts, args: passedThruArgs })
|
||||
if (
|
||||
opts.enablePrePostScripts &&
|
||||
manifest.scripts?.[`post${scriptName}`] &&
|
||||
!manifest.scripts[scriptName].includes(`post${scriptName}`)
|
||||
) {
|
||||
await runLifecycleHook(`post${scriptName}`, manifest, lifecycleOpts)
|
||||
}
|
||||
const limitRun = pLimit(opts.workspaceConcurrency ?? 4)
|
||||
|
||||
const _runScript = runScript.bind(null, { manifest, lifecycleOpts, runScriptOptions: { enablePrePostScripts: opts.enablePrePostScripts ?? false }, passedThruArgs })
|
||||
|
||||
await Promise.all(specifiedScripts.map(script => limitRun(() => _runScript(script))))
|
||||
} catch (err: any) { // eslint-disable-line
|
||||
if (opts.bail !== false) {
|
||||
throw err
|
||||
@@ -300,6 +303,48 @@ ${renderCommands(rootScripts)}`
|
||||
return output
|
||||
}
|
||||
|
||||
export interface RunScriptOptions {
|
||||
enablePrePostScripts: boolean
|
||||
}
|
||||
|
||||
export const runScript: (opts: {
|
||||
manifest: ProjectManifest
|
||||
lifecycleOpts: RunLifecycleHookOptions
|
||||
runScriptOptions: RunScriptOptions
|
||||
passedThruArgs: string[]
|
||||
}, scriptName: string) => Promise<void> = async function (opts, scriptName) {
|
||||
if (
|
||||
opts.runScriptOptions.enablePrePostScripts &&
|
||||
opts.manifest.scripts?.[`pre${scriptName}`] &&
|
||||
!opts.manifest.scripts[scriptName].includes(`pre${scriptName}`)
|
||||
) {
|
||||
await runLifecycleHook(`pre${scriptName}`, opts.manifest, opts.lifecycleOpts)
|
||||
}
|
||||
await runLifecycleHook(scriptName, opts.manifest, { ...opts.lifecycleOpts, args: opts.passedThruArgs })
|
||||
if (
|
||||
opts.runScriptOptions.enablePrePostScripts &&
|
||||
opts.manifest.scripts?.[`post${scriptName}`] &&
|
||||
!opts.manifest.scripts[scriptName].includes(`post${scriptName}`)
|
||||
) {
|
||||
await runLifecycleHook(`post${scriptName}`, opts.manifest, opts.lifecycleOpts)
|
||||
}
|
||||
}
|
||||
|
||||
function renderCommands (commands: string[][]) {
|
||||
return commands.map(([scriptName, script]) => ` ${scriptName}\n ${script}`).join('\n')
|
||||
}
|
||||
|
||||
function getSpecifiedScripts (scripts: PackageScripts, scriptName: string) {
|
||||
const specifiedSelector = getSpecifiedScriptWithoutStartCommand(scripts, scriptName)
|
||||
|
||||
if (specifiedSelector.length > 0) {
|
||||
return specifiedSelector
|
||||
}
|
||||
|
||||
// if a user passes start command as scriptName, `node server.js` will be executed as a fallback, so return start command even if start command is not defined in package.json
|
||||
if (scriptName === 'start') {
|
||||
return [scriptName]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { RecursiveSummary, throwOnCommandFail } from '@pnpm/cli-utils'
|
||||
import { Config } from '@pnpm/config'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import {
|
||||
runLifecycleHook,
|
||||
makeNodeRequireOption,
|
||||
RunLifecycleHookOptions,
|
||||
} from '@pnpm/lifecycle'
|
||||
@@ -13,6 +12,9 @@ import pLimit from 'p-limit'
|
||||
import realpathMissing from 'realpath-missing'
|
||||
import { existsInDir } from './existsInDir'
|
||||
import { getResumedPackageChunks } from './exec'
|
||||
import { runScript } from './run'
|
||||
import { tryBuildRegExpFromCommand } from './regexpCommand'
|
||||
import { PackageScripts } from '@pnpm/types'
|
||||
|
||||
export type RecursiveRunOpts = Pick<Config,
|
||||
| 'enablePrePostScripts'
|
||||
@@ -73,7 +75,7 @@ export async function runRecursive (
|
||||
const missingScriptPackages: string[] = packageChunks
|
||||
.flat()
|
||||
.map((prefix) => opts.selectedProjectsGraph[prefix])
|
||||
.filter((pkg) => !pkg.package.manifest.scripts?.[scriptName])
|
||||
.filter((pkg) => getSpecifiedScripts(pkg.package.manifest.scripts ?? {}, scriptName).length < 1)
|
||||
.map((pkg) => pkg.package.manifest.name ?? pkg.package.dir)
|
||||
if (missingScriptPackages.length) {
|
||||
throw new PnpmError('RECURSIVE_RUN_NO_SCRIPT', `Missing script "${scriptName}" in packages: ${missingScriptPackages.join(', ')}`)
|
||||
@@ -81,7 +83,14 @@ export async function runRecursive (
|
||||
}
|
||||
|
||||
for (const chunk of packageChunks) {
|
||||
await Promise.all(chunk.map(async (prefix: string) =>
|
||||
const selectedScripts = chunk.map(prefix => {
|
||||
const pkg = opts.selectedProjectsGraph[prefix]
|
||||
const specifiedScripts = getSpecifiedScripts(pkg.package.manifest.scripts ?? {}, scriptName)
|
||||
|
||||
return specifiedScripts.map(script => ({ prefix, scriptName: script }))
|
||||
}).flat()
|
||||
|
||||
await Promise.all(selectedScripts.map(async ({ prefix, scriptName }) =>
|
||||
limitRun(async () => {
|
||||
const pkg = opts.selectedProjectsGraph[prefix]
|
||||
if (
|
||||
@@ -113,21 +122,9 @@ export async function runRecursive (
|
||||
...makeNodeRequireOption(pnpPath),
|
||||
}
|
||||
}
|
||||
if (
|
||||
opts.enablePrePostScripts &&
|
||||
pkg.package.manifest.scripts?.[`pre${scriptName}`] &&
|
||||
!pkg.package.manifest.scripts[scriptName].includes(`pre${scriptName}`)
|
||||
) {
|
||||
await runLifecycleHook(`pre${scriptName}`, pkg.package.manifest, lifecycleOpts)
|
||||
}
|
||||
await runLifecycleHook(scriptName, pkg.package.manifest, { ...lifecycleOpts, args: passedThruArgs })
|
||||
if (
|
||||
opts.enablePrePostScripts &&
|
||||
pkg.package.manifest.scripts?.[`post${scriptName}`] &&
|
||||
!pkg.package.manifest.scripts[scriptName].includes(`post${scriptName}`)
|
||||
) {
|
||||
await runLifecycleHook(`post${scriptName}`, pkg.package.manifest, lifecycleOpts)
|
||||
}
|
||||
|
||||
const _runScript = runScript.bind(null, { manifest: pkg.package.manifest, lifecycleOpts, runScriptOptions: { enablePrePostScripts: opts.enablePrePostScripts ?? false }, passedThruArgs })
|
||||
await _runScript(scriptName)
|
||||
result.passes++
|
||||
} catch (err: any) { // eslint-disable-line
|
||||
logger.info(err)
|
||||
@@ -164,3 +161,20 @@ export async function runRecursive (
|
||||
|
||||
throwOnCommandFail('pnpm recursive run', result)
|
||||
}
|
||||
|
||||
export function getSpecifiedScripts (scripts: PackageScripts, scriptName: string) {
|
||||
// if scripts in package.json has script which is equal to scriptName a user passes, return it.
|
||||
if (scripts[scriptName]) {
|
||||
return [scriptName]
|
||||
}
|
||||
|
||||
const scriptSelector = tryBuildRegExpFromCommand(scriptName)
|
||||
|
||||
// if scriptName which a user passes is RegExp (like /build:.*/), multiple scripts to execute will be selected with RegExp
|
||||
if (scriptSelector) {
|
||||
const scriptKeys = Object.keys(scripts)
|
||||
return scriptKeys.filter(script => script.match(scriptSelector))
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -411,7 +411,7 @@ test('if a script is not found but is present in the root, print an info message
|
||||
}
|
||||
|
||||
expect(err).toBeTruthy()
|
||||
expect(err.hint).toMatch(/But build is present in the root/)
|
||||
expect(err.hint).toMatch(/But script matched with build is present in the root/)
|
||||
})
|
||||
|
||||
test('scripts work with PnP', async () => {
|
||||
@@ -465,3 +465,123 @@ test('pnpm run with custom shell', async () => {
|
||||
|
||||
expect((await import(path.resolve('shell-input.json'))).default).toStrictEqual(['-c', 'foo bar'])
|
||||
})
|
||||
|
||||
test('pnpm run with RegExp script selector should work', async () => {
|
||||
prepare({
|
||||
scripts: {
|
||||
'build:a': 'node -e "require(\'fs\').writeFileSync(\'./output-build-a.txt\', \'a\', \'utf8\')"',
|
||||
'build:b': 'node -e "require(\'fs\').writeFileSync(\'./output-build-b.txt\', \'b\', \'utf8\')"',
|
||||
'build:c': 'node -e "require(\'fs\').writeFileSync(\'./output-build-c.txt\', \'c\', \'utf8\')"',
|
||||
build: 'node -e "require(\'fs\').writeFileSync(\'./output-build-a.txt\', \'should not run\', \'utf8\')"',
|
||||
'lint:a': 'node -e "require(\'fs\').writeFileSync(\'./output-lint-a.txt\', \'a\', \'utf8\')"',
|
||||
'lint:b': 'node -e "require(\'fs\').writeFileSync(\'./output-lint-b.txt\', \'b\', \'utf8\')"',
|
||||
'lint:c': 'node -e "require(\'fs\').writeFileSync(\'./output-lint-c.txt\', \'c\', \'utf8\')"',
|
||||
lint: 'node -e "require(\'fs\').writeFileSync(\'./output-lint-a.txt\', \'should not run\', \'utf8\')"',
|
||||
},
|
||||
})
|
||||
|
||||
await run.handler({
|
||||
dir: process.cwd(),
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
rawConfig: {},
|
||||
}, ['/^(lint|build):.*/'])
|
||||
|
||||
expect(await fs.readFile('output-build-a.txt', { encoding: 'utf-8' })).toEqual('a')
|
||||
expect(await fs.readFile('output-build-b.txt', { encoding: 'utf-8' })).toEqual('b')
|
||||
expect(await fs.readFile('output-build-c.txt', { encoding: 'utf-8' })).toEqual('c')
|
||||
|
||||
expect(await fs.readFile('output-lint-a.txt', { encoding: 'utf-8' })).toEqual('a')
|
||||
expect(await fs.readFile('output-lint-b.txt', { encoding: 'utf-8' })).toEqual('b')
|
||||
expect(await fs.readFile('output-lint-c.txt', { encoding: 'utf-8' })).toEqual('c')
|
||||
})
|
||||
|
||||
test('pnpm run with RegExp script selector should work also for pre/post script', async () => {
|
||||
prepare({
|
||||
scripts: {
|
||||
'build:a': 'node -e "require(\'fs\').writeFileSync(\'./output-a.txt\', \'a\', \'utf8\')"',
|
||||
'prebuild:a': 'node -e "require(\'fs\').writeFileSync(\'./output-pre-a.txt\', \'pre-a\', \'utf8\')"',
|
||||
},
|
||||
})
|
||||
|
||||
await run.handler({
|
||||
dir: process.cwd(),
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
rawConfig: {},
|
||||
enablePrePostScripts: true,
|
||||
}, ['/build:.*/'])
|
||||
|
||||
expect(await fs.readFile('output-a.txt', { encoding: 'utf-8' })).toEqual('a')
|
||||
expect(await fs.readFile('output-pre-a.txt', { encoding: 'utf-8' })).toEqual('pre-a')
|
||||
})
|
||||
|
||||
test('pnpm run with RegExp script selector should work parallel as a default behavior (parallel execution limits number is four)', async () => {
|
||||
prepare({
|
||||
scripts: {
|
||||
'build:a': 'node -e "let i = 20;setInterval(() => {if (!--i) process.exit(0); require(\'json-append\').append(Date.now(),\'./output-a.json\');},50)"',
|
||||
'build:b': 'node -e "let i = 40;setInterval(() => {if (!--i) process.exit(0); require(\'json-append\').append(Date.now(),\'./output-b.json\');},25)"',
|
||||
},
|
||||
})
|
||||
|
||||
await execa('pnpm', ['add', 'json-append@1'])
|
||||
|
||||
await run.handler({
|
||||
dir: process.cwd(),
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
rawConfig: {},
|
||||
}, ['/build:.*/'])
|
||||
|
||||
const { default: outputsA } = await import(path.resolve('output-a.json'))
|
||||
const { default: outputsB } = await import(path.resolve('output-b.json'))
|
||||
|
||||
expect(Math.max(outputsA[0], outputsB[0]) < Math.min(outputsA[outputsA.length - 1], outputsB[outputsB.length - 1])).toBeTruthy()
|
||||
})
|
||||
|
||||
test('pnpm run with RegExp script selector should work sequentially with --workspace-concurrency=1', async () => {
|
||||
prepare({
|
||||
scripts: {
|
||||
'build:a': 'node -e "let i = 2;setInterval(() => {if (!i--) process.exit(0); require(\'json-append\').append(Date.now(),\'./output-a.json\');},16)"',
|
||||
'build:b': 'node -e "let i = 2;setInterval(() => {if (!i--) process.exit(0); require(\'json-append\').append(Date.now(),\'./output-b.json\');},16)"',
|
||||
},
|
||||
})
|
||||
|
||||
await execa('pnpm', ['add', 'json-append@1'])
|
||||
|
||||
await run.handler({
|
||||
dir: process.cwd(),
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
rawConfig: {},
|
||||
workspaceConcurrency: 1,
|
||||
}, ['/build:.*/'])
|
||||
|
||||
const { default: outputsA } = await import(path.resolve('output-a.json'))
|
||||
const { default: outputsB } = await import(path.resolve('output-b.json'))
|
||||
|
||||
expect(outputsA[0] < outputsB[0] && outputsA[1] < outputsB[1]).toBeTruthy()
|
||||
})
|
||||
|
||||
test('pnpm run with RegExp script selector with flag should throw error', async () => {
|
||||
prepare({
|
||||
scripts: {
|
||||
'build:a': 'node -e "let i = 2;setInterval(() => {if (!i--) process.exit(0); require(\'json-append\').append(Date.now(),\'./output-a.json\');},16)"',
|
||||
'build:b': 'node -e "let i = 2;setInterval(() => {if (!i--) process.exit(0); require(\'json-append\').append(Date.now(),\'./output-b.json\');},16)"',
|
||||
},
|
||||
})
|
||||
|
||||
let err!: Error
|
||||
try {
|
||||
await run.handler({
|
||||
dir: process.cwd(),
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
rawConfig: {},
|
||||
workspaceConcurrency: 1,
|
||||
}, ['/build:.*/i'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err = _err
|
||||
}
|
||||
expect(err.message).toBe('RegExp flags are not supported in script command selector')
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import path from 'path'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { run } from '@pnpm/plugin-commands-script-runners'
|
||||
@@ -890,3 +891,89 @@ test('`pnpm -r --resume-from run` should executed from given package', async ()
|
||||
expect(output1).toContain('project-2')
|
||||
expect(output1).toContain('project-3')
|
||||
})
|
||||
|
||||
test('pnpm run with RegExp script selector should work on recursive', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
'build:a': 'node -e "require(\'fs\').writeFileSync(\'../output-build-1-a.txt\', \'1-a\', \'utf8\')"',
|
||||
'build:b': 'node -e "require(\'fs\').writeFileSync(\'../output-build-1-b.txt\', \'1-b\', \'utf8\')"',
|
||||
'build:c': 'node -e "require(\'fs\').writeFileSync(\'../output-build-1-c.txt\', \'1-c\', \'utf8\')"',
|
||||
build: 'node -e "require(\'fs\').writeFileSync(\'../output-build-1-a.txt\', \'should not run\', \'utf8\')"',
|
||||
'lint:a': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-1-a.txt\', \'1-a\', \'utf8\')"',
|
||||
'lint:b': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-1-b.txt\', \'1-b\', \'utf8\')"',
|
||||
'lint:c': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-1-c.txt\', \'1-c\', \'utf8\')"',
|
||||
lint: 'node -e "require(\'fs\').writeFileSync(\'../output-lint-1-a.txt\', \'should not run\', \'utf8\')"',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
'build:a': 'node -e "require(\'fs\').writeFileSync(\'../output-build-2-a.txt\', \'2-a\', \'utf8\')"',
|
||||
'build:b': 'node -e "require(\'fs\').writeFileSync(\'../output-build-2-b.txt\', \'2-b\', \'utf8\')"',
|
||||
'build:c': 'node -e "require(\'fs\').writeFileSync(\'../output-build-2-c.txt\', \'2-c\', \'utf8\')"',
|
||||
build: 'node -e "require(\'fs\').writeFileSync(\'../output-build-2-a.txt\', \'should not run\', \'utf8\')"',
|
||||
'lint:a': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-2-a.txt\', \'2-a\', \'utf8\')"',
|
||||
'lint:b': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-2-b.txt\', \'2-b\', \'utf8\')"',
|
||||
'lint:c': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-2-c.txt\', \'2-c\', \'utf8\')"',
|
||||
lint: 'node -e "require(\'fs\').writeFileSync(\'../output-lint-2-a.txt\', \'should not run\', \'utf8\')"',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
'build:a': 'node -e "require(\'fs\').writeFileSync(\'../output-build-3-a.txt\', \'3-a\', \'utf8\')"',
|
||||
'build:b': 'node -e "require(\'fs\').writeFileSync(\'../output-build-3-b.txt\', \'3-b\', \'utf8\')"',
|
||||
'build:c': 'node -e "require(\'fs\').writeFileSync(\'../output-build-3-c.txt\', \'3-c\', \'utf8\')"',
|
||||
build: 'node -e "require(\'fs\').writeFileSync(\'../output-build-3-a.txt\', \'should not run\', \'utf8\')"',
|
||||
'lint:a': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-3-a.txt\', \'3-a\', \'utf8\')"',
|
||||
'lint:b': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-3-b.txt\', \'3-b\', \'utf8\')"',
|
||||
'lint:c': 'node -e "require(\'fs\').writeFileSync(\'../output-lint-3-c.txt\', \'3-c\', \'utf8\')"',
|
||||
lint: 'node -e "require(\'fs\').writeFileSync(\'../output-lint-3-a.txt\', \'should not run\', \'utf8\')"',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await execa(pnpmBin, [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY_URL,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await run.handler({
|
||||
...DEFAULT_OPTS,
|
||||
...await readProjects(process.cwd(), [{ namePattern: '*' }]),
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
rootProjectManifest: {
|
||||
name: 'test-workspaces',
|
||||
private: true,
|
||||
},
|
||||
workspaceDir: process.cwd(),
|
||||
}, ['/^(lint|build):.*/'])
|
||||
expect(await fs.readFile('output-build-1-a.txt', { encoding: 'utf-8' })).toEqual('1-a')
|
||||
expect(await fs.readFile('output-build-1-b.txt', { encoding: 'utf-8' })).toEqual('1-b')
|
||||
expect(await fs.readFile('output-build-1-c.txt', { encoding: 'utf-8' })).toEqual('1-c')
|
||||
expect(await fs.readFile('output-build-2-a.txt', { encoding: 'utf-8' })).toEqual('2-a')
|
||||
expect(await fs.readFile('output-build-2-b.txt', { encoding: 'utf-8' })).toEqual('2-b')
|
||||
expect(await fs.readFile('output-build-2-c.txt', { encoding: 'utf-8' })).toEqual('2-c')
|
||||
expect(await fs.readFile('output-build-3-a.txt', { encoding: 'utf-8' })).toEqual('3-a')
|
||||
expect(await fs.readFile('output-build-3-b.txt', { encoding: 'utf-8' })).toEqual('3-b')
|
||||
expect(await fs.readFile('output-build-3-c.txt', { encoding: 'utf-8' })).toEqual('3-c')
|
||||
|
||||
expect(await fs.readFile('output-lint-1-a.txt', { encoding: 'utf-8' })).toEqual('1-a')
|
||||
expect(await fs.readFile('output-lint-1-b.txt', { encoding: 'utf-8' })).toEqual('1-b')
|
||||
expect(await fs.readFile('output-lint-1-c.txt', { encoding: 'utf-8' })).toEqual('1-c')
|
||||
expect(await fs.readFile('output-lint-2-a.txt', { encoding: 'utf-8' })).toEqual('2-a')
|
||||
expect(await fs.readFile('output-lint-2-b.txt', { encoding: 'utf-8' })).toEqual('2-b')
|
||||
expect(await fs.readFile('output-lint-2-c.txt', { encoding: 'utf-8' })).toEqual('2-c')
|
||||
expect(await fs.readFile('output-lint-3-a.txt', { encoding: 'utf-8' })).toEqual('3-a')
|
||||
expect(await fs.readFile('output-lint-3-b.txt', { encoding: 'utf-8' })).toEqual('3-b')
|
||||
expect(await fs.readFile('output-lint-3-c.txt', { encoding: 'utf-8' })).toEqual('3-c')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user