UNPKG

@rushstack/heft

Version:

Build all your JavaScript projects the same way: A way that works.

203 lines 10.8 kB
"use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HeftCommandLineParser = void 0; const node_os_1 = __importDefault(require("node:os")); const ts_command_line_1 = require("@rushstack/ts-command-line"); const node_core_library_1 = require("@rushstack/node-core-library"); const terminal_1 = require("@rushstack/terminal"); const MetricsCollector_1 = require("../metrics/MetricsCollector"); const HeftConfiguration_1 = require("../configuration/HeftConfiguration"); const InternalHeftSession_1 = require("../pluginFramework/InternalHeftSession"); const LoggingManager_1 = require("../pluginFramework/logging/LoggingManager"); const CleanAction_1 = require("./actions/CleanAction"); const PhaseAction_1 = require("./actions/PhaseAction"); const RunAction_1 = require("./actions/RunAction"); const AliasAction_1 = require("./actions/AliasAction"); const CliUtilities_1 = require("../utilities/CliUtilities"); const Constants_1 = require("../utilities/Constants"); const HEFT_TOOL_FILENAME = 'heft'; class HeftCommandLineParser extends ts_command_line_1.CommandLineParser { constructor() { var _a, _b; super({ toolFilename: HEFT_TOOL_FILENAME, toolDescription: 'Heft is a pluggable build system designed for web projects.' }); // Initialize the debug flag as a parameter on the tool itself this._debugFlag = this.defineFlagParameter({ parameterLongName: Constants_1.Constants.debugParameterLongName, description: 'Show the full call stack if an error occurs while executing the tool' }); // Initialize the unmanaged flag as a parameter on the tool itself. While this parameter // is only used during version selection, we need to support parsing it here so that we // don't throw due to an unrecognized parameter. this._unmanagedFlag = this.defineFlagParameter({ parameterLongName: Constants_1.Constants.unmanagedParameterLongName, description: 'Disables the Heft version selector: When Heft is invoked via the shell path, normally it' + " will examine the project's package.json dependencies and try to use the locally installed version" + ' of Heft. Specify "--unmanaged" to force the invoked version of Heft to be used. This is useful for' + ' example if you want to test a different version of Heft.' }); // Pre-initialize with known argument values to determine state of "--debug" const preInitializationArgumentValues = this._getPreInitializationArgumentValues(); this._debug = !!preInitializationArgumentValues.debug; // Enable debug and verbose logging if the "--debug" flag is set this._terminalProvider = new terminal_1.ConsoleTerminalProvider({ debugEnabled: this._debug, verboseEnabled: this._debug }); this.globalTerminal = new terminal_1.Terminal(this._terminalProvider); this._loggingManager = new LoggingManager_1.LoggingManager({ terminalProvider: this._terminalProvider }); if (this._debug) { // Enable printing stacktraces if the "--debug" flag is set this._loggingManager.enablePrintStacks(); node_core_library_1.InternalError.breakInDebugger = true; } const numberOfCores = (_b = (_a = node_os_1.default.availableParallelism) === null || _a === void 0 ? void 0 : _a.call(node_os_1.default)) !== null && _b !== void 0 ? _b : node_os_1.default.cpus().length; this._heftConfiguration = HeftConfiguration_1.HeftConfiguration.initialize({ cwd: process.cwd(), terminalProvider: this._terminalProvider, numberOfCores }); this._metricsCollector = new MetricsCollector_1.MetricsCollector(); } async executeAsync(args) { // Defensively set the exit code to 1 so if the tool crashes for whatever reason, // we'll have a nonzero exit code. process.exitCode = 1; try { this._normalizeCwd(); const internalHeftSession = await InternalHeftSession_1.InternalHeftSession.initializeAsync({ debug: this._debug, heftConfiguration: this._heftConfiguration, loggingManager: this._loggingManager, metricsCollector: this._metricsCollector }); this._internalHeftSession = internalHeftSession; const actionOptions = { internalHeftSession: internalHeftSession, terminal: this.globalTerminal, loggingManager: this._loggingManager, metricsCollector: this._metricsCollector, heftConfiguration: this._heftConfiguration }; // Add the clean action, the run action, and the individual phase actions this.addAction(new CleanAction_1.CleanAction(actionOptions)); this.addAction(new RunAction_1.RunAction(actionOptions)); for (const phase of internalHeftSession.phases) { this.addAction(new PhaseAction_1.PhaseAction({ ...actionOptions, phase })); } // Add the watch variant of the run action and the individual phase actions this.addAction(new RunAction_1.RunAction({ ...actionOptions, watch: true })); for (const phase of internalHeftSession.phases) { this.addAction(new PhaseAction_1.PhaseAction({ ...actionOptions, phase, watch: true })); } // Add the action aliases last, since we need the targets to be defined before we can add the aliases const aliasActions = []; for (const [aliasName, { actionName, defaultParameters }] of internalHeftSession.actionReferencesByAlias) { const existingAction = this.tryGetAction(aliasName); if (existingAction) { throw new Error(`The alias "${aliasName}" specified in heft.json cannot be used because an action ` + 'with that name already exists.'); } const targetAction = this.tryGetAction(actionName); if (!targetAction) { throw new Error(`The action "${actionName}" referred to by alias "${aliasName}" in heft.json could not be found.`); } aliasActions.push(new AliasAction_1.AliasAction({ terminal: this.globalTerminal, toolFilename: HEFT_TOOL_FILENAME, aliasName, targetAction, defaultParameters })); } // Add the alias actions. Do this in a second pass to disallow aliases that refer to other aliases. for (const aliasAction of aliasActions) { this.addAction(aliasAction); } return await super.executeAsync(args); } catch (e) { await this._reportErrorAndSetExitCodeAsync(e); return false; } } async onExecuteAsync() { try { const selectedAction = this.selectedAction; let commandName = ''; let unaliasedCommandName = ''; if (selectedAction) { commandName = selectedAction.actionName; if (selectedAction instanceof AliasAction_1.AliasAction) { unaliasedCommandName = selectedAction.targetAction.actionName; } else { unaliasedCommandName = selectedAction.actionName; } } this._internalHeftSession.parsedCommandLine = { commandName, unaliasedCommandName }; await super.onExecuteAsync(); } catch (e) { await this._reportErrorAndSetExitCodeAsync(e); } // If we make it here, things are fine and reset the exit code back to 0 process.exitCode = 0; } _normalizeCwd() { const buildFolder = this._heftConfiguration.buildFolderPath; const currentCwd = process.cwd(); if (currentCwd !== buildFolder) { // Update the CWD to the project's build root. Some tools, like Jest, use process.cwd() this.globalTerminal.writeVerboseLine(`CWD is "${currentCwd}". Normalizing to "${buildFolder}".`); // If `process.cwd()` and `buildFolder` differ only by casing on Windows, the chdir operation will not fix the casing, which is the entire purpose of the exercise. // As such, chdir to a different directory first. That directory needs to exist, so use the parent of the current directory. // This will not work if the current folder is the drive root, but that is a rather exotic case. process.chdir(__dirname); process.chdir(buildFolder); } } _getPreInitializationArgumentValues(args = process.argv) { if (!this._debugFlag) { // The `this._debugFlag` parameter (the parameter itself, not its value) // has not yet been defined. Parameters need to be defined before we // try to evaluate any parameters. This is to ensure that the // `--debug` flag is defined correctly before we do this not-so-rigorous // parameter parsing. throw new node_core_library_1.InternalError('parameters have not yet been defined.'); } const toolParameters = (0, CliUtilities_1.getToolParameterNamesFromArgs)(args); return { debug: toolParameters.has(this._debugFlag.longName), unmanaged: toolParameters.has(this._unmanagedFlag.longName) }; } async _reportErrorAndSetExitCodeAsync(error) { if (!(error instanceof node_core_library_1.AlreadyReportedError)) { this.globalTerminal.writeErrorLine(error.toString()); } if (this._debug) { this.globalTerminal.writeLine(); this.globalTerminal.writeErrorLine(error.stack); } const exitCode = process.exitCode; if (!exitCode || typeof exitCode !== 'number' || exitCode > 0) { process.exit(exitCode); } else { process.exit(1); } } } exports.HeftCommandLineParser = HeftCommandLineParser; //# sourceMappingURL=HeftCommandLineParser.js.map