mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-06 22:18:17 -05:00
5
.changeset/smooth-drinks-do.md
Normal file
5
.changeset/smooth-drinks-do.md
Normal 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.
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user