New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support to run multi script commands specified with regex selector #5871
Changes from 10 commits
413fb09
fd2ebe5
25fae54
776dd24
8424515
f256571
b92e3bb
514986f
bd0aef2
b7e6428
7608448
ec8e4ee
bc23e6b
274a076
671a063
3864f03
e140531
f6fe3cf
8a70297
f917b1f
c02be84
4802e46
421a693
7db6957
acf55fd
cbc19e7
0d81ca4
2b337da
67f62a1
d9bd169
46522b3
1485f79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@pnpm/plugin-commands-script-runners": minor | ||
--- | ||
|
||
feat: support wildcard selector to specify multiple scripts to execute" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add more details. This text is going to be on the release page. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4802e46 👍 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import path from 'path' | ||
import pLimit from 'p-limit' | ||
import { | ||
docsUrl, | ||
readProjectManifestOnly, | ||
|
@@ -43,13 +44,22 @@ export const RESUME_FROM_OPTION_HELP = { | |
name: '--resume-from', | ||
} | ||
|
||
export const SEQUENTIAL_OPTION_HELP = { | ||
description: 'Run a multiple scripts specified with workspace selector or a wildcard script selector sequential.\ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description of this option is confusing because we have a parallel option but this is unrelated to that. I'd just write that scripts are executed one at a time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. I updated this help message more simple 3864f03 |
||
This is the preferred flag for tasks which has dependency on execution order.', | ||
name: '--sequential', | ||
} | ||
|
||
export const shorthands = { | ||
parallel: [ | ||
'--workspace-concurrency=Infinity', | ||
'--no-sort', | ||
'--stream', | ||
'--recursive', | ||
], | ||
sequential: [ | ||
'--workspace-concurrency=1', | ||
], | ||
} | ||
|
||
export function rcOptionsTypes () { | ||
|
@@ -112,6 +122,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 +170,13 @@ export async function handler ( | |
: undefined | ||
return printProjectCommands(manifest, rootManifest ?? undefined) | ||
} | ||
if (scriptName !== 'start' && !manifest.scripts?.[scriptName]) { | ||
|
||
const multiScriptSelectorSpecified = scriptName.slice(-2) === ':*' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand this. You switched to a regex but still use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For example, a regular expression However I rethought about it and this behavior is against intention as above description and if we want to use RegExp for script matching, a detailed condition judgement process is needed and I thought it is clearer and more maintainable to match script name with simple string prefix matching ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After all, |
||
// extract XXX:YYY: from XXX:YYY:ZZZ | ||
const multiScriptSelectorRegExp = new RegExp(scriptName) | ||
const specifiedScriptsWithWildcardSelector = Object.keys(manifest.scripts ?? {}).filter(script => script.match(multiScriptSelectorRegExp)) | ||
|
||
if (scriptName !== 'start' && !manifest.scripts?.[scriptName] && !(multiScriptSelectorSpecified && specifiedScriptsWithWildcardSelector.length > 0)) { | ||
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,7 +187,7 @@ export async function handler ( | |
} | ||
if (opts.workspaceDir) { | ||
const { manifest: rootManifest } = await tryReadProjectManifest(opts.workspaceDir, opts) | ||
if (rootManifest?.scripts?.[scriptName]) { | ||
if (rootManifest?.scripts?.[scriptName] && !(multiScriptSelectorSpecified && specifiedScriptsWithWildcardSelector.length > 0)) { | ||
throw new PnpmError('NO_SCRIPT', `Missing script: ${scriptName}`, { | ||
hint: `But ${scriptName} is present in the root of the workspace, | ||
so you may run "pnpm -w run ${scriptName}"`, | ||
|
@@ -203,20 +220,12 @@ 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) | ||
if (multiScriptSelectorSpecified) { | ||
const limitRun = pLimit(opts.workspaceConcurrency ?? 4) | ||
|
||
await Promise.all(specifiedScriptsWithWildcardSelector.map(script => limitRun(() => runScript(script, manifest, lifecycleOpts, { enablePrePostScripts: opts.enablePrePostScripts ?? false }, passedThruArgs)))) | ||
} else { | ||
await runScript(scriptName, manifest, lifecycleOpts, { enablePrePostScripts: opts.enablePrePostScripts ?? false }, passedThruArgs) | ||
} | ||
} catch (err: any) { // eslint-disable-line | ||
if (opts.bail !== false) { | ||
|
@@ -300,6 +309,28 @@ ${renderCommands(rootScripts)}` | |
return output | ||
} | ||
|
||
export interface RunScriptOptions { | ||
enablePrePostScripts: boolean | ||
} | ||
|
||
export const runScript: (scriptName: string, manifest: ProjectManifest, lifecycleOpts: RunLifecycleHookOptions, runScriptOptions: RunScriptOptions, passedThruArgs: string[]) => Promise<void> = async function (scriptName, manifest, lifecycleOpts, runScriptOptions, passedThruArgs) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move all the arguments to the options except make options the first argument. Then you'll able to bind the options and only run the new function with script name in the map at line 224 const _runScript = runScript.bind(null, {
....
})
await Promise.all(specifiedScripts.map(script => limitRun(() => runScript(script)))) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure I understand your point properly, but is this what you mean? f917b1f |
||
if ( | ||
runScriptOptions.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 ( | ||
runScriptOptions.enablePrePostScripts && | ||
manifest.scripts?.[`post${scriptName}`] && | ||
!manifest.scripts[scriptName].includes(`post${scriptName}`) | ||
) { | ||
await runLifecycleHook(`post${scriptName}`, manifest, lifecycleOpts) | ||
} | ||
} | ||
|
||
function renderCommands (commands: string[][]) { | ||
return commands.map(([scriptName, script]) => ` ${scriptName}\n ${script}`).join('\n') | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also add
pnpm: minor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
4802e46 👍