Skip to content

Commit

Permalink
feat: add pnpm.requiredScripts config (#5802)
Browse files Browse the repository at this point in the history
close #5569

Co-authored-by: Zoltan Kochan <z@kochan.io>
  • Loading branch information
await-ovo and zkochan committed Dec 20, 2022
1 parent 2458741 commit b77651d
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .changeset/bright-deers-return.md
@@ -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).
13 changes: 13 additions & 0 deletions exec/plugin-commands-script-runners/src/runRecursive.ts
Expand Up @@ -17,6 +17,7 @@ export type RecursiveRunOpts = Pick<Config,
| 'enablePrePostScripts'
| 'unsafePerm'
| 'rawConfig'
| 'rootProjectManifest'
| 'scriptsPrependNodePath'
| 'scriptShell'
| 'shellEmulator'
Expand Down Expand Up @@ -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 () => {
Expand Down
48 changes: 48 additions & 0 deletions exec/plugin-commands-script-runners/test/runRecursive.ts
Expand Up @@ -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')
})
1 change: 1 addition & 0 deletions packages/types/src/package.ts
Expand Up @@ -135,6 +135,7 @@ export type ProjectManifest = BaseManifest & {
auditConfig?: {
ignoreCves?: string[]
}
requiredScripts?: string[]
}
private?: boolean
resolutions?: Record<string, string>
Expand Down

0 comments on commit b77651d

Please sign in to comment.