nativescript
Version:
Command-line interface for building NativeScript projects
410 lines • 22.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Options = void 0;
const helpers = require("./common/helpers");
const yargs = require("yargs");
const helpers_1 = require("yargs/helpers");
const _ = require("lodash");
const yok_1 = require("./common/yok");
const constants_1 = require("./constants");
class Options {
setupOptions(commandSpecificDashedOptions) {
if (commandSpecificDashedOptions) {
_.extend(this.options, commandSpecificDashedOptions);
this.setArgv();
}
this.argv.bundle = "webpack";
// Check if the user has explicitly provide --hmr and --release options from command line
if (this.initialArgv.release && this.initialArgv.hmr) {
this.$errors.fail("The options --release and --hmr cannot be used simultaneously.");
}
if (this.argv.hmr) {
this.argv.hmr = !this.argv.release;
}
if (this.argv.debugBrk) {
// we cannot use HMR along with debug-brk because we have to restart the app
// on each livesync in order to stop and allow debugging on app start
this.argv.hmr = false;
}
if (this.argv.justlaunch) {
this.argv.hmr = false;
}
}
constructor($errors, $settingsService) {
this.$errors = $errors;
this.$settingsService = $settingsService;
this.optionsWhiteList = [
"ui",
"recursive",
"reporter",
"require",
"timeout",
"_",
"$0",
]; // These options shouldn't be validated
this.globalOptions = {
log: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
verbose: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
version: { type: "boolean" /* OptionType.Boolean */, alias: "v", hasSensitiveValue: false },
help: { type: "boolean" /* OptionType.Boolean */, alias: "h", hasSensitiveValue: false },
profileDir: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
analyticsClient: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
path: { type: "string" /* OptionType.String */, alias: "p", hasSensitiveValue: true },
config: { type: "string" /* OptionType.String */, alias: "c", hasSensitiveValue: true },
// This will parse all non-hyphenated values as strings.
_: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
};
this.options = _.extend({}, this.commonOptions, this.globalOptions);
this.setArgv();
}
get shorthands() {
const result = [];
_.each(_.keys(this.options), (optionName) => {
if (this.options[optionName].alias) {
result.push(this.options[optionName].alias);
}
});
return result;
}
get commonOptions() {
return {
ipa: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
frameworkPath: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
frameworkName: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
framework: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
frameworkVersion: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
forDevice: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
iCloudContainerEnvironment: {
type: "string" /* OptionType.String */,
hasSensitiveValue: false,
},
provision: { type: "object" /* OptionType.Object */, hasSensitiveValue: true },
client: {
type: "boolean" /* OptionType.Boolean */,
default: true,
hasSensitiveValue: false,
},
env: { type: "object" /* OptionType.Object */, hasSensitiveValue: false },
production: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
debugTransport: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
keyStorePath: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
keyStorePassword: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
keyStoreAlias: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
keyStoreAliasPassword: {
type: "string" /* OptionType.String */,
hasSensitiveValue: true,
},
ignoreScripts: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
disableNpmInstall: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
compileSdk: { type: "number" /* OptionType.Number */, hasSensitiveValue: false },
port: { type: "number" /* OptionType.Number */, hasSensitiveValue: false },
copyTo: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
js: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
javascript: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
ng: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
angular: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
react: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
reactjs: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
vue: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
vuejs: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
svelte: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
vision: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
"vision-ng": { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
"vision-react": { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
"vision-solid": { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
"vision-svelte": { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
"vision-vue": { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
tsc: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
ts: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
typescript: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
yarn: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
yarn2: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
pnpm: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
androidTypings: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
bundle: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
all: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
teamId: { type: "object" /* OptionType.Object */, hasSensitiveValue: true },
chrome: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
inspector: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
clean: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
watch: {
type: "boolean" /* OptionType.Boolean */,
default: true,
hasSensitiveValue: false,
},
background: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
username: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
pluginName: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
includeTypeScriptDemo: {
type: "string" /* OptionType.String */,
hasSensitiveValue: false,
},
includeAngularDemo: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
hmr: {
type: "boolean" /* OptionType.Boolean */,
hasSensitiveValue: false,
default: true,
},
collection: {
type: "string" /* OptionType.String */,
alias: "c",
hasSensitiveValue: false,
},
json: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
avd: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
// check not used
config: { type: "array" /* OptionType.Array */, hasSensitiveValue: false },
insecure: {
type: "boolean" /* OptionType.Boolean */,
alias: "k",
hasSensitiveValue: false,
},
debug: { type: "boolean" /* OptionType.Boolean */, alias: "d", hasSensitiveValue: false },
timeout: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
device: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
availableDevices: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
appid: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
geny: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
debugBrk: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
debugPort: { type: "number" /* OptionType.Number */, hasSensitiveValue: false },
start: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
stop: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
ddi: { type: "string" /* OptionType.String */, hasSensitiveValue: true }, // the path to developer disk image
justlaunch: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
file: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
force: { type: "boolean" /* OptionType.Boolean */, alias: "f", hasSensitiveValue: false },
emulator: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
simulator: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
sdk: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
template: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
certificate: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
certificatePassword: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
release: {
type: "boolean" /* OptionType.Boolean */,
alias: "r",
hasSensitiveValue: false,
},
markingMode: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
var: { type: "object" /* OptionType.Object */, hasSensitiveValue: true },
default: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
count: { type: "number" /* OptionType.Number */, hasSensitiveValue: false },
analyticsLogFile: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
disableAnalytics: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
cleanupLogFile: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
hooks: {
type: "boolean" /* OptionType.Boolean */,
default: true,
hasSensitiveValue: false,
},
link: {
type: "boolean" /* OptionType.Boolean */,
default: false,
hasSensitiveValue: false,
},
gradlePath: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
gradleArgs: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
hostProjectPath: { type: "string" /* OptionType.String */, hasSensitiveValue: false },
hostProjectModuleName: {
type: "string" /* OptionType.String */,
hasSensitiveValue: false,
default: constants_1.APP_FOLDER_NAME,
},
aab: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
performance: { type: "object" /* OptionType.Object */, hasSensitiveValue: true },
appleApplicationSpecificPassword: {
type: "string" /* OptionType.String */,
hasSensitiveValue: true,
},
appleSessionBase64: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
jar: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
aar: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
filter: { type: "string" /* OptionType.String */, hasSensitiveValue: true },
git: {
type: "boolean" /* OptionType.Boolean */,
hasSensitiveValue: false,
default: true,
},
dryRun: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
uniqueBundle: { type: "boolean" /* OptionType.Boolean */, hasSensitiveValue: false },
};
}
get optionNames() {
return _.keys(this.options);
}
getOptionValue(optionName) {
optionName = this.getCorrectOptionName(optionName);
return this.argv[optionName];
}
validateOptions(commandSpecificDashedOptions) {
this.setupOptions(commandSpecificDashedOptions);
const parsed = {};
for (const key of Object.keys(this.argv)) {
const optionName = `${this.argv[key]}`;
parsed[optionName] = this.getOptionValue(optionName);
}
_.each(parsed, (value, originalOptionName) => {
// when this.options are passed to yargs, it returns all of them and the ones that are not part of process.argv are set to undefined.
if (value === undefined) {
return;
}
const optionName = this.getCorrectOptionName(originalOptionName);
if (!_.includes(this.optionsWhiteList, optionName)) {
if (!this.isOptionSupported(optionName)) {
this.$errors.failWithHelp(`The option '${originalOptionName}' is not supported.`);
}
const optionType = this.getOptionType(optionName), optionValue = parsed[optionName];
if (_.isArray(optionValue) && optionType !== "array" /* OptionType.Array */) {
this.$errors.failWithHelp("The '%s' option requires a single value.", originalOptionName);
}
else if (optionType === "string" /* OptionType.String */ &&
helpers.isNullOrWhitespace(optionValue)) {
this.$errors.failWithHelp("The option '%s' requires non-empty value.", originalOptionName);
}
else if (optionType === "array" /* OptionType.Array */ &&
optionValue.length === 0) {
this.$errors.failWithHelp(`The option '${originalOptionName}' requires one or more values, separated by a space.`);
}
}
});
}
getCorrectOptionName(optionName) {
const secondaryOptionName = this.getNonDashedOptionName(optionName);
return _.includes(this.optionNames, secondaryOptionName)
? secondaryOptionName
: optionName;
}
getOptionType(optionName) {
const option = this.options[optionName] || this.tryGetOptionByAliasName(optionName);
return option ? option.type : "";
}
tryGetOptionByAliasName(aliasName) {
const option = _.find(this.options, (opt) => opt.alias === aliasName);
return option;
}
isOptionSupported(option) {
if (!this.options[option]) {
const opt = this.tryGetOptionByAliasName(option);
return !!opt;
}
return true;
}
// If you pass value with dash, yargs adds it to yargs.argv in two ways:
// with dash and without dash, replacing first symbol after it with its toUpper equivalent
// ex, "$ <cli name> emulate android --profile-dir" will add profile-dir to yargs.argv as profile-dir and profileDir
// IMPORTANT: In your code, it is better to use the value without dashes (profileDir in the example).
// This way your code will work in case "$ <cli name> emulate android --profile-dir" or "$ <cli name> emulate android --profileDir" is used by user.
getNonDashedOptionName(optionName) {
const matchUpperCaseLetters = optionName.match(Options.NONDASHED_OPTION_REGEX);
if (matchUpperCaseLetters) {
// get here if option with upperCase letter is specified, for example profileDir
// check if in knownOptions we have its kebabCase presentation
const secondaryOptionName = matchUpperCaseLetters[1] +
matchUpperCaseLetters[2].toUpperCase() +
matchUpperCaseLetters[3] || "";
return this.getNonDashedOptionName(secondaryOptionName);
}
return optionName;
}
getDashedOptionName(optionName) {
const matchUpperCaseLetters = optionName.match(Options.DASHED_OPTION_REGEX);
if (matchUpperCaseLetters) {
const secondaryOptionName = `${matchUpperCaseLetters[1]}-${matchUpperCaseLetters[2].toLowerCase()}${matchUpperCaseLetters[3] || ""}`;
return this.getDashedOptionName(secondaryOptionName);
}
return optionName;
}
setArgv() {
const opts = {};
_.each(this.options, (value, key) => {
opts[this.getDashedOptionName(key)] = value;
});
const parsed = yargs((0, helpers_1.hideBin)(process.argv))
.version(false)
.help(false)
.completion("completion_generate_script", async (current_, argv) => {
var _a;
const args = argv._.slice(1);
const commands = yok_1.injector
.getRegisteredCommandsNames(false)
.filter((c) => c != "/?"); // remove the /? command, looks weird... :D
const currentDepth = args.length > 0 ? args.length - 1 : 0;
const current = (_a = current_ !== null && current_ !== void 0 ? current_ : args[currentDepth]) !== null && _a !== void 0 ? _a : "";
// split all commands into their components ie. "device|list" => ["device", "list"]
const matchGroups = commands.map((c) => c.split("|"));
// find all commands that match the current depth and all the previous args
const possibleMatches = matchGroups.filter((group) => {
return group.slice(0, currentDepth).every((g, i) => {
return g === args[i] || args[i].at(0) === "-";
});
});
// filter out duplicates
const completions = [
...new Set(possibleMatches
.map((match) => {
return match[currentDepth];
})
.filter(Boolean)),
];
// autocomplete long -- options
if (current.startsWith("--")) {
return this.optionNames.filter((o) => o !== "_").map((o) => `--${o}`);
}
// autocomple short - options
if (current.startsWith("-")) {
return this.shorthands.map((o) => `-${o}`);
}
// autocomplete matched completions
return completions;
});
this.initialArgv = parsed.argv;
this.argv = parsed.options(opts).argv;
// For backwards compatibility
// Previously profileDir had a default option and calling `this.$options.profileDir` always returned valid result.
// Now the profileDir should be used from $settingsService, but ensure the `this.$options.profileDir` returns the same value.
this.$settingsService.setSettings({
profileDir: this.argv.profileDir,
});
this.argv.profileDir = this.argv["profile-dir"] =
this.$settingsService.getProfileDir();
// if justlaunch is set, it takes precedence over the --watch flag and the default true value
if (this.argv.justlaunch) {
this.argv.watch = false;
}
if (this.argv.ts || this.argv.typescript) {
this.argv.tsc = true;
}
if (this.argv.angular) {
this.argv.ng = true;
}
if (this.argv.vuejs) {
this.argv.vue = true;
}
if (this.argv.javascript) {
this.argv.js = true;
}
// alias --simulator to --emulator
if (this.argv.simulator) {
this.argv.emulator = this.argv.simulator;
}
this.argv.bundle = "webpack";
this.adjustDashedOptions();
}
adjustDashedOptions() {
_.each(this.optionNames, (optionName) => {
Object.defineProperty(Options.prototype, optionName, {
configurable: true,
get: () => {
return this.getOptionValue(optionName);
},
set: (value) => {
this.argv[optionName] = value;
},
});
});
}
}
exports.Options = Options;
Options.DASHED_OPTION_REGEX = /(.+?)([A-Z])(.*)/;
Options.NONDASHED_OPTION_REGEX = /(.+?)[-]([a-zA-Z])(.*)/;
yok_1.injector.register("options", Options);
//# sourceMappingURL=options.js.map