-
Notifications
You must be signed in to change notification settings - Fork 2.7k
/
Copy pathrun.js
140 lines (123 loc) · 4.36 KB
/
run.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* @flow */
import type {Reporter} from '../../reporters/index.js';
import type Config from '../../config.js';
import {execCommand, makeEnv} from '../../util/execute-lifecycle-script.js';
import {MessageError} from '../../errors.js';
import {registries} from '../../resolvers/index.js';
import * as fs from '../../util/fs.js';
import map from '../../util/map.js';
const leven = require('leven');
const path = require('path');
const {quoteForShell, sh, unquoted} = require('puka');
export function setFlags(commander: Object) {
commander.description('Runs a defined package script.');
}
export function hasWrapper(commander: Object, args: Array<string>): boolean {
return true;
}
export async function run(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> {
// build up a list of possible scripts
const pkg = await config.readManifest(config.cwd);
const scripts = map();
const binCommands = [];
const visitedBinFolders = new Set();
let pkgCommands = [];
for (const registry of Object.keys(registries)) {
const binFolder = path.join(config.cwd, config.registries[registry].folder, '.bin');
if (!visitedBinFolders.has(binFolder)) {
if (await fs.exists(binFolder)) {
for (const name of await fs.readdir(binFolder)) {
binCommands.push(name);
scripts[name] = quoteForShell(path.join(binFolder, name));
}
}
visitedBinFolders.add(binFolder);
}
}
const pkgScripts = pkg.scripts;
const cmdHints = {};
if (pkgScripts) {
// inherit `scripts` from manifest
pkgCommands = Object.keys(pkgScripts).sort();
// add command hints (what the actual yarn command will do)
for (const cmd of pkgCommands) {
cmdHints[cmd] = pkgScripts[cmd] || '';
}
Object.assign(scripts, pkgScripts);
}
async function runCommand(args): Promise<void> {
const action = args.shift();
// build up list of commands
const cmds = [];
if (pkgScripts && action in pkgScripts) {
const preAction = `pre${action}`;
if (preAction in pkgScripts) {
cmds.push([preAction, pkgScripts[preAction]]);
}
cmds.push([action, scripts[action]]);
const postAction = `post${action}`;
if (postAction in pkgScripts) {
cmds.push([postAction, pkgScripts[postAction]]);
}
} else if (scripts[action]) {
cmds.push([action, scripts[action]]);
}
if (cmds.length) {
// Disable wrapper in executed commands
process.env.YARN_WRAP_OUTPUT = 'false';
for (const [stage, cmd] of cmds) {
// only tack on trailing arguments for default script, ignore for pre and post - #1595
const cmdWithArgs = stage === action ? sh`${unquoted(cmd)} ${args}` : cmd;
const customShell = config.getOption('script-shell');
await execCommand({
stage,
config,
cmd: cmdWithArgs,
cwd: config.cwd,
isInteractive: true,
customShell: customShell ? String(customShell) : undefined,
});
}
} else if (action === 'env') {
reporter.log(JSON.stringify(await makeEnv('env', config.cwd, config), null, 2), {force: true});
} else {
let suggestion;
for (const commandName in scripts) {
const steps = leven(commandName, action);
if (steps < 2) {
suggestion = commandName;
}
}
let msg = `Command ${JSON.stringify(action)} not found.`;
if (suggestion) {
msg += ` Did you mean ${JSON.stringify(suggestion)}?`;
}
throw new MessageError(msg);
}
}
// list possible scripts if none specified
if (args.length === 0) {
if (binCommands.length) {
reporter.info(`${reporter.lang('binCommands') + binCommands.join(', ')}`);
} else {
reporter.error(reporter.lang('noBinAvailable'));
}
if (pkgCommands.length) {
reporter.info(`${reporter.lang('possibleCommands')}`);
reporter.list('possibleCommands', pkgCommands, cmdHints);
if (!flags.nonInteractive) {
await reporter
.question(reporter.lang('commandQuestion'))
.then(
answer => runCommand(answer.trim().split(' ')),
() => reporter.error(reporter.lang('commandNotSpecified')),
);
}
} else {
reporter.error(reporter.lang('noScriptsAvailable'));
}
return Promise.resolve();
} else {
return runCommand(args);
}
}