You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
9.2 KiB
241 lines
9.2 KiB
"use strict";
|
|
/**
|
|
* @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.io/license
|
|
*/
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
}) : (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 (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.runCommand = void 0;
|
|
const core_1 = require("@angular-devkit/core");
|
|
const fs_1 = require("fs");
|
|
const path_1 = require("path");
|
|
const json_file_1 = require("../utilities/json-file");
|
|
const json_schema_1 = require("../utilities/json-schema");
|
|
const analytics_1 = require("./analytics");
|
|
const command_1 = require("./command");
|
|
const parser = __importStar(require("./parser"));
|
|
// NOTE: Update commands.json if changing this. It's still deep imported in one CI validation
|
|
const standardCommands = {
|
|
'add': '../commands/add.json',
|
|
'analytics': '../commands/analytics.json',
|
|
'build': '../commands/build.json',
|
|
'deploy': '../commands/deploy.json',
|
|
'config': '../commands/config.json',
|
|
'doc': '../commands/doc.json',
|
|
'e2e': '../commands/e2e.json',
|
|
'extract-i18n': '../commands/extract-i18n.json',
|
|
'make-this-awesome': '../commands/easter-egg.json',
|
|
'generate': '../commands/generate.json',
|
|
'help': '../commands/help.json',
|
|
'lint': '../commands/lint.json',
|
|
'new': '../commands/new.json',
|
|
'run': '../commands/run.json',
|
|
'serve': '../commands/serve.json',
|
|
'test': '../commands/test.json',
|
|
'update': '../commands/update.json',
|
|
'version': '../commands/version.json',
|
|
};
|
|
/**
|
|
* Create the analytics instance.
|
|
* @private
|
|
*/
|
|
async function _createAnalytics(workspace, skipPrompt = false) {
|
|
let config = await analytics_1.getGlobalAnalytics();
|
|
// If in workspace and global analytics is enabled, defer to workspace level
|
|
if (workspace && config) {
|
|
const skipAnalytics = skipPrompt ||
|
|
(process.env['NG_CLI_ANALYTICS'] &&
|
|
(process.env['NG_CLI_ANALYTICS'].toLowerCase() === 'false' ||
|
|
process.env['NG_CLI_ANALYTICS'] === '0'));
|
|
// TODO: This should honor the `no-interactive` option.
|
|
// It is currently not an `ng` option but rather only an option for specific commands.
|
|
// The concept of `ng`-wide options are needed to cleanly handle this.
|
|
if (!skipAnalytics && !(await analytics_1.hasWorkspaceAnalyticsConfiguration())) {
|
|
await analytics_1.promptProjectAnalytics();
|
|
}
|
|
config = await analytics_1.getWorkspaceAnalytics();
|
|
}
|
|
const maybeSharedAnalytics = await analytics_1.getSharedAnalytics();
|
|
if (config && maybeSharedAnalytics) {
|
|
return new core_1.analytics.MultiAnalytics([config, maybeSharedAnalytics]);
|
|
}
|
|
else if (config) {
|
|
return config;
|
|
}
|
|
else if (maybeSharedAnalytics) {
|
|
return maybeSharedAnalytics;
|
|
}
|
|
else {
|
|
return new core_1.analytics.NoopAnalytics();
|
|
}
|
|
}
|
|
async function loadCommandDescription(name, path, registry) {
|
|
const schemaPath = path_1.resolve(__dirname, path);
|
|
const schema = json_file_1.readAndParseJson(schemaPath);
|
|
if (!core_1.isJsonObject(schema)) {
|
|
throw new Error('Invalid command JSON loaded from ' + JSON.stringify(schemaPath));
|
|
}
|
|
return json_schema_1.parseJsonSchemaToCommandDescription(name, schemaPath, registry, schema);
|
|
}
|
|
/**
|
|
* Run a command.
|
|
* @param args Raw unparsed arguments.
|
|
* @param logger The logger to use.
|
|
* @param workspace Workspace information.
|
|
* @param commands The map of supported commands.
|
|
* @param options Additional options.
|
|
*/
|
|
async function runCommand(args, logger, workspace, commands = standardCommands, options = {
|
|
currentDirectory: process.cwd(),
|
|
}) {
|
|
var _a;
|
|
// This registry is exclusively used for flattening schemas, and not for validating.
|
|
const registry = new core_1.schema.CoreSchemaRegistry([]);
|
|
registry.registerUriHandler((uri) => {
|
|
if (uri.startsWith('ng-cli://')) {
|
|
const content = fs_1.readFileSync(path_1.join(__dirname, '..', uri.substr('ng-cli://'.length)), 'utf-8');
|
|
return Promise.resolve(JSON.parse(content));
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
});
|
|
let commandName = undefined;
|
|
for (let i = 0; i < args.length; i++) {
|
|
const arg = args[i];
|
|
if (!arg.startsWith('-')) {
|
|
commandName = arg;
|
|
args.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
let description = null;
|
|
// if no commands were found, use `help`.
|
|
if (!commandName) {
|
|
if (args.length === 1 && args[0] === '--version') {
|
|
commandName = 'version';
|
|
}
|
|
else {
|
|
commandName = 'help';
|
|
}
|
|
if (!(commandName in commands)) {
|
|
logger.error(core_1.tags.stripIndent `
|
|
The "${commandName}" command seems to be disabled.
|
|
This is an issue with the CLI itself. If you see this comment, please report it and
|
|
provide your repository.
|
|
`);
|
|
return 1;
|
|
}
|
|
}
|
|
if (commandName in commands) {
|
|
description = await loadCommandDescription(commandName, commands[commandName], registry);
|
|
}
|
|
else {
|
|
const commandNames = Object.keys(commands);
|
|
// Optimize loading for common aliases
|
|
if (commandName.length === 1) {
|
|
commandNames.sort((a, b) => {
|
|
const aMatch = a[0] === commandName;
|
|
const bMatch = b[0] === commandName;
|
|
if (aMatch && !bMatch) {
|
|
return -1;
|
|
}
|
|
else if (!aMatch && bMatch) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
});
|
|
}
|
|
for (const name of commandNames) {
|
|
const aliasDesc = await loadCommandDescription(name, commands[name], registry);
|
|
const aliases = aliasDesc.aliases;
|
|
if (aliases && aliases.some((alias) => alias === commandName)) {
|
|
commandName = name;
|
|
description = aliasDesc;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!description) {
|
|
const commandsDistance = {};
|
|
const name = commandName;
|
|
const allCommands = Object.keys(commands).sort((a, b) => {
|
|
if (!(a in commandsDistance)) {
|
|
commandsDistance[a] = core_1.strings.levenshtein(a, name);
|
|
}
|
|
if (!(b in commandsDistance)) {
|
|
commandsDistance[b] = core_1.strings.levenshtein(b, name);
|
|
}
|
|
return commandsDistance[a] - commandsDistance[b];
|
|
});
|
|
logger.error(core_1.tags.stripIndent `
|
|
The specified command ("${commandName}") is invalid. For a list of available options,
|
|
run "ng help".
|
|
|
|
Did you mean "${allCommands[0]}"?
|
|
`);
|
|
return 1;
|
|
}
|
|
try {
|
|
const parsedOptions = parser.parseArguments(args, description.options, logger);
|
|
command_1.Command.setCommandMap(async () => {
|
|
const map = {};
|
|
for (const [name, path] of Object.entries(commands)) {
|
|
map[name] = await loadCommandDescription(name, path, registry);
|
|
}
|
|
return map;
|
|
});
|
|
const analytics = options.analytics || (await _createAnalytics(!!workspace, description.name === 'update'));
|
|
const context = {
|
|
workspace,
|
|
analytics,
|
|
currentDirectory: options.currentDirectory,
|
|
root: (_a = workspace === null || workspace === void 0 ? void 0 : workspace.basePath) !== null && _a !== void 0 ? _a : options.currentDirectory,
|
|
};
|
|
const command = new description.impl(context, description, logger);
|
|
// Flush on an interval (if the event loop is waiting).
|
|
let analyticsFlushPromise = Promise.resolve();
|
|
const analyticsFlushInterval = setInterval(() => {
|
|
analyticsFlushPromise = analyticsFlushPromise.then(() => analytics.flush());
|
|
}, 1000);
|
|
const result = await command.validateAndRun(parsedOptions);
|
|
// Flush one last time.
|
|
clearInterval(analyticsFlushInterval);
|
|
await analyticsFlushPromise.then(() => analytics.flush());
|
|
return result;
|
|
}
|
|
catch (e) {
|
|
if (e instanceof parser.ParseArgumentException) {
|
|
logger.fatal('Cannot parse arguments. See below for the reasons.');
|
|
logger.fatal(' ' + e.comments.join('\n '));
|
|
return 1;
|
|
}
|
|
else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
exports.runCommand = runCommand;
|