nativescript
Version:
Command-line interface for building NativeScript projects
434 lines • 17.4 kB
JavaScript
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
;