UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

434 lines • 17.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Yok = exports.injector = void 0; exports.register = register; exports.setGlobalInjector = setGlobalInjector; const path = require("path"); const _ = require("lodash"); const helpers_1 = require("./helpers"); const constants_1 = require("./constants"); let indent = ""; function trace(formatStr, ...args) { // uncomment following lines when debugging dependency injection // const items: any[] = []; // for (let _i = 1; _i < arguments.length; _i++) { // items[_i - 1] = arguments[_i]; // } // const util = require("util"); // console.log(util.format.apply(util, [indent + formatStr].concat(args))); } function pushIndent() { indent += " "; } function popIndent() { indent = indent.slice(0, -2); } function forEachName(names, action) { if (_.isString(names)) { action(names); } else { names.forEach(action); } } function register(...rest) { return function (target) { // TODO: Check if 'rest' has more arguments that have to be registered exports.injector.register(rest[0], target); }; } class Yok { constructor() { this.overrideAlreadyRequiredModule = false; this.COMMANDS_NAMESPACE = "commands"; this.KEY_COMMANDS_NAMESPACE = "keyCommands"; this.modules = {}; this.resolutionProgress = {}; this.hierarchicalCommands = {}; this.publicApi = { __modules__: {}, }; this.register("injector", this); } requireCommand(names, file) { forEachName(names, (commandName) => { const commands = commandName.split("|" /* CommandsDelimiters.HierarchicalCommand */); if (commands.length > 1) { if (_.startsWith(commands[1], "*") && this.modules[this.createCommandName(commands[0])]) { throw new Error("Default commands should be required before child commands"); } const parentCommandName = commands[0]; if (!this.hierarchicalCommands[parentCommandName]) { this.hierarchicalCommands[parentCommandName] = []; } this.hierarchicalCommands[parentCommandName].push(_.tail(commands).join("|" /* CommandsDelimiters.HierarchicalCommand */)); } if (commands.length > 1 && !this.modules[this.createCommandName(commands[0])]) { this.require(this.createCommandName(commands[0]), file); if (commands[1] && !commandName.match(/\|\*/)) { this.require(this.createCommandName(commandName), file); } } else { this.require(this.createCommandName(commandName), file); } }); } require(names, file) { forEachName(names, (name) => this.requireOne(name, file)); } requireKeyCommand(name, file) { this.requireOne(this.createKeyCommandName(name), file); } requirePublic(names, file) { forEachName(names, (name) => { this.requireOne(name, file); this.resolvePublicApi(name, file); }); } requirePublicClass(names, file) { forEachName(names, (name) => { this.requireOne(name, file); this.addClassToPublicApi(name, file); }); } addClassToPublicApi(name, file) { Object.defineProperty(this.publicApi, name, { get: () => { return this.resolveInstance(name); }, }); } resolvePublicApi(name, file) { Object.defineProperty(this.publicApi, name, { get: () => { this.resolveInstance(name); return this.publicApi.__modules__[name]; }, }); } resolveInstance(name) { let classInstance = _.first(this.modules[name].instances); if (!classInstance) { classInstance = this.resolve(name); } return classInstance; } requireOne(name, file) { const relativePath = path.join("../", file); const dependency = { require: require("fs").existsSync(path.join(__dirname, relativePath + ".js")) ? relativePath : file, shared: true, }; if (!this.modules[name] || this.overrideAlreadyRequiredModule) { this.modules[name] = dependency; } else { throw new Error(`module '${name}' require'd twice.`); } } registerCommand(names, resolver) { forEachName(names, (name) => { const commands = name.split("|" /* CommandsDelimiters.HierarchicalCommand */); this.register(this.createCommandName(name), resolver); if (commands.length > 1) { this.createHierarchicalCommand(commands[0]); } }); } registerKeyCommand(name, resolver) { this.register(this.createKeyCommandName(name), resolver); } getDefaultCommand(name, commandArguments) { const subCommands = this.hierarchicalCommands[name]; const defaultCommand = _.find(subCommands, (command) => _.some(command.split("|" /* CommandsDelimiters.HierarchicalCommand */), (c) => _.startsWith(c, "*" /* CommandsDelimiters.DefaultCommandSymbol */))); return defaultCommand; } buildHierarchicalCommand(parentCommandName, commandLineArguments) { let currentSubCommandName, finalSubCommandName, matchingSubCommandName; const subCommands = this.hierarchicalCommands[parentCommandName]; let remainingArguments = commandLineArguments; let finalRemainingArguments = commandLineArguments; _.each(commandLineArguments, (arg) => { arg = arg.toLowerCase(); currentSubCommandName = currentSubCommandName ? this.getHierarchicalCommandName(currentSubCommandName, arg) : arg; remainingArguments = _.tail(remainingArguments); if ((matchingSubCommandName = _.find(subCommands, (sc) => sc === currentSubCommandName || sc === `${"*" /* CommandsDelimiters.DefaultCommandSymbol */}${currentSubCommandName}`))) { finalSubCommandName = matchingSubCommandName; finalRemainingArguments = remainingArguments; } }); if (!finalSubCommandName) { finalSubCommandName = this.getDefaultCommand(parentCommandName, commandLineArguments) || ""; finalRemainingArguments = _.difference(commandLineArguments, finalSubCommandName .split("|" /* CommandsDelimiters.HierarchicalCommand */) .map((command) => _.startsWith(command, "*" /* CommandsDelimiters.DefaultCommandSymbol */) ? command.substr(1) : command)); } if (finalSubCommandName) { return { commandName: this.getHierarchicalCommandName(parentCommandName, finalSubCommandName), remainingArguments: finalRemainingArguments, }; } } createHierarchicalCommand(name) { const factory = () => { return { disableAnalytics: true, isHierarchicalCommand: true, execute: async (args) => { const commandsService = exports.injector.resolve("commandsService"); let commandName = null; const defaultCommand = this.getDefaultCommand(name, args); let commandArguments = []; if (args.length > 0) { const hierarchicalCommand = this.buildHierarchicalCommand(name, args); if (hierarchicalCommand) { commandName = hierarchicalCommand.commandName; commandArguments = hierarchicalCommand.remainingArguments; } else { commandName = defaultCommand ? this.getHierarchicalCommandName(name, defaultCommand) : "help"; // If we'll execute the default command, but it's full name had been written by the user // for example "tns run ios", we have to remove the "ios" option from the arguments that we'll pass to the command. if (_.includes(this.hierarchicalCommands[name], "*" /* CommandsDelimiters.DefaultCommandSymbol */ + args[0])) { commandArguments = _.tail(args); } else { commandArguments = args; } } } else { //Execute only default command without arguments if (defaultCommand) { commandName = this.getHierarchicalCommandName(name, defaultCommand); } else { commandName = "help"; // Show command-line help const options = this.resolve("options"); options.help = true; } } await commandsService.tryExecuteCommand(commandName, commandName === "help" ? [name] : commandArguments); }, }; }; exports.injector.registerCommand(name, factory); } getHierarchicalCommandName(parentCommandName, subCommandName) { return [parentCommandName, subCommandName].join("|" /* CommandsDelimiters.HierarchicalCommand */); } async isValidHierarchicalCommand(commandName, commandArguments) { if (_.includes(Object.keys(this.hierarchicalCommands), commandName)) { const subCommands = this.hierarchicalCommands[commandName]; if (subCommands) { const fullCommandName = this.buildHierarchicalCommand(commandName, commandArguments); if (!fullCommandName) { // In case buildHierarchicalCommand doesn't find a valid command // there isn't a valid command or default with those arguments const errors = exports.injector.resolve("errors"); errors.failWithHelp(constants_1.ERROR_NO_VALID_SUBCOMMAND_FORMAT, commandName); } return true; } } return false; } isDefaultCommand(commandName) { return (commandName.indexOf("*" /* CommandsDelimiters.DefaultCommandSymbol */) > 0 && commandName.indexOf("|" /* CommandsDelimiters.HierarchicalCommand */) > 0); } register(name, resolver, shared) { shared = shared === undefined ? true : shared; trace("registered '%s'", name); const dependency = this.modules[name] || {}; dependency.shared = shared; if (_.isFunction(resolver)) { dependency.resolver = resolver; } else { dependency.instances = dependency.instances || []; if (shared) { dependency.instances[0] = resolver; } else { dependency.instances.push(resolver); } } this.modules[name] = dependency; } resolveCommand(name) { let command; const commandModuleName = this.createCommandName(name); if (!this.modules[commandModuleName]) { return null; } command = this.resolve(commandModuleName); return command; } resolveKeyCommand(name) { let command; const commandModuleName = this.createKeyCommandName(name); if (!this.modules[commandModuleName]) { return null; } command = this.resolve(commandModuleName); return command; } resolve(param, ctorArguments) { if (_.isFunction(param)) { return this.resolveConstructor(param, ctorArguments); } else { return this.resolveByName(param, ctorArguments); } } /* Regex to match dynamic calls in the following format: #{moduleName.functionName} or #{moduleName.functionName(param1)} or #{moduleName.functionName(param1, param2)} - multiple parameters separated with comma are supported Check dynamicCall method for sample usage of this regular expression and see how to determine the passed parameters */ get dynamicCallRegex() { return /#{([^.]+)\.([^}]+?)(\((.+)\))*}/; } getDynamicCallData(call, args) { const parsed = call.match(this.dynamicCallRegex); const module = this.resolve(parsed[1]); if (!args && parsed[3]) { args = _.map(parsed[4].split(","), (arg) => arg.trim()); } return module[parsed[2]].apply(module, args); } async dynamicCall(call, args) { const data = this.getDynamicCallData(call, args); if ((0, helpers_1.isPromise)(data)) { return await data; } return data; } resolveConstructor(ctor, ctorArguments) { (0, helpers_1.annotate)(ctor); const resolvedArgs = ctor.$inject.args.map((paramName) => { if (ctorArguments && ctorArguments.hasOwnProperty(paramName)) { return ctorArguments[paramName]; } else { return this.resolve(paramName); } }); const name = ctor.$inject.name; if (name && name[0] === name[0].toUpperCase()) { return new ctor(...resolvedArgs); } else { return ctor.apply(null, resolvedArgs); } } resolveByName(name, ctorArguments) { if (name[0] === "$") { name = name.substr(1); } if (this.resolutionProgress[name]) { throw new Error(`Cyclic dependency detected on dependency '${name}'`); } this.resolutionProgress[name] = true; trace("resolving '%s'", name); pushIndent(); let dependency; let instance; try { dependency = this.resolveDependency(name); if (!dependency) { throw new Error("unable to resolve " + name); } if (!dependency.instances || !dependency.instances.length || !dependency.shared) { if (!dependency.resolver) { throw new Error("no resolver registered for " + name); } dependency.instances = dependency.instances || []; instance = this.resolveConstructor(dependency.resolver, ctorArguments); dependency.instances.push(instance); } else { instance = _.first(dependency.instances); } } finally { popIndent(); delete this.resolutionProgress[name]; } return instance; } resolveDependency(name) { const module = this.modules[name]; if (!module) { throw new Error("unable to resolve " + name); } if (module.require) { require(module.require); } return module; } getRegisteredCommandsNames(includeDev) { const modulesNames = _.keys(this.modules); const commandsNames = _.filter(modulesNames, (moduleName) => _.startsWith(moduleName, `${this.COMMANDS_NAMESPACE}.`)); let commands = _.map(commandsNames, (commandName) => commandName.substr(this.COMMANDS_NAMESPACE.length + 1)); if (!includeDev) { commands = _.reject(commands, (command) => _.startsWith(command, "dev-")); } return commands; } getRegisteredKeyCommandsNames() { const modulesNames = _.keys(this.modules); const commandsNames = _.filter(modulesNames, (moduleName) => _.startsWith(moduleName, `${this.KEY_COMMANDS_NAMESPACE}.`)); let commands = _.map(commandsNames, (commandName) => commandName.substr(this.KEY_COMMANDS_NAMESPACE.length + 1)); return commands; } getChildrenCommandsNames(commandName) { return this.hierarchicalCommands[commandName]; } createCommandName(name) { return `${this.COMMANDS_NAMESPACE}.${name}`; } createKeyCommandName(name) { return `${this.KEY_COMMANDS_NAMESPACE}.${name}`; } dispose() { Object.keys(this.modules).forEach((moduleName) => { const instances = this.modules[moduleName].instances; _.forEach(instances, (instance) => { if (instance && instance.dispose && instance !== this) { instance.dispose(); } }); }); } } exports.Yok = Yok; if (!global.$injector) { global.$injector = new Yok(); exports.injector = global.$injector; } function setGlobalInjector(inj) { global.$injector = exports.injector = inj; return inj; } //# sourceMappingURL=yok.js.map