feat(run): print info about root workspace scripts

ref #2853
PR #2859
This commit is contained in:
Zoltan Kochan
2020-09-15 11:24:06 +03:00
committed by GitHub
parent 634dfd13b3
commit d11442a578
3 changed files with 144 additions and 5 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-script-runners": minor
---
If a script is not found in the current project but is present in the root project of the workspace, notify the user about it in the hint of the error.

View File

@@ -1,4 +1,8 @@
import { docsUrl, readProjectManifestOnly } from '@pnpm/cli-utils'
import {
docsUrl,
readProjectManifestOnly,
tryReadProjectManifest,
} from '@pnpm/cli-utils'
import { CompletionFunc } from '@pnpm/command'
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { Config, types as allTypes } from '@pnpm/config'
@@ -124,10 +128,20 @@ export async function handler (
}
const manifest = await readProjectManifestOnly(dir, opts)
if (!scriptName) {
return printProjectCommands(manifest)
const rootManifest = opts.workspaceDir ? (await tryReadProjectManifest(opts.workspaceDir, opts)).manifest : undefined
return printProjectCommands(manifest, rootManifest ?? undefined)
}
if (scriptName !== 'start' && !manifest.scripts?.[scriptName]) {
if (opts.ifPresent) return
if (opts.workspaceDir) {
const { manifest: rootManifest } = await tryReadProjectManifest(opts.workspaceDir, opts)
if (rootManifest?.scripts?.[scriptName]) {
throw new PnpmError('NO_SCRIPT', `Missing script: ${scriptName}`, {
hint: `But ${scriptName} is present in the root of the workspace,
so you may run this command in: ${opts.workspaceDir}`,
})
}
}
throw new PnpmError('NO_SCRIPT', `Missing script: ${scriptName}`)
}
const lifecycleOpts = {
@@ -184,11 +198,14 @@ const ALL_LIFECYCLE_SCRIPTS = new Set([
'postshrinkwrap',
])
function printProjectCommands (manifest: ProjectManifest) {
function printProjectCommands (
manifest: ProjectManifest,
rootManifest?: ProjectManifest
) {
const lifecycleScripts = [] as string[][]
const otherScripts = [] as string[][]
for (const [scriptName, script] of R.toPairs(manifest.scripts ?? {})) {
for (const [scriptName, script] of Object.entries(manifest.scripts ?? {})) {
if (ALL_LIFECYCLE_SCRIPTS.has(scriptName)) {
lifecycleScripts.push([scriptName, script])
} else {
@@ -208,6 +225,15 @@ function printProjectCommands (manifest: ProjectManifest) {
if (output !== '') output += '\n\n'
output += `Commands available via "pnpm run":\n${renderCommands(otherScripts)}`
}
if (rootManifest?.scripts) {
const rootScripts = Object.entries(rootManifest.scripts)
.filter(([scriptName]) => !manifest.scripts?.[scriptName])
if (rootScripts.length) {
if (output !== '') output += '\n\n'
output += `Commands of the root workspace project (to run them, go to the root of the workspace):
${renderCommands(rootScripts)}`
}
}
return output
}

View File

@@ -1,18 +1,24 @@
/// <reference path="../../../typings/index.d.ts" />
import PnpmError from '@pnpm/error'
import { readProjects } from '@pnpm/filter-workspace-packages'
import {
restart,
run,
test as testCommand,
} from '@pnpm/plugin-commands-script-runners'
import prepare from '@pnpm/prepare'
import prepare, { preparePackages } from '@pnpm/prepare'
import './exec'
import './runCompletion'
import './runRecursive'
import './testRecursive'
import { DEFAULT_OPTS, REGISTRY } from './utils'
import execa = require('execa')
import fs = require('mz/fs')
import path = require('path')
import test = require('tape')
import writeYamlFile = require('write-yaml-file')
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.js')
test('pnpm run: returns correct exit code', async (t) => {
prepare(t, {
@@ -244,6 +250,60 @@ Commands available via "pnpm run":
t.end()
})
test('"pnpm run" prints the list of available commands, including commands of the root workspace project', async (t) => {
preparePackages(t, [
{
location: '.',
package: {
dependencies: {
'json-append': '1',
},
scripts: {
build: 'echo root',
test: 'test-all',
},
},
},
{
name: 'foo',
version: '1.0.0',
scripts: {
foo: 'echo hi',
test: 'ts-node test',
},
},
])
await writeYamlFile('pnpm-workspace.yaml', {})
const workspaceDir = process.cwd()
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
process.chdir('foo')
const output = await run.handler({
allProjects,
dir: process.cwd(),
extraBinPaths: [],
rawConfig: {},
selectedProjectsGraph,
workspaceDir,
}, [])
t.equal(output, `\
Lifecycle scripts:
test
ts-node test
Commands available via "pnpm run":
foo
echo hi
Commands of the root workspace project (to run them, go to the root of the workspace):
build
echo root`)
t.end()
})
test('pnpm run does not fail with --if-present even if the wanted script is not present', async (t) => {
prepare(t, {})
@@ -256,3 +316,51 @@ test('pnpm run does not fail with --if-present even if the wanted script is not
t.end()
})
test('if a script is not found but is present in the root, print an info message about it in the error message', async (t) => {
preparePackages(t, [
{
location: '.',
package: {
dependencies: {
'json-append': '1',
},
scripts: {
build: 'node -e "process.stdout.write(\'root\')" | json-append ./output.json',
},
},
},
{
name: 'foo',
version: '1.0.0',
},
])
await writeYamlFile('pnpm-workspace.yaml', {})
await execa(pnpmBin, [
'install',
'-r',
'--registry',
REGISTRY,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
let err!: PnpmError
try {
await run.handler({
...DEFAULT_OPTS,
allProjects,
dir: path.resolve('foo'),
selectedProjectsGraph,
workspaceDir: process.cwd(),
}, ['build'])
} catch (_err) {
err = _err
}
t.ok(err)
t.ok(err.hint.includes('But build is present in the root'))
t.end()
})