1 | import * as path from 'path';
|
2 | import { Command } from 'commander';
|
3 | import * as fs from 'fs';
|
4 | import invariant from 'invariant';
|
5 | import loudRejection from 'loud-rejection';
|
6 | import semver from 'semver';
|
7 | import { ConsoleReporter, JSONReporter } from './reporters/index.js';
|
8 | import { commands } from './commands/index.js';
|
9 | import * as helpCommand from './commands/help.js';
|
10 | import * as constants from './constants.js';
|
11 | import { MessageError } from '@pika/types';
|
12 | import Config from './config.js';
|
13 | import handleSignals from './util/signal-handler.js';
|
14 | import { boolify, boolifyWithDefault } from './util/conversion.js';
|
15 | import map from './util/map.js';
|
16 | import stripBOM from 'strip-bom';
|
17 | import uri2path from 'file-uri-to-path';
|
18 | const commander = new Command();
|
19 |
|
20 | const currentFilename = uri2path(import.meta.url);
|
21 | function getVersion() {
|
22 | const packageJsonContent = fs.readFileSync(path.resolve(currentFilename, '../../package.json'), { encoding: 'utf-8' });
|
23 | const { version } = map(JSON.parse(stripBOM(packageJsonContent)));
|
24 | return version;
|
25 | }
|
26 | function findProjectRoot(base) {
|
27 | let prev = null;
|
28 | let dir = base;
|
29 | do {
|
30 | if (fs.existsSync(path.join(dir, constants.NODE_PACKAGE_JSON))) {
|
31 | return dir;
|
32 | }
|
33 | prev = dir;
|
34 | dir = path.dirname(dir);
|
35 | } while (dir !== prev);
|
36 | return base;
|
37 | }
|
38 | export async function main({ startArgs, args, endArgs, }) {
|
39 | const version = getVersion();
|
40 | loudRejection();
|
41 | handleSignals();
|
42 |
|
43 | commander.version(version, '-v, --version');
|
44 | commander.usage('[command] [flags]');
|
45 | commander.option('--verbose', 'output verbose messages on internal operations');
|
46 | commander.option('--json', 'format Pika log messages as lines of JSON (see jsonlines.org)');
|
47 |
|
48 |
|
49 | commander.option('--emoji [bool]', 'enable emoji in output', boolify, process.platform === 'darwin' || process.env.TERM_PROGRAM === 'Hyper' || process.env.TERM_PROGRAM === 'HyperTerm');
|
50 | commander.option('-s, --silent', 'skip Pika console logs, other types of logs (script output) will be printed');
|
51 | commander.option('--cwd <cwd>', 'working directory to use', process.cwd());
|
52 | commander.option('--no-progress', 'disable progress bar');
|
53 | commander.option('--no-node-version-check', 'do not warn when using a potentially unsupported Node version');
|
54 | commander.option('--pipeline <pipeline>', 'the build pipeline to run');
|
55 |
|
56 | if (args[0] === '-v') {
|
57 | console.log(version.trim());
|
58 | process.exitCode = 0;
|
59 | return;
|
60 | }
|
61 |
|
62 | const firstNonFlagIndex = args.findIndex((arg, idx, arr) => {
|
63 | const isOption = arg.startsWith('-');
|
64 | const prev = idx > 0 && arr[idx - 1];
|
65 | const prevOption = prev && prev.startsWith('-') && commander.optionFor(prev);
|
66 | const boundToPrevOption = prevOption && (prevOption.optional || prevOption.required);
|
67 | return !isOption && !boundToPrevOption;
|
68 | });
|
69 | let preCommandArgs;
|
70 | let commandName = '';
|
71 | if (firstNonFlagIndex > -1) {
|
72 | preCommandArgs = args.slice(0, firstNonFlagIndex);
|
73 | commandName = args[firstNonFlagIndex];
|
74 | args = args.slice(firstNonFlagIndex + 1);
|
75 | }
|
76 | else {
|
77 | preCommandArgs = args;
|
78 | args = [];
|
79 | }
|
80 | let isKnownCommand = Object.prototype.hasOwnProperty.call(commands, commandName);
|
81 | const isHelp = arg => arg === '--help' || arg === '-h';
|
82 | const helpInPre = preCommandArgs.findIndex(isHelp);
|
83 | const helpInArgs = args.findIndex(isHelp);
|
84 | const setHelpMode = () => {
|
85 | if (isKnownCommand) {
|
86 | args.unshift(commandName);
|
87 | }
|
88 | commandName = 'help';
|
89 | isKnownCommand = true;
|
90 | };
|
91 | if (helpInPre > -1) {
|
92 | preCommandArgs.splice(helpInPre);
|
93 | setHelpMode();
|
94 | }
|
95 | else if (isKnownCommand && helpInArgs === 0) {
|
96 | args.splice(helpInArgs);
|
97 | setHelpMode();
|
98 | }
|
99 | if (!commandName) {
|
100 | commandName = 'help';
|
101 | isKnownCommand = true;
|
102 | }
|
103 | if (!isKnownCommand) {
|
104 |
|
105 | args.unshift(commandName);
|
106 | commandName = 'help';
|
107 | }
|
108 | const command = commandName === 'help' ? helpCommand : commands[commandName];
|
109 | commander.originalArgs = args;
|
110 | args = [...preCommandArgs, ...args];
|
111 | command.setFlags(commander);
|
112 | commander.parse([
|
113 | ...startArgs,
|
114 |
|
115 |
|
116 | 'this-arg-will-get-stripped-later',
|
117 | ...args,
|
118 | ]);
|
119 | commander.args = commander.args.concat(endArgs.slice(1));
|
120 |
|
121 | console.assert(commander.args.length >= 1);
|
122 | console.assert(commander.args[0] === 'this-arg-will-get-stripped-later');
|
123 | commander.args.shift();
|
124 |
|
125 | const Reporter = commander.json ? JSONReporter : ConsoleReporter;
|
126 | const reporter = new Reporter({
|
127 | emoji: process.stdout.isTTY && commander.emoji,
|
128 | verbose: commander.verbose,
|
129 | noProgress: !commander.progress,
|
130 | isSilent: boolifyWithDefault(process.env.PIKA_SILENT, false) || commander.silent,
|
131 | nonInteractive: commander.nonInteractive,
|
132 | });
|
133 | const exit = (exitCode = 0) => {
|
134 | if (exitCode === 0) {
|
135 | clearErrorReport();
|
136 | }
|
137 | process.exitCode = exitCode;
|
138 | reporter.close();
|
139 | };
|
140 | reporter.initPeakMemoryCounter();
|
141 | const outputWrapperEnabled = boolifyWithDefault(process.env.PIKA_WRAP_OUTPUT, true);
|
142 | const shouldWrapOutput = outputWrapperEnabled && !commander.json && command.hasWrapper(commander, commander.args);
|
143 |
|
144 | reporter.header(commandName, { name: '@pika/pack', version });
|
145 |
|
146 | if (commander.nodeVersionCheck && !semver.satisfies(process.versions.node, constants.SUPPORTED_NODE_VERSIONS)) {
|
147 | reporter.warn(reporter.lang('unsupportedNodeVersion', process.versions.node, constants.SUPPORTED_NODE_VERSIONS));
|
148 | }
|
149 | if (command.noArguments && commander.args.length) {
|
150 | reporter.error(reporter.lang('noArguments'));
|
151 |
|
152 | exit(1);
|
153 | return;
|
154 | }
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 | const run = () => {
|
161 | invariant(command, 'missing command');
|
162 |
|
163 |
|
164 |
|
165 | return command.run(config, reporter, commander, commander.args).then(exitCode => {
|
166 | if (shouldWrapOutput) {
|
167 | reporter.footer(false);
|
168 | }
|
169 | return exitCode;
|
170 | });
|
171 | };
|
172 | function onUnexpectedError(err) {
|
173 | function indent(str) {
|
174 | return '\n ' + str.trim().split('\n').join('\n ');
|
175 | }
|
176 | const log = [];
|
177 | log.push(`Arguments: ${indent(process.argv.join(' '))}`);
|
178 | log.push(`PATH: ${indent(process.env.PATH || 'undefined')}`);
|
179 | log.push(`Pika version: ${indent(version)}`);
|
180 | log.push(`Node version: ${indent(process.versions.node)}`);
|
181 | log.push(`Platform: ${indent(process.platform + ' ' + process.arch)}`);
|
182 | log.push(`Trace: ${indent(err.stack)}`);
|
183 | const errorReportLoc = writeErrorReport(log);
|
184 | reporter.error(reporter.lang('unexpectedError', err.message));
|
185 | if (errorReportLoc) {
|
186 | reporter.info(reporter.lang('bugReport', errorReportLoc));
|
187 | }
|
188 | }
|
189 | function writeErrorReport(log) {
|
190 | const errorReportLoc = path.join(config.cwd, 'pika-error.log');
|
191 | try {
|
192 | fs.writeFileSync(errorReportLoc, log.join('\n\n') + '\n');
|
193 | }
|
194 | catch (err) {
|
195 | reporter.error(reporter.lang('fileWriteError', errorReportLoc, err.message));
|
196 | return undefined;
|
197 | }
|
198 | return errorReportLoc;
|
199 | }
|
200 | function clearErrorReport() {
|
201 | const errorReportLoc = path.join(config.cwd, 'pika-error.log');
|
202 | if (fs.existsSync(errorReportLoc)) {
|
203 | try {
|
204 | fs.unlinkSync(errorReportLoc);
|
205 | }
|
206 | catch (err) {
|
207 | reporter.error(reporter.lang('fileDeleteError', errorReportLoc, err.message));
|
208 | return undefined;
|
209 | }
|
210 | }
|
211 | return errorReportLoc;
|
212 | }
|
213 | const cwd = command.shouldRunInCurrentCwd ? commander.cwd : findProjectRoot(commander.cwd);
|
214 | const config = new Config(reporter, cwd, commander);
|
215 | await config.loadPackageManifest();
|
216 | try {
|
217 |
|
218 | const noProgressConfig = false;
|
219 | if (noProgressConfig) {
|
220 | reporter.disableProgress();
|
221 | }
|
222 |
|
223 | reporter.verbose(`current time: ${new Date().toISOString()}`);
|
224 | return run().then(exit);
|
225 | }
|
226 | catch (err) {
|
227 | reporter.verbose(err.stack);
|
228 | if (err instanceof MessageError) {
|
229 | reporter.error(err.message);
|
230 | }
|
231 | else {
|
232 | onUnexpectedError(err);
|
233 | }
|
234 |
|
235 |
|
236 |
|
237 | return exit(1);
|
238 | }
|
239 | }
|
240 | async function start() {
|
241 |
|
242 | const doubleDashIndex = process.argv.findIndex(element => element === '--');
|
243 | const startArgs = process.argv.slice(0, 2);
|
244 | const args = process.argv.slice(2, doubleDashIndex === -1 ? process.argv.length : doubleDashIndex);
|
245 | const endArgs = doubleDashIndex === -1 ? [] : process.argv.slice(doubleDashIndex);
|
246 | await main({ startArgs, args, endArgs });
|
247 | }
|
248 | export const autoRun = false;
|
249 | export default start;
|