@angular-devkit/architect-cli
Version:
Angular Architect CLI
222 lines (217 loc) • 9.04 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const architect_1 = require("@angular-devkit/architect");
const node_1 = require("@angular-devkit/architect/node");
const core_1 = require("@angular-devkit/core");
const node_2 = require("@angular-devkit/core/node");
const ansiColors = __importStar(require("ansi-colors"));
const node_fs_1 = require("node:fs");
const path = __importStar(require("node:path"));
const yargs_parser_1 = __importStar(require("yargs-parser"));
const progress_1 = require("../src/progress");
function findUp(names, from) {
if (!Array.isArray(names)) {
names = [names];
}
const root = path.parse(from).root;
let currentDir = from;
while (currentDir && currentDir !== root) {
for (const name of names) {
const p = path.join(currentDir, name);
if ((0, node_fs_1.existsSync)(p)) {
return p;
}
}
currentDir = path.dirname(currentDir);
}
return null;
}
/**
* Show usage of the CLI tool, and exit the process.
*/
function usage(logger, exitCode = 0) {
logger.info(core_1.tags.stripIndent `
architect [project][:target][:configuration] [options, ...]
Run a project target.
If project/target/configuration are not specified, the workspace defaults will be used.
Options:
--help Show available options for project target.
Shows this message instead when ran without the run argument.
Any additional option is passed the target, overriding existing options.
`);
return process.exit(exitCode);
}
function _targetStringFromTarget({ project, target, configuration }) {
return `${project}:${target}${configuration !== undefined ? ':' + configuration : ''}`;
}
// Create a separate instance to prevent unintended global changes to the color configuration
const colors = ansiColors.create();
async function _executeTarget(parentLogger, workspace, root, argv, registry) {
const architectHost = new node_1.WorkspaceNodeModulesArchitectHost(workspace, root);
const architect = new architect_1.Architect(architectHost, registry);
// Split a target into its parts.
const { _: [targetStr = ''], help, ...options } = argv;
const [project, target, configuration] = targetStr.toString().split(':');
const targetSpec = { project, target, configuration };
const logger = new core_1.logging.Logger('jobs');
const logs = [];
logger.subscribe((entry) => logs.push({ ...entry, message: `${entry.name}: ` + entry.message }));
// Camelize options as yargs will return the object in kebab-case when camel casing is disabled.
const camelCasedOptions = {};
for (const [key, value] of Object.entries(options)) {
if (/[A-Z]/.test(key)) {
throw new Error(`Unknown argument ${key}. Did you mean ${(0, yargs_parser_1.decamelize)(key)}?`);
}
camelCasedOptions[(0, yargs_parser_1.camelCase)(key)] = value;
}
const run = await architect.scheduleTarget(targetSpec, camelCasedOptions, { logger });
const bars = new progress_1.MultiProgressBar(':name :bar (:current/:total) :status');
run.progress.subscribe((update) => {
const data = bars.get(update.id) || {
id: update.id,
builder: update.builder,
target: update.target,
status: update.status || '',
name: ((update.target ? _targetStringFromTarget(update.target) : update.builder.name) +
' '.repeat(80)).substring(0, 40),
};
if (update.status !== undefined) {
data.status = update.status;
}
switch (update.state) {
case architect_1.BuilderProgressState.Error:
data.status = 'Error: ' + update.error;
bars.update(update.id, data);
break;
case architect_1.BuilderProgressState.Stopped:
data.status = 'Done.';
bars.complete(update.id);
bars.update(update.id, data, update.total, update.total);
break;
case architect_1.BuilderProgressState.Waiting:
bars.update(update.id, data);
break;
case architect_1.BuilderProgressState.Running:
bars.update(update.id, data, update.current, update.total);
break;
}
bars.render();
});
// Wait for full completion of the builder.
try {
const result = await run.lastOutput;
if (result.success) {
parentLogger.info(colors.green('SUCCESS'));
}
else {
parentLogger.info(colors.red('FAILURE'));
}
parentLogger.info('Result: ' + JSON.stringify({ ...result, info: undefined }, null, 4));
parentLogger.info('\nLogs:');
logs.forEach((l) => parentLogger.next(l));
logs.splice(0);
await run.stop();
bars.terminate();
return result.success ? 0 : 1;
}
catch (err) {
parentLogger.info(colors.red('ERROR'));
parentLogger.info('\nLogs:');
logs.forEach((l) => parentLogger.next(l));
parentLogger.fatal('Exception:');
parentLogger.fatal((err instanceof Error && err.stack) || `${err}`);
return 2;
}
}
async function main(args) {
/** Parse the command line. */
const argv = (0, yargs_parser_1.default)(args, {
boolean: ['help'],
configuration: {
'dot-notation': false,
'boolean-negation': true,
'strip-aliased': true,
'camel-case-expansion': false,
},
});
/** Create the DevKit Logger used through the CLI. */
const logger = (0, node_2.createConsoleLogger)(argv['verbose'], process.stdout, process.stderr, {
info: (s) => s,
debug: (s) => s,
warn: (s) => colors.bold.yellow(s),
error: (s) => colors.bold.red(s),
fatal: (s) => colors.bold.red(s),
});
// Check the target.
const targetStr = argv._[0] || '';
if (!targetStr || argv.help) {
// Show architect usage if there's no target.
usage(logger);
}
// Load workspace configuration file.
const currentPath = process.cwd();
const configFileNames = ['angular.json', '.angular.json', 'workspace.json', '.workspace.json'];
const configFilePath = findUp(configFileNames, currentPath);
if (!configFilePath) {
logger.fatal(`Workspace configuration file (${configFileNames.join(', ')}) cannot be found in ` +
`'${currentPath}' or in parent directories.`);
return 3;
}
const root = path.dirname(configFilePath);
const registry = new core_1.schema.CoreSchemaRegistry();
registry.addPostTransform(core_1.schema.transforms.addUndefinedDefaults);
// Show usage of deprecated options
registry.useXDeprecatedProvider((msg) => logger.warn(msg));
const { workspace } = await core_1.workspaces.readWorkspace(configFilePath, core_1.workspaces.createWorkspaceHost(new node_2.NodeJsSyncHost()));
// Clear the console.
process.stdout.write('\u001Bc');
return await _executeTarget(logger, workspace, root, argv, registry);
}
main(process.argv.slice(2)).then((code) => {
process.exit(code);
}, (err) => {
// eslint-disable-next-line no-console
console.error('Error: ' + err.stack || err.message || err);
process.exit(-1);
});
;