#!/usr/bin/env node
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseArgs = exports.initArgs = void 0;
require("source-map-support/register");
const log = __importStar(require("loglevel"));
const yargs_1 = __importDefault(require("yargs"));
const constants_1 = require("./constants");
const helpers_1 = require("./helpers/helpers");
const inferOs_1 = require("./infer/inferOs");
const main_1 = require("./main");
const parseUtils_1 = require("./utils/parseUtils");
function initArgs(argv) {
    const sanitizedArgs = sanitizeArgs(argv);
    const args = (0, yargs_1.default)(sanitizedArgs)
        .scriptName('nativefier')
        .usage('$0 <targetUrl> [outputDirectory] [other options]\nor\n$0 --upgrade <pathToExistingApp> [other options]')
        .example('$0 <targetUrl> -n <name>', 'Make an app from <targetUrl> and set the application name to <name>')
        .example('$0 --upgrade <pathToExistingApp>', 'Upgrade (in place) the existing Nativefier app at <pathToExistingApp>')
        .example('$0 <targetUrl> -p <platform> -a <arch>', 'Make an app from <targetUrl> for the OS <platform> and CPU architecture <arch>')
        .example('for more examples and help...', 'See https://github.com/nativefier/nativefier/blob/master/CATALOG.md')
        .positional('targetUrl', {
        description: 'the URL that you wish to to turn into a native app; required if not using --upgrade',
        type: 'string',
    })
        .positional('outputDirectory', {
        defaultDescription: 'defaults to the current directory, or env. var. NATIVEFIER_APPS_DIR if set',
        description: 'the directory to generate the app in',
        normalize: true,
        type: 'string',
    })
        // App Creation Options
        .option('a', {
        alias: 'arch',
        choices: inferOs_1.supportedArchs,
        defaultDescription: "current Node's arch",
        description: 'the CPU architecture to build for',
        type: 'string',
    })
        .option('c', {
        alias: 'conceal',
        default: false,
        description: 'package the app source code into an asar archive',
        type: 'boolean',
    })
        .option('e', {
        alias: 'electron-version',
        defaultDescription: constants_1.DEFAULT_ELECTRON_VERSION,
        description: "specify the electron version to use (without the 'v'); see https://github.com/electron/electron/releases",
    })
        .option('global-shortcuts', {
        description: 'define global keyboard shortcuts via a JSON file; See https://github.com/nativefier/nativefier/blob/master/API.md#global-shortcuts',
        normalize: true,
        type: 'string',
    })
        .option('i', {
        alias: 'icon',
        description: 'the icon file to use as the icon for the app (.ico on Windows, .icns/.png on macOS, .png on Linux)',
        normalize: true,
        type: 'string',
    })
        .option('n', {
        alias: 'name',
        defaultDescription: 'the title of the page passed via targetUrl',
        description: 'specify the name of the app',
        type: 'string',
    })
        .option('no-overwrite', {
        default: false,
        description: 'do not overwrite output directory if it already exists',
        type: 'boolean',
    })
        .option('overwrite', {
        // This is needed to have the `no-overwrite` flag to work correctly
        default: true,
        hidden: true,
        type: 'boolean',
    })
        .option('p', {
        alias: 'platform',
        choices: inferOs_1.supportedPlatforms,
        defaultDescription: 'current operating system',
        description: 'the operating system platform to build for',
        type: 'string',
    })
        .option('portable', {
        default: false,
        description: 'make the app store its user data in the app folder; WARNING: see https://github.com/nativefier/nativefier/blob/master/API.md#portable for security risks',
        type: 'boolean',
    })
        .option('upgrade', {
        description: 'upgrade an app built by an older version of Nativefier\nYou must pass the full path to the existing app executable (app will be overwritten with upgraded version by default)',
        normalize: true,
        type: 'string',
    })
        .option('widevine', {
        default: false,
        description: "use a Widevine-enabled version of Electron for DRM playback (use at your own risk, it's unofficial, provided by CastLabs)",
        type: 'boolean',
    })
        .group([
        'arch',
        'conceal',
        'electron-version',
        'global-shortcuts',
        'icon',
        'name',
        'no-overwrite',
        'platform',
        'portable',
        'upgrade',
        'widevine',
    ], decorateYargOptionGroup('App Creation Options'))
        // App Window Options
        .option('always-on-top', {
        default: false,
        description: 'enable always on top window',
        type: 'boolean',
    })
        .option('background-color', {
        description: "set the app background color, for better integration while the app is loading. Example value: '#2e2c29'",
        type: 'string',
    })
        .option('bookmarks-menu', {
        description: 'create a bookmarks menu (via JSON file); See https://github.com/nativefier/nativefier/blob/master/API.md#bookmarks-menu',
        normalize: true,
        type: 'string',
    })
        .option('browserwindow-options', {
        coerce: parseUtils_1.parseJson,
        description: 'override Electron BrowserWindow options (via JSON string); see https://github.com/nativefier/nativefier/blob/master/API.md#browserwindow-options',
    })
        .option('disable-context-menu', {
        default: false,
        description: 'disable the context menu (right click)',
        type: 'boolean',
    })
        .option('disable-dev-tools', {
        default: false,
        description: 'disable developer tools (Ctrl+Shift+I / F12)',
        type: 'boolean',
    })
        .option('full-screen', {
        default: false,
        description: 'always start the app full screen',
        type: 'boolean',
    })
        .option('height', {
        defaultDescription: '800',
        description: 'set window default height in pixels',
        type: 'number',
    })
        .option('hide-window-frame', {
        default: false,
        description: 'disable window frame and controls',
        type: 'boolean',
    })
        .option('m', {
        alias: 'show-menu-bar',
        default: false,
        description: 'set menu bar visible',
        type: 'boolean',
    })
        .option('max-height', {
        defaultDescription: 'unlimited',
        description: 'set window maximum height in pixels',
        type: 'number',
    })
        .option('max-width', {
        defaultDescription: 'unlimited',
        description: 'set window maximum width in pixels',
        type: 'number',
    })
        .option('maximize', {
        default: false,
        description: 'always start the app maximized',
        type: 'boolean',
    })
        .option('min-height', {
        defaultDescription: '0',
        description: 'set window minimum height in pixels',
        type: 'number',
    })
        .option('min-width', {
        defaultDescription: '0',
        description: 'set window minimum width in pixels',
        type: 'number',
    })
        .option('process-envs', {
        coerce: helpers_1.getProcessEnvs,
        description: 'a JSON string of key/value pairs to be set as environment variables before any browser windows are opened',
    })
        .option('single-instance', {
        default: false,
        description: 'allow only a single instance of the app',
        type: 'boolean',
    })
        .option('tray', {
        default: 'false',
        description: "allow app to stay in system tray. If 'start-in-tray' is set as argument, don't show main window on first start",
        choices: ['true', 'false', 'start-in-tray'],
    })
        .option('width', {
        defaultDescription: '1280',
        description: 'app window default width in pixels',
        type: 'number',
    })
        .option('x', {
        description: 'set window x location in pixels from left',
        type: 'number',
    })
        .option('y', {
        description: 'set window y location in pixels from top',
        type: 'number',
    })
        .option('zoom', {
        default: 1.0,
        description: 'set the default zoom factor for the app',
        type: 'number',
    })
        .group([
        'always-on-top',
        'background-color',
        'bookmarks-menu',
        'browserwindow-options',
        'disable-context-menu',
        'disable-dev-tools',
        'full-screen',
        'height',
        'hide-window-frame',
        'm',
        'max-width',
        'max-height',
        'maximize',
        'min-height',
        'min-width',
        'process-envs',
        'single-instance',
        'tray',
        'width',
        'x',
        'y',
        'zoom',
    ], decorateYargOptionGroup('App Window Options'))
        // Internal Browser Options
        .option('file-download-options', {
        coerce: parseUtils_1.parseJson,
        description: 'a JSON string defining file download options; see https://github.com/sindresorhus/electron-dl',
    })
        .option('inject', {
        description: 'path to a CSS/JS file to be injected; pass multiple times to inject multiple files',
        string: true,
        type: 'array',
    })
        .option('lang', {
        defaultDescription: 'os language at runtime of the app',
        description: 'set the language or locale to render the web site as (e.g., "fr", "en-US", "es", etc.)',
        type: 'string',
    })
        .option('u', {
        alias: 'user-agent',
        description: "set the app's user agent string; may also use 'edge', 'firefox', or 'safari' to have one auto-generated",
        type: 'string',
    })
        .option('user-agent-honest', {
        alias: 'honest',
        default: false,
        description: 'prevent the normal changing of the user agent string to appear as a regular Chrome browser',
        type: 'boolean',
    })
        .group([
        'file-download-options',
        'inject',
        'lang',
        'user-agent',
        'user-agent-honest',
    ], decorateYargOptionGroup('Internal Browser Options'))
        // Internal Browser Cache Options
        .option('clear-cache', {
        default: false,
        description: 'prevent the app from preserving cache between launches',
        type: 'boolean',
    })
        .option('disk-cache-size', {
        defaultDescription: 'chromium default',
        description: 'set the maximum disk space (in bytes) to be used by the disk cache',
        type: 'number',
    })
        .group(['clear-cache', 'disk-cache-size'], decorateYargOptionGroup('Internal Browser Cache Options'))
        // URL Handling Options
        .option('block-external-urls', {
        default: false,
        description: `forbid navigation to URLs not considered "internal" (see '--internal-urls').  Instead of opening in an external browser, attempts to navigate to external URLs will be blocked`,
        type: 'boolean',
    })
        .option('internal-urls', {
        defaultDescription: 'URLs sharing the same base domain',
        description: `regex of URLs to consider "internal"; by default matches based on domain (see '--strict-internal-urls'); all other URLs will be opened in an external browser`,
        type: 'string',
    })
        .option('strict-internal-urls', {
        default: false,
        description: 'disable domain-based matching on internal URLs',
        type: 'boolean',
    })
        .option('proxy-rules', {
        description: 'proxy rules; see https://www.electronjs.org/docs/api/session#sessetproxyconfig',
        type: 'string',
    })
        .group([
        'block-external-urls',
        'internal-urls',
        'strict-internal-urls',
        'proxy-rules',
    ], decorateYargOptionGroup('URL Handling Options'))
        // Auth Options
        .option('basic-auth-password', {
        description: 'basic http(s) auth password',
        type: 'string',
    })
        .option('basic-auth-username', {
        description: 'basic http(s) auth username',
        type: 'string',
    })
        .group(['basic-auth-password', 'basic-auth-username'], decorateYargOptionGroup('Auth Options'))
        // Graphics Options
        .option('disable-gpu', {
        default: false,
        description: 'disable hardware acceleration',
        type: 'boolean',
    })
        .option('enable-es3-apis', {
        default: false,
        description: 'force activation of WebGL 2.0',
        type: 'boolean',
    })
        .option('ignore-gpu-blacklist', {
        default: false,
        description: 'force WebGL apps to work on unsupported GPUs',
        type: 'boolean',
    })
        .group(['disable-gpu', 'enable-es3-apis', 'ignore-gpu-blacklist'], decorateYargOptionGroup('Graphics Options'))
        // (In)Security Options
        .option('disable-old-build-warning-yesiknowitisinsecure', {
        default: false,
        description: 'disable warning shown when opening an app made too long ago; Nativefier uses the Chrome browser (through Electron), and it is dangerous to keep using an old version of it',
        type: 'boolean',
    })
        .option('ignore-certificate', {
        default: false,
        description: 'ignore certificate-related errors',
        type: 'boolean',
    })
        .option('insecure', {
        default: false,
        description: 'enable loading of insecure content',
        type: 'boolean',
    })
        .group([
        'disable-old-build-warning-yesiknowitisinsecure',
        'ignore-certificate',
        'insecure',
    ], decorateYargOptionGroup('(In)Security Options'))
        // Flash Options (DEPRECATED)
        .option('flash', {
        default: false,
        deprecated: true,
        description: 'enable Adobe Flash',
        hidden: true,
        type: 'boolean',
    })
        .option('flash-path', {
        deprecated: true,
        description: 'path to Chrome flash plugin; find it in `chrome://plugins`',
        hidden: true,
        normalize: true,
        type: 'string',
    })
        // Platform Specific Options
        .option('app-copyright', {
        description: '(macOS, windows only) set a human-readable copyright line for the app; maps to `LegalCopyright` metadata property on Windows, and `NSHumanReadableCopyright` on macOS',
        type: 'string',
    })
        .option('app-version', {
        description: '(macOS, windows only) set the version of the app; maps to the `ProductVersion` metadata property on Windows, and `CFBundleShortVersionString` on macOS',
        type: 'string',
    })
        .option('bounce', {
        default: false,
        description: '(macOS only) make the dock icon bounce when the counter increases',
        type: 'boolean',
    })
        .option('build-version', {
        description: '(macOS, windows only) set the build version of the app; maps to `FileVersion` metadata property on Windows, and `CFBundleVersion` on macOS',
        type: 'string',
    })
        .option('counter', {
        default: false,
        description: '(macOS only) set a dock count badge, determined by looking for a number in the window title',
        type: 'boolean',
    })
        .option('darwin-dark-mode-support', {
        default: false,
        description: '(macOS only) enable Dark Mode support on macOS 10.14+',
        type: 'boolean',
    })
        .option('f', {
        alias: 'fast-quit',
        default: false,
        description: '(macOS only) quit app on window close',
        type: 'boolean',
    })
        .option('title-bar-style', {
        choices: ['hidden', 'hiddenInset'],
        description: '(macOS only) set title bar style; consider injecting custom CSS (via --inject) for better integration',
        type: 'string',
    })
        .option('win32metadata', {
        coerce: (value) => (0, parseUtils_1.parseJson)(value),
        description: '(windows only) a JSON string of key/value pairs (ProductName, InternalName, FileDescription) to embed as executable metadata',
    })
        .group([
        'app-copyright',
        'app-version',
        'bounce',
        'build-version',
        'counter',
        'darwin-dark-mode-support',
        'fast-quit',
        'title-bar-style',
        'win32metadata',
    ], decorateYargOptionGroup('Platform-Specific Options'))
        // Debug Options
        .option('crash-reporter', {
        description: 'remote server URL to send crash reports',
        type: 'string',
    })
        .option('verbose', {
        default: false,
        description: 'enable verbose/debug/troubleshooting logs',
        type: 'boolean',
    })
        .option('quiet', {
        default: false,
        description: 'suppress all logging',
        type: 'boolean',
    })
        .group(['crash-reporter', 'verbose', 'quiet'], decorateYargOptionGroup('Debug Options'))
        .version()
        .help()
        .group(['version', 'help'], 'Other Options')
        .wrap(yargs_1.default.terminalWidth());
    // We must access argv in order to get yargs to actually process args
    // Do this now to go ahead and get any errors out of the way
    args.argv;
    return args;
}
exports.initArgs = initArgs;
function decorateYargOptionGroup(value) {
    return `====== ${value} ======`;
}
function parseArgs(args) {
    const parsed = { ...args.argv };
    // In yargs, the _ property of the parsed args is an array of the positional args
    // https://github.com/yargs/yargs/blob/master/docs/examples.md#and-non-hyphenated-options-too-just-use-argv_
    // So try to extract the targetUrl and outputDirectory from these
    parsed.targetUrl = parsed._.length > 0 ? parsed._[0].toString() : undefined;
    parsed.out = parsed._.length > 1 ? parsed._[1] : undefined;
    if (parsed.upgrade && parsed.targetUrl) {
        let targetAndUpgrade = false;
        if (!parsed.out) {
            // If we're upgrading, the first positional args might be the outputDirectory, so swap these if we can
            try {
                // If this succeeds, we have a problem
                new URL(parsed.targetUrl);
                targetAndUpgrade = true;
            }
            catch {
                // Cool, it's not a URL
                parsed.out = parsed.targetUrl;
                parsed.targetUrl = undefined;
            }
        }
        else {
            // Someone supplied a targetUrl, an outputDirectory, and --upgrade. That's not cool.
            targetAndUpgrade = true;
        }
        if (targetAndUpgrade) {
            throw new Error('ERROR: Nativefier must be called with either a targetUrl or the --upgrade option, not both.\n');
        }
    }
    if (!parsed.targetUrl && !parsed.upgrade) {
        throw new Error('ERROR: Nativefier must be called with either a targetUrl or the --upgrade option.\n');
    }
    parsed.noOverwrite = parsed['no-overwrite'] = !parsed.overwrite;
    // Since coerce in yargs seems to have broken since
    // https://github.com/yargs/yargs/pull/1978
    for (const arg of [
        'win32metadata',
        'browserwindow-options',
        'file-download-options',
    ]) {
        if (parsed[arg] && typeof parsed[arg] === 'string') {
            parsed[arg] = (0, parseUtils_1.parseJson)(parsed[arg]);
            // sets fileDownloadOptions and browserWindowOptions
            // as parsed object as they were still strings in `nativefier.json`
            // because only their snake-cased variants were being parsed above
            parsed[(0, helpers_1.camelCased)(arg)] = parsed[arg];
        }
    }
    if (parsed['process-envs'] && typeof parsed['process-envs'] === 'string') {
        parsed['process-envs'] = (0, helpers_1.getProcessEnvs)(parsed['process-envs']);
    }
    return parsed;
}
exports.parseArgs = parseArgs;
function sanitizeArgs(argv) {
    const sanitizedArgs = [];
    argv.forEach((arg) => {
        if ((0, helpers_1.isArgFormatInvalid)(arg)) {
            throw new Error(`Invalid argument passed: ${arg} .\nNativefier supports short options (like "-n") and long options (like "--name"), all lowercase. Run "nativefier --help" for help.\nAborting`);
        }
        const isLastArg = sanitizedArgs.length + 1 === argv.length;
        if (sanitizedArgs.length > 0) {
            const previousArg = sanitizedArgs[sanitizedArgs.length - 1];
            log.debug({ arg, previousArg, isLastArg });
            // Work around commander.js not supporting default argument for options
            if (previousArg === '--tray' &&
                !['true', 'false', 'start-in-tray'].includes(arg)) {
                sanitizedArgs.push('true');
            }
        }
        sanitizedArgs.push(arg);
        if (arg === '--tray' && isLastArg) {
            // Add a true if --tray is last so it gets enabled
            sanitizedArgs.push('true');
        }
    });
    return sanitizedArgs;
}
if (require.main === module) {
    let args = undefined;
    let parsedArgs;
    try {
        args = initArgs(process.argv.slice(2));
        parsedArgs = parseArgs(args);
    }
    catch (err) {
        if (args) {
            log.error(err);
            args.showHelp();
        }
        else {
            log.error('Failed to parse command-line arguments. Aborting.', err);
        }
        process.exit(1);
    }
    const options = {
        ...parsedArgs,
    };
    if (options.verbose) {
        log.setLevel('trace');
        try {
            // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
            require('debug').enable('electron-packager');
        }
        catch (err) {
            log.debug('Failed to enable electron-packager debug output. This should not happen,', 'and suggests their internals changed. Please report an issue.');
        }
        log.debug('Running in verbose mode! This will produce a mountain of logs and', 'is recommended only for troubleshooting or if you like Shakespeare.');
    }
    else if (options.quiet) {
        log.setLevel('silent');
    }
    else {
        log.setLevel('info');
    }
    (0, helpers_1.checkInternet)();
    if (!options.out && process.env.NATIVEFIER_APPS_DIR) {
        options.out = process.env.NATIVEFIER_APPS_DIR;
    }
    (0, main_1.buildNativefierApp)(options).catch((error) => {
        log.error('Error during build. Run with --verbose for details.', error);
    });
}
//# sourceMappingURL=cli.js.map