"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
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 },
            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 },
        };
    }
    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", (current_, argv) => __awaiter(this, void 0, void 0, function* () {
            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;
                },
            });
        });
    }
}
Options.DASHED_OPTION_REGEX = /(.+?)([A-Z])(.*)/;
Options.NONDASHED_OPTION_REGEX = /(.+?)[-]([a-zA-Z])(.*)/;
exports.Options = Options;
yok_1.injector.register("options", Options);
//# sourceMappingURL=options.js.map