UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

246 lines (245 loc) • 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandsService = void 0; const jaroWinklerDistance = require("../vendor/jaro-winkler_distance"); const helpers = require("../helpers"); const os_1 = require("os"); const _ = require("lodash"); const yok_1 = require("../yok"); class CommandArgumentsValidationHelper { constructor(isValid, _remainingArguments) { this.isValid = isValid; this.remainingArguments = _remainingArguments.slice(); } } class CommandsService { get currentCommandData() { return _.last(this.commands); } constructor($errors, $hooksService, $injector, $logger, $options, $staticConfig, $extensibilityService, $optionsTracker) { this.$errors = $errors; this.$hooksService = $hooksService; this.$injector = $injector; this.$logger = $logger; this.$options = $options; this.$staticConfig = $staticConfig; this.$extensibilityService = $extensibilityService; this.$optionsTracker = $optionsTracker; this.commands = []; } allCommands(opts) { const commands = this.$injector.getRegisteredCommandsNames(opts.includeDevCommands); return _.reject(commands, (command) => _.includes(command, "|")); } async executeCommandUnchecked(commandName, commandArguments) { this.commands.push({ commandName, commandArguments }); const command = this.$injector.resolveCommand(commandName); if (command) { if (!this.$staticConfig.disableAnalytics && !command.disableAnalytics && !this.$options.disableAnalytics) { const analyticsService = this.$injector.resolve("analyticsService"); await analyticsService.checkConsent(); const beautifiedCommandName = this.beautifyCommandName(commandName).replace(/\|/g, " "); const googleAnalyticsPageData = { googleAnalyticsDataType: "pageview", path: beautifiedCommandName, title: beautifiedCommandName, }; await analyticsService.trackInGoogleAnalytics(googleAnalyticsPageData); await this.$optionsTracker.trackOptions(this.$options); } const shouldExecuteHooks = !this.$staticConfig.disableCommandHooks && (command.enableHooks === undefined || command.enableHooks === true); if (shouldExecuteHooks) { const hierarchicalCommandName = this.$injector.buildHierarchicalCommand(commandName, commandArguments); if (hierarchicalCommandName) { commandName = helpers.stringReplaceAll(hierarchicalCommandName.commandName, "|*", "-"); commandName = helpers.stringReplaceAll(commandName, "|", "-"); } await this.$hooksService.executeBeforeHooks(commandName); } await command.execute(commandArguments); if (command.postCommandAction) { await command.postCommandAction(commandArguments); } if (shouldExecuteHooks) { await this.$hooksService.executeAfterHooks(commandName); } this.commands.pop(); return true; } this.commands.pop(); return false; } printHelpSuggestion(commandName) { const command = commandName ? helpers.stringReplaceAll(this.beautifyCommandName(commandName), "|", " ") + " " : ""; const commandHelp = `ns ${command}--help`; this.$logger.printMarkdown(`__Run \`${commandHelp}\` for more information.__`); return; } async executeCommandAction(commandName, commandArguments, action) { return this.$errors.beginCommand(() => action.apply(this, [commandName, commandArguments]), () => this.printHelpSuggestion(commandName)); } async tryExecuteCommandAction(commandName, commandArguments) { const command = this.$injector.resolveCommand(commandName); if (!command || !command.isHierarchicalCommand) { const dashedOptions = command ? command.dashedOptions : null; this.$options.validateOptions(dashedOptions); } return this.canExecuteCommand(commandName, commandArguments); } async tryExecuteCommand(commandName, commandArguments) { const canExecuteResult = await this.executeCommandAction(commandName, commandArguments, this.tryExecuteCommandAction); const canExecute = typeof canExecuteResult === "object" ? canExecuteResult.canExecute : canExecuteResult; if (canExecute) { await this.executeCommandAction(commandName, commandArguments, this.executeCommandUnchecked); } else { const command = this.$injector.resolveCommand(commandName); if (command) { let commandWithArgs = commandName; if (commandArguments && commandArguments.length) { commandWithArgs += ` ${commandArguments.join(" ")}`; } this.$logger.error(`Command '${commandWithArgs}' cannot be executed.`); await this.printHelpSuggestion(commandName); } } } async canExecuteCommand(commandName, commandArguments, isDynamicCommand) { const command = this.$injector.resolveCommand(commandName); const beautifiedName = helpers.stringReplaceAll(commandName, "|", " "); if (command) { if (command.isDisabled) { this.$errors.fail("This command is not applicable to your environment."); } if (command.canExecute) { return await command.canExecute(commandArguments); } if (await this.$injector.isValidHierarchicalCommand(commandName, commandArguments)) { return true; } if (await this.validateCommandArguments(command, commandArguments)) { return true; } this.$errors.fail(`Unable to execute command '${beautifiedName}'.`); return false; } const commandInfo = { inputStrings: [commandName, ...commandArguments], commandDelimiter: "|", defaultCommandDelimiter: "|*", }; const extensionData = await this.$extensibilityService.getExtensionNameWhereCommandIsRegistered(commandInfo); if (extensionData) { this.$logger.warn(extensionData.installationMessage); } else { this.$logger.error("Unknown command '%s'.", beautifiedName); await this.printHelpSuggestion(); this.tryMatchCommand(commandName); } return false; } async validateMandatoryParams(commandArguments, mandatoryParams) { const commandArgsHelper = new CommandArgumentsValidationHelper(true, commandArguments); if (mandatoryParams.length > 0) { if (mandatoryParams.length > commandArguments.length) { const customErrorMessages = _.map(mandatoryParams, (mp) => mp.errorMessage); customErrorMessages.splice(0, 0, "You need to provide all the required parameters."); this.$errors.failWithHelp(customErrorMessages.join(os_1.EOL)); } for (let mandatoryParamIndex = 0; mandatoryParamIndex < mandatoryParams.length; ++mandatoryParamIndex) { const mandatoryParam = mandatoryParams[mandatoryParamIndex]; let argument = null; for (let remainingArgsIndex = 0; remainingArgsIndex < commandArgsHelper.remainingArguments.length; ++remainingArgsIndex) { const c = commandArgsHelper.remainingArguments[remainingArgsIndex]; if (await mandatoryParam.validate(c)) { argument = c; break; } } if (argument) { helpers.remove(commandArgsHelper.remainingArguments, (arg) => arg === argument); } else { this.$errors.failWithHelp("Missing mandatory parameter."); } } } return commandArgsHelper; } async validateCommandArguments(command, commandArguments) { const mandatoryParams = _.filter(command.allowedParameters, (param) => param.mandatory); const commandArgsHelper = await this.validateMandatoryParams(commandArguments, mandatoryParams); if (!commandArgsHelper.isValid) { return false; } if (!command.allowedParameters || command.allowedParameters.length === 0) { if (commandArguments.length > 0) { this.$errors.failWithHelp("This command doesn't accept parameters."); } } else { const unverifiedAllowedParams = command.allowedParameters.filter((param) => !param.mandatory); for (let remainingArgsIndex = 0; remainingArgsIndex < commandArgsHelper.remainingArguments.length; ++remainingArgsIndex) { const argument = commandArgsHelper.remainingArguments[remainingArgsIndex]; let parameter = null; for (let unverifiedIndex = 0; unverifiedIndex < unverifiedAllowedParams.length; ++unverifiedIndex) { const c = unverifiedAllowedParams[unverifiedIndex]; if (await c.validate(argument)) { parameter = c; break; } } if (parameter) { const index = unverifiedAllowedParams.indexOf(parameter); unverifiedAllowedParams.splice(index, 1); } else { this.$errors.failWithHelp(`The parameter ${argument} is not valid for this command.`); } } } return true; } tryMatchCommand(commandName) { const allCommands = this.allCommands({ includeDevCommands: false }); let similarCommands = []; _.each(allCommands, (command) => { if (!this.$injector.isDefaultCommand(command)) { command = helpers.stringReplaceAll(command, "|", " "); const distance = jaroWinklerDistance(commandName, command); if (commandName.length > 3 && command.indexOf(commandName) !== -1) { similarCommands.push({ rating: 1, name: command }); } else if (distance >= 0.65) { similarCommands.push({ rating: distance, name: command }); } } }); similarCommands = _.sortBy(similarCommands, (command) => { return -command.rating; }).slice(0, 5); if (similarCommands.length > 0) { const message = ["Did you mean?"]; _.each(similarCommands, (command) => { message.push("\t" + command.name); }); this.$logger.fatal(message.join("\n")); } } beautifyCommandName(commandName) { if (commandName.indexOf("*") > 0) { return commandName.substring(0, commandName.indexOf("|")); } return commandName; } } exports.CommandsService = CommandsService; yok_1.injector.register("commandsService", CommandsService);