mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
feat: add pnpm.requiredScripts config (#5802)
close #5569 Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
7
.changeset/bright-deers-return.md
Normal file
7
.changeset/bright-deers-return.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-script-runners": minor
|
||||
"@pnpm/types": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
New setting supported in the `package.json` that is in the root of the workspace: `pnpm.requiredScripts`. Scripts listed in this array will be required in each project of the worksapce. Otherwise, `pnpm -r run <script name>` will fail [#5569](https://github.com/pnpm/pnpm/issues/5569).
|
||||
@@ -17,6 +17,7 @@ export type RecursiveRunOpts = Pick<Config,
|
||||
| 'enablePrePostScripts'
|
||||
| 'unsafePerm'
|
||||
| 'rawConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'scriptsPrependNodePath'
|
||||
| 'scriptShell'
|
||||
| 'shellEmulator'
|
||||
@@ -57,6 +58,18 @@ export async function runRecursive (
|
||||
const existsPnp = existsInDir.bind(null, '.pnp.cjs')
|
||||
const workspacePnpPath = opts.workspaceDir && await existsPnp(opts.workspaceDir)
|
||||
|
||||
const requiredScripts = opts.rootProjectManifest?.pnpm?.requiredScripts ?? []
|
||||
if (requiredScripts.includes(scriptName)) {
|
||||
const missingScriptPackages: string[] = packageChunks
|
||||
.flat()
|
||||
.map((prefix) => opts.selectedProjectsGraph[prefix])
|
||||
.filter((pkg) => !pkg.package.manifest.scripts?.[scriptName])
|
||||
.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(', ')}`)
|
||||
}
|
||||
}
|
||||
|
||||
for (const chunk of packageChunks) {
|
||||
await Promise.all(chunk.map(async (prefix: string) =>
|
||||
limitRun(async () => {
|
||||
|
||||
@@ -784,3 +784,51 @@ test('`pnpm run -r` should avoid infinite recursion', async () => {
|
||||
expect(outputs1).toStrictEqual(['project-2'])
|
||||
expect(outputs2).toStrictEqual(['project-3'])
|
||||
})
|
||||
|
||||
test('`pnpm recursive run` should fail when no script in package with requiredScripts', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
build: 'echo 2',
|
||||
},
|
||||
dependencies: {
|
||||
'project-1': '1',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
dependencies: {
|
||||
'project-1': '1',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await run.handler({
|
||||
...DEFAULT_OPTS,
|
||||
...await readProjects(process.cwd(), [{ namePattern: '*' }]),
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
rootProjectManifest: {
|
||||
name: 'test-workspaces',
|
||||
private: true,
|
||||
pnpm: {
|
||||
requiredScripts: ['build'],
|
||||
},
|
||||
},
|
||||
workspaceDir: process.cwd(),
|
||||
}, ['build'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err = _err
|
||||
}
|
||||
expect(err.message).toContain('Missing script "build" in packages: project-1, project-3')
|
||||
expect(err.code).toBe('ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT')
|
||||
})
|
||||
|
||||
@@ -135,6 +135,7 @@ export type ProjectManifest = BaseManifest & {
|
||||
auditConfig?: {
|
||||
ignoreCves?: string[]
|
||||
}
|
||||
requiredScripts?: string[]
|
||||
}
|
||||
private?: boolean
|
||||
resolutions?: Record<string, string>
|
||||
|
||||
Reference in New Issue
Block a user