/*!
 Stencil CLI v2.1.1 | MIT Licensed | https://stenciljs.com
 */
const toLowerCase = (str) => str.toLowerCase();
const dashToPascalCase = (str) => toLowerCase(str)
    .split('-')
    .map(segment => segment.charAt(0).toUpperCase() + segment.slice(1))
    .join('');
const isFunction = (v) => typeof v === 'function';
const isString = (v) => typeof v === 'string';

const buildError = (diagnostics) => {
    const diagnostic = {
        level: 'error',
        type: 'build',
        header: 'Build Error',
        messageText: 'build error',
        relFilePath: null,
        absFilePath: null,
        lines: [],
    };
    if (diagnostics) {
        diagnostics.push(diagnostic);
    }
    return diagnostic;
};
const catchError = (diagnostics, err, msg) => {
    const diagnostic = {
        level: 'error',
        type: 'build',
        header: 'Build Error',
        messageText: 'build error',
        relFilePath: null,
        absFilePath: null,
        lines: [],
    };
    if (isString(msg)) {
        diagnostic.messageText = msg;
    }
    else if (err != null) {
        if (err.stack != null) {
            diagnostic.messageText = err.stack.toString();
        }
        else {
            if (err.message != null) {
                diagnostic.messageText = err.message.toString();
            }
            else {
                diagnostic.messageText = err.toString();
            }
        }
    }
    if (diagnostics != null && !shouldIgnoreError(diagnostic.messageText)) {
        diagnostics.push(diagnostic);
    }
    return diagnostic;
};
const hasError = (diagnostics) => {
    if (diagnostics == null || diagnostics.length === 0) {
        return false;
    }
    return diagnostics.some(d => d.level === 'error' && d.type !== 'runtime');
};
const shouldIgnoreError = (msg) => {
    return msg === TASK_CANCELED_MSG;
};
const TASK_CANCELED_MSG = `task canceled`;

/**
 * Convert Windows backslash paths to slash paths: foo\\bar ➔ foo/bar
 * Forward-slash paths can be used in Windows as long as they're not
 * extended-length paths and don't contain any non-ascii characters.
 * This was created since the path methods in Node.js outputs \\ paths on Windows.
 */
const normalizePath = (path) => {
    if (typeof path !== 'string') {
        throw new Error(`invalid path to normalize`);
    }
    path = normalizeSlashes(path.trim());
    const components = pathComponents(path, getRootLength(path));
    const reducedComponents = reducePathComponents(components);
    const rootPart = reducedComponents[0];
    const secondPart = reducedComponents[1];
    const normalized = rootPart + reducedComponents.slice(1).join('/');
    if (normalized === '') {
        return '.';
    }
    if (rootPart === '' && secondPart && path.includes('/') && !secondPart.startsWith('.') && !secondPart.startsWith('@')) {
        return './' + normalized;
    }
    return normalized;
};
const normalizeSlashes = (path) => path.replace(backslashRegExp, '/');
const altDirectorySeparator = '\\';
const urlSchemeSeparator = '://';
const backslashRegExp = /\\/g;
const reducePathComponents = (components) => {
    if (!Array.isArray(components) || components.length === 0) {
        return [];
    }
    const reduced = [components[0]];
    for (let i = 1; i < components.length; i++) {
        const component = components[i];
        if (!component)
            continue;
        if (component === '.')
            continue;
        if (component === '..') {
            if (reduced.length > 1) {
                if (reduced[reduced.length - 1] !== '..') {
                    reduced.pop();
                    continue;
                }
            }
            else if (reduced[0])
                continue;
        }
        reduced.push(component);
    }
    return reduced;
};
const getRootLength = (path) => {
    const rootLength = getEncodedRootLength(path);
    return rootLength < 0 ? ~rootLength : rootLength;
};
const getEncodedRootLength = (path) => {
    if (!path)
        return 0;
    const ch0 = path.charCodeAt(0);
    // POSIX or UNC
    if (ch0 === 47 /* slash */ || ch0 === 92 /* backslash */) {
        if (path.charCodeAt(1) !== ch0)
            return 1; // POSIX: "/" (or non-normalized "\")
        const p1 = path.indexOf(ch0 === 47 /* slash */ ? '/' : altDirectorySeparator, 2);
        if (p1 < 0)
            return path.length; // UNC: "//server" or "\\server"
        return p1 + 1; // UNC: "//server/" or "\\server\"
    }
    // DOS
    if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* colon */) {
        const ch2 = path.charCodeAt(2);
        if (ch2 === 47 /* slash */ || ch2 === 92 /* backslash */)
            return 3; // DOS: "c:/" or "c:\"
        if (path.length === 2)
            return 2; // DOS: "c:" (but not "c:d")
    }
    // URL
    const schemeEnd = path.indexOf(urlSchemeSeparator);
    if (schemeEnd !== -1) {
        const authorityStart = schemeEnd + urlSchemeSeparator.length;
        const authorityEnd = path.indexOf('/', authorityStart);
        if (authorityEnd !== -1) {
            // URL: "file:///", "file://server/", "file://server/path"
            // For local "file" URLs, include the leading DOS volume (if present).
            // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
            // special case interpreted as "the machine from which the URL is being interpreted".
            const scheme = path.slice(0, schemeEnd);
            const authority = path.slice(authorityStart, authorityEnd);
            if (scheme === 'file' && (authority === '' || authority === 'localhost') && isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
                const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
                if (volumeSeparatorEnd !== -1) {
                    if (path.charCodeAt(volumeSeparatorEnd) === 47 /* slash */) {
                        // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
                        return ~(volumeSeparatorEnd + 1);
                    }
                    if (volumeSeparatorEnd === path.length) {
                        // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
                        // but not "file:///c:d" or "file:///c%3ad"
                        return ~volumeSeparatorEnd;
                    }
                }
            }
            return ~(authorityEnd + 1); // URL: "file://server/", "http://server/"
        }
        return ~path.length; // URL: "file://server", "http://server"
    }
    // relative
    return 0;
};
const isVolumeCharacter = (charCode) => (charCode >= 97 /* a */ && charCode <= 122 /* z */) || (charCode >= 65 /* A */ && charCode <= 90 /* Z */);
const getFileUrlVolumeSeparatorEnd = (url, start) => {
    const ch0 = url.charCodeAt(start);
    if (ch0 === 58 /* colon */)
        return start + 1;
    if (ch0 === 37 /* percent */ && url.charCodeAt(start + 1) === 51 /* _3 */) {
        const ch2 = url.charCodeAt(start + 2);
        if (ch2 === 97 /* a */ || ch2 === 65 /* A */)
            return start + 3;
    }
    return -1;
};
const pathComponents = (path, rootLength) => {
    const root = path.substring(0, rootLength);
    const rest = path.substring(rootLength).split('/');
    const restLen = rest.length;
    if (restLen > 0 && !rest[restLen - 1]) {
        rest.pop();
    }
    return [root, ...rest];
};

const validateComponentTag = (tag) => {
    if (tag !== tag.trim()) {
        return `Tag can not contain white spaces`;
    }
    if (tag !== tag.toLowerCase()) {
        return `Tag can not contain upper case characters`;
    }
    if (typeof tag !== 'string') {
        return `Tag "${tag}" must be a string type`;
    }
    if (tag.length === 0) {
        return `Received empty tag value`;
    }
    if (tag.indexOf(' ') > -1) {
        return `"${tag}" tag cannot contain a space`;
    }
    if (tag.indexOf(',') > -1) {
        return `"${tag}" tag cannot be used for multiple tags`;
    }
    const invalidChars = tag.replace(/\w|-/g, '');
    if (invalidChars !== '') {
        return `"${tag}" tag contains invalid characters: ${invalidChars}`;
    }
    if (tag.indexOf('-') === -1) {
        return `"${tag}" tag must contain a dash (-) to work as a valid web component`;
    }
    if (tag.indexOf('--') > -1) {
        return `"${tag}" tag cannot contain multiple dashes (--) next to each other`;
    }
    if (tag.indexOf('-') === 0) {
        return `"${tag}" tag cannot start with a dash (-)`;
    }
    if (tag.lastIndexOf('-') === tag.length - 1) {
        return `"${tag}" tag cannot end with a dash (-)`;
    }
    return undefined;
};

const parseFlags = (args, sys) => {
    const flags = {
        task: null,
        args: [],
        knownArgs: [],
        unknownArgs: null,
    };
    // cmd line has more priority over npm scripts cmd
    flags.args = args.slice();
    if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
        flags.task = flags.args[0];
    }
    parseArgs(flags, flags.args, flags.knownArgs);
    if (sys && sys.name === 'node') {
        const envArgs = getNpmConfigEnvArgs(sys);
        parseArgs(flags, envArgs, flags.knownArgs);
        envArgs.forEach(envArg => {
            if (!flags.args.includes(envArg)) {
                flags.args.push(envArg);
            }
        });
    }
    if (flags.task != null) {
        const i = flags.args.indexOf(flags.task);
        if (i > -1) {
            flags.args.splice(i, 1);
        }
    }
    flags.unknownArgs = flags.args.filter((arg) => {
        return !flags.knownArgs.includes(arg);
    });
    return flags;
};
const parseArgs = (flags, args, knownArgs) => {
    ARG_OPTS.boolean.forEach(booleanName => {
        const alias = ARG_OPTS.alias[booleanName];
        const flagKey = configCase(booleanName);
        if (typeof flags[flagKey] !== 'boolean') {
            flags[flagKey] = null;
        }
        args.forEach(cmdArg => {
            if (cmdArg === `--${booleanName}`) {
                flags[flagKey] = true;
                knownArgs.push(cmdArg);
            }
            else if (cmdArg === `--${flagKey}`) {
                flags[flagKey] = true;
                knownArgs.push(cmdArg);
            }
            else if (cmdArg === `--no-${booleanName}`) {
                flags[flagKey] = false;
                knownArgs.push(cmdArg);
            }
            else if (cmdArg === `--no${dashToPascalCase(booleanName)}`) {
                flags[flagKey] = false;
                knownArgs.push(cmdArg);
            }
            else if (alias && cmdArg === `-${alias}`) {
                flags[flagKey] = true;
                knownArgs.push(cmdArg);
            }
        });
    });
    ARG_OPTS.string.forEach(stringName => {
        const alias = ARG_OPTS.alias[stringName];
        const flagKey = configCase(stringName);
        if (typeof flags[flagKey] !== 'string') {
            flags[flagKey] = null;
        }
        for (let i = 0; i < args.length; i++) {
            const cmdArg = args[i];
            if (cmdArg.startsWith(`--${stringName}=`)) {
                const values = cmdArg.split('=');
                values.shift();
                flags[flagKey] = values.join('=');
                knownArgs.push(cmdArg);
            }
            else if (cmdArg === `--${stringName}`) {
                flags[flagKey] = args[i + 1];
                knownArgs.push(cmdArg);
                knownArgs.push(args[i + 1]);
            }
            else if (cmdArg === `--${flagKey}`) {
                flags[flagKey] = args[i + 1];
                knownArgs.push(cmdArg);
                knownArgs.push(args[i + 1]);
            }
            else if (cmdArg.startsWith(`--${flagKey}=`)) {
                const values = cmdArg.split('=');
                values.shift();
                flags[flagKey] = values.join('=');
                knownArgs.push(cmdArg);
            }
            else if (alias) {
                if (cmdArg.startsWith(`-${alias}=`)) {
                    const values = cmdArg.split('=');
                    values.shift();
                    flags[flagKey] = values.join('=');
                    knownArgs.push(cmdArg);
                }
                else if (cmdArg === `-${alias}`) {
                    flags[flagKey] = args[i + 1];
                    knownArgs.push(args[i + 1]);
                }
            }
        }
    });
    ARG_OPTS.number.forEach(numberName => {
        const alias = ARG_OPTS.alias[numberName];
        const flagKey = configCase(numberName);
        if (typeof flags[flagKey] !== 'number') {
            flags[flagKey] = null;
        }
        for (let i = 0; i < args.length; i++) {
            const cmdArg = args[i];
            if (cmdArg.startsWith(`--${numberName}=`)) {
                const values = cmdArg.split('=');
                values.shift();
                flags[flagKey] = parseInt(values.join(''), 10);
                knownArgs.push(cmdArg);
            }
            else if (cmdArg === `--${numberName}`) {
                flags[flagKey] = parseInt(args[i + 1], 10);
                knownArgs.push(args[i + 1]);
            }
            else if (cmdArg.startsWith(`--${flagKey}=`)) {
                const values = cmdArg.split('=');
                values.shift();
                flags[flagKey] = parseInt(values.join(''), 10);
                knownArgs.push(cmdArg);
            }
            else if (cmdArg === `--${flagKey}`) {
                flags[flagKey] = parseInt(args[i + 1], 10);
                knownArgs.push(args[i + 1]);
            }
            else if (alias) {
                if (cmdArg.startsWith(`-${alias}=`)) {
                    const values = cmdArg.split('=');
                    values.shift();
                    flags[flagKey] = parseInt(values.join(''), 10);
                    knownArgs.push(cmdArg);
                }
                else if (cmdArg === `-${alias}`) {
                    flags[flagKey] = parseInt(args[i + 1], 10);
                    knownArgs.push(args[i + 1]);
                }
            }
        }
    });
};
const configCase = (prop) => {
    prop = dashToPascalCase(prop);
    return prop.charAt(0).toLowerCase() + prop.substr(1);
};
const ARG_OPTS = {
    boolean: [
        'build',
        'cache',
        'check-version',
        'ci',
        'compare',
        'debug',
        'dev',
        'devtools',
        'docs',
        'e2e',
        'es5',
        'esm',
        'headless',
        'help',
        'log',
        'open',
        'prerender',
        'prerender-external',
        'prod',
        'profile',
        'service-worker',
        'screenshot',
        'serve',
        'skip-node-check',
        'spec',
        'ssr',
        'stats',
        'update-screenshot',
        'verbose',
        'version',
        'watch',
    ],
    number: ['max-workers', 'port'],
    string: ['address', 'config', 'docs-json', 'emulate', 'log-level', 'root', 'screenshot-connector'],
    alias: {
        config: 'c',
        help: 'h',
        port: 'p',
        version: 'v',
    },
};
const getNpmConfigEnvArgs = (sys) => {
    // process.env.npm_config_argv
    // {"remain":["4444"],"cooked":["run","serve","--port","4444"],"original":["run","serve","--port","4444"]}
    let args = [];
    try {
        const npmConfigArgs = sys.getEnvironmentVar('npm_config_argv');
        if (npmConfigArgs) {
            args = JSON.parse(npmConfigArgs).original;
            if (args[0] === 'run') {
                args = args.slice(2);
            }
        }
    }
    catch (e) { }
    return args;
};

const dependencies = [
	{
		name: "@stencil/core",
		version: "2.1.1",
		main: "compiler/stencil.js",
		resources: [
			"package.json",
			"compiler/lib.d.ts",
			"compiler/lib.dom.d.ts",
			"compiler/lib.dom.iterable.d.ts",
			"compiler/lib.es2015.collection.d.ts",
			"compiler/lib.es2015.core.d.ts",
			"compiler/lib.es2015.d.ts",
			"compiler/lib.es2015.generator.d.ts",
			"compiler/lib.es2015.iterable.d.ts",
			"compiler/lib.es2015.promise.d.ts",
			"compiler/lib.es2015.proxy.d.ts",
			"compiler/lib.es2015.reflect.d.ts",
			"compiler/lib.es2015.symbol.d.ts",
			"compiler/lib.es2015.symbol.wellknown.d.ts",
			"compiler/lib.es2016.array.include.d.ts",
			"compiler/lib.es2016.d.ts",
			"compiler/lib.es2016.full.d.ts",
			"compiler/lib.es2017.d.ts",
			"compiler/lib.es2017.full.d.ts",
			"compiler/lib.es2017.intl.d.ts",
			"compiler/lib.es2017.object.d.ts",
			"compiler/lib.es2017.sharedmemory.d.ts",
			"compiler/lib.es2017.string.d.ts",
			"compiler/lib.es2017.typedarrays.d.ts",
			"compiler/lib.es2018.asyncgenerator.d.ts",
			"compiler/lib.es2018.asynciterable.d.ts",
			"compiler/lib.es2018.d.ts",
			"compiler/lib.es2018.full.d.ts",
			"compiler/lib.es2018.intl.d.ts",
			"compiler/lib.es2018.promise.d.ts",
			"compiler/lib.es2018.regexp.d.ts",
			"compiler/lib.es2019.array.d.ts",
			"compiler/lib.es2019.d.ts",
			"compiler/lib.es2019.full.d.ts",
			"compiler/lib.es2019.object.d.ts",
			"compiler/lib.es2019.string.d.ts",
			"compiler/lib.es2019.symbol.d.ts",
			"compiler/lib.es2020.bigint.d.ts",
			"compiler/lib.es2020.d.ts",
			"compiler/lib.es2020.full.d.ts",
			"compiler/lib.es2020.intl.d.ts",
			"compiler/lib.es2020.promise.d.ts",
			"compiler/lib.es2020.string.d.ts",
			"compiler/lib.es2020.symbol.wellknown.d.ts",
			"compiler/lib.es5.d.ts",
			"compiler/lib.es6.d.ts",
			"compiler/lib.esnext.d.ts",
			"compiler/lib.esnext.full.d.ts",
			"compiler/lib.esnext.intl.d.ts",
			"compiler/lib.esnext.promise.d.ts",
			"compiler/lib.esnext.string.d.ts",
			"compiler/lib.scripthost.d.ts",
			"compiler/lib.webworker.d.ts",
			"compiler/lib.webworker.importscripts.d.ts",
			"internal/index.d.ts",
			"internal/index.js",
			"internal/package.json",
			"internal/stencil-ext-modules.d.ts",
			"internal/stencil-private.d.ts",
			"internal/stencil-public-compiler.d.ts",
			"internal/stencil-public-docs.d.ts",
			"internal/stencil-public-runtime.d.ts",
			"mock-doc/index.js",
			"mock-doc/package.json",
			"internal/client/css-shim.js",
			"internal/client/dom.js",
			"internal/client/index.js",
			"internal/client/package.json",
			"internal/client/patch-browser.js",
			"internal/client/patch-esm.js",
			"internal/client/shadow-css.js",
			"internal/hydrate/index.js",
			"internal/hydrate/package.json",
			"internal/hydrate/runner.js",
			"internal/hydrate/shadow-css.js",
			"internal/stencil-core/index.d.ts",
			"internal/stencil-core/index.js"
		]
	},
	{
		name: "rollup",
		version: "2.32.0",
		main: "dist/es/rollup.browser.js"
	},
	{
		name: "terser",
		version: "5.3.7",
		main: "dist/bundle.min.js"
	},
	{
		name: "typescript",
		version: "4.0.3",
		main: "lib/typescript.js"
	}
];

const findConfig = async (opts) => {
    const sys = opts.sys;
    const cwd = sys.getCurrentDirectory();
    const results = {
        configPath: null,
        rootDir: normalizePath(cwd),
        diagnostics: [],
    };
    let configPath = opts.configPath;
    if (isString(configPath)) {
        if (!sys.platformPath.isAbsolute(configPath)) {
            // passed in a custom stencil config location
            // but it's relative, so prefix the cwd
            configPath = normalizePath(sys.platformPath.join(cwd, configPath));
        }
        else {
            // config path already an absolute path, we're good here
            configPath = normalizePath(opts.configPath);
        }
    }
    else {
        // nothing was passed in, use the current working directory
        configPath = results.rootDir;
    }
    const stat = await sys.stat(configPath);
    if (stat.error) {
        const diagnostic = buildError(results.diagnostics);
        diagnostic.absFilePath = configPath;
        diagnostic.header = `Invalid config path`;
        diagnostic.messageText = `Config path "${configPath}" not found`;
        return results;
    }
    if (stat.isFile) {
        results.configPath = configPath;
        results.rootDir = sys.platformPath.dirname(configPath);
    }
    else if (stat.isDirectory) {
        // this is only a directory, so let's make some assumptions
        for (const configName of ['stencil.config.ts', 'stencil.config.js']) {
            const testConfigFilePath = sys.platformPath.join(configPath, configName);
            const stat = await sys.stat(testConfigFilePath);
            if (stat.isFile) {
                results.configPath = testConfigFilePath;
                results.rootDir = sys.platformPath.dirname(testConfigFilePath);
                break;
            }
        }
    }
    return results;
};

const loadCoreCompiler = async (sys) => {
    await sys.dynamicImport(sys.getCompilerExecutingPath());
    return globalThis.stencil;
};

const startupLog = (logger, task) => {
    if (task === 'info' || task === 'serve' || task === 'version') {
        return;
    }
    logger.info(logger.cyan(`@stencil/core`));
};
const startupLogVersion = (logger, task, coreCompiler) => {
    if (task === 'info' || task === 'serve' || task === 'version') {
        return;
    }
    const isDevBuild = coreCompiler.version.includes('-dev.');
    let startupMsg;
    if (isDevBuild) {
        startupMsg = logger.yellow('[LOCAL DEV]');
    }
    else {
        startupMsg = logger.cyan(`v${coreCompiler.version}`);
    }
    startupMsg += logger.emoji(' ' + coreCompiler.vermoji);
    logger.info(startupMsg);
};
const loadedCompilerLog = (sys, logger, flags, coreCompiler) => {
    const sysDetails = sys.details;
    const runtimeInfo = `${sys.name} ${sys.version}`;
    const platformInfo = `${sysDetails.platform}, ${sysDetails.cpuModel}`;
    const statsInfo = `cpus: ${sys.hardwareConcurrency}, freemem: ${Math.round(sysDetails.freemem() / 1000000)}MB, totalmem: ${Math.round(sysDetails.totalmem / 1000000)}MB`;
    if (logger.getLevel() === 'debug') {
        logger.debug(runtimeInfo);
        logger.debug(platformInfo);
        logger.debug(statsInfo);
        logger.debug(`compiler: ${sys.getCompilerExecutingPath()}`);
        logger.debug(`build: ${coreCompiler.buildId}`);
    }
    else if (flags.ci) {
        logger.info(runtimeInfo);
        logger.info(platformInfo);
        logger.info(statsInfo);
    }
};
const startupCompilerLog = (coreCompiler, config) => {
    if (config.suppressLogs === true) {
        return;
    }
    const { logger } = config;
    const isDebug = logger.getLevel() === 'debug';
    const isPrerelease = coreCompiler.version.includes('-');
    const isDevBuild = coreCompiler.version.includes('-dev.');
    if (isPrerelease && !isDevBuild) {
        logger.warn(logger.yellow(`This is a prerelease build, undocumented changes might happen at any time. Technical support is not available for prereleases, but any assistance testing is appreciated.`));
    }
    if (config.devMode && !isDebug) {
        if (config.buildEs5) {
            logger.warn(`Generating ES5 during development is a very task expensive, initial and incremental builds will be much slower. Drop the '--es5' flag and use a modern browser for development.`);
        }
        if (!config.enableCache) {
            logger.warn(`Disabling cache during development will slow down incremental builds.`);
        }
    }
};

const taskPrerender = async (coreCompiler, config) => {
    startupCompilerLog(coreCompiler, config);
    const hydrateAppFilePath = config.flags.unknownArgs[0];
    if (typeof hydrateAppFilePath !== 'string') {
        config.logger.error(`Missing hydrate app script path`);
        return config.sys.exit(1);
    }
    const srcIndexHtmlPath = config.srcIndexHtml;
    const diagnostics = await runPrerenderTask(coreCompiler, config, hydrateAppFilePath, null, srcIndexHtmlPath);
    config.logger.printDiagnostics(diagnostics);
    if (diagnostics.some(d => d.level === 'error')) {
        return config.sys.exit(1);
    }
};
const runPrerenderTask = async (coreCompiler, config, hydrateAppFilePath, componentGraph, srcIndexHtmlPath) => {
    const diagnostics = [];
    try {
        const prerenderer = await coreCompiler.createPrerenderer(config);
        const results = await prerenderer.start({
            hydrateAppFilePath,
            componentGraph,
            srcIndexHtmlPath,
        });
        diagnostics.push(...results.diagnostics);
    }
    catch (e) {
        catchError(diagnostics, e);
    }
    return diagnostics;
};

const startCheckVersion = async (config, currentVersion) => {
    if (config.devMode && !config.flags.ci && !currentVersion.includes('-dev.') && isFunction(config.sys.checkVersion)) {
        return config.sys.checkVersion(config.logger, currentVersion);
    }
    return null;
};
const printCheckVersionResults = async (versionChecker) => {
    if (versionChecker) {
        const checkVersionResults = await versionChecker;
        if (isFunction(checkVersionResults)) {
            checkVersionResults();
        }
    }
};

const taskWatch = async (coreCompiler, config) => {
    let devServer = null;
    let exitCode = 0;
    try {
        startupCompilerLog(coreCompiler, config);
        const versionChecker = startCheckVersion(config, coreCompiler.version);
        const compiler = await coreCompiler.createCompiler(config);
        const watcher = await compiler.createWatcher();
        if (config.flags.serve) {
            const devServerPath = config.sys.getDevServerExecutingPath();
            const { start } = await config.sys.dynamicImport(devServerPath);
            devServer = await start(config.devServer, config.logger, watcher);
        }
        config.sys.onProcessInterrupt(() => {
            config.logger.debug(`close watch`);
            compiler && compiler.destroy();
        });
        const rmVersionCheckerLog = watcher.on('buildFinish', async () => {
            // log the version check one time
            rmVersionCheckerLog();
            printCheckVersionResults(versionChecker);
        });
        if (devServer) {
            const rmDevServerLog = watcher.on('buildFinish', () => {
                // log the dev server url one time
                rmDevServerLog();
                config.logger.info(`${config.logger.cyan(devServer.browserUrl)}\n`);
            });
        }
        const closeResults = await watcher.start();
        if (closeResults.exitCode > 0) {
            exitCode = closeResults.exitCode;
        }
    }
    catch (e) {
        exitCode = 1;
        config.logger.error(e);
    }
    if (devServer) {
        await devServer.close();
    }
    if (exitCode > 0) {
        return config.sys.exit(exitCode);
    }
};

const taskBuild = async (coreCompiler, config) => {
    if (config.flags.watch) {
        // watch build
        await taskWatch(coreCompiler, config);
        return;
    }
    // one-time build
    let exitCode = 0;
    try {
        startupCompilerLog(coreCompiler, config);
        const versionChecker = startCheckVersion(config, coreCompiler.version);
        const compiler = await coreCompiler.createCompiler(config);
        const results = await compiler.build();
        await compiler.destroy();
        if (results.hasError) {
            exitCode = 1;
        }
        else if (config.flags.prerender) {
            const prerenderDiagnostics = await runPrerenderTask(coreCompiler, config, results.hydrateAppFilePath, results.componentGraph, null);
            config.logger.printDiagnostics(prerenderDiagnostics);
            if (prerenderDiagnostics.some(d => d.level === 'error')) {
                exitCode = 1;
            }
        }
        await printCheckVersionResults(versionChecker);
    }
    catch (e) {
        exitCode = 1;
        config.logger.error(e);
    }
    if (exitCode > 0) {
        return config.sys.exit(exitCode);
    }
};

const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
const DOCS_CUSTOM = 'docs-custom';
const DOCS_JSON = `docs-json`;
const DOCS_README = `docs-readme`;
const DOCS_VSCODE = `docs-vscode`;

const taskDocs = async (coreCompiler, config) => {
    config.devServer = null;
    config.outputTargets = config.outputTargets.filter(isOutputTargetDocs);
    config.devMode = true;
    startupCompilerLog(coreCompiler, config);
    const compiler = await coreCompiler.createCompiler(config);
    await compiler.build();
    await compiler.destroy();
};

const IS_DENO_ENV = typeof Deno !== 'undefined';
const IS_NODE_ENV = !IS_DENO_ENV &&
    typeof global !== 'undefined' &&
    typeof require === 'function' &&
    !!global.process &&
    typeof __filename === 'string' &&
    (!global.origin || typeof global.origin !== 'string');

/**
 * Task to generate component boilerplate.
 */
const taskGenerate = async (coreCompiler, config) => {
    if (!IS_NODE_ENV) {
        config.logger.error(`"generate" command is currently only implemented for a NodeJS environment`);
        return config.sys.exit(1);
    }
    const path = coreCompiler.path;
    if (!config.configPath) {
        config.logger.error('Please run this command in your root directory (i. e. the one containing stencil.config.ts).');
        return config.sys.exit(1);
    }
    const absoluteSrcDir = config.srcDir;
    if (!absoluteSrcDir) {
        config.logger.error(`Stencil's srcDir was not specified.`);
        return config.sys.exit(1);
    }
    const { prompt } = await import('../sys/node/prompts.js');
    const input = config.flags.unknownArgs.find(arg => !arg.startsWith('-')) ||
        (await prompt({ name: 'tagName', type: 'text', message: 'Component tag name (dash-case):' })).tagName;
    const { dir, base: componentName } = path.parse(input);
    const tagError = validateComponentTag(componentName);
    if (tagError) {
        config.logger.error(tagError);
        return config.sys.exit(1);
    }
    const extensionsToGenerate = ['tsx', ...(await chooseFilesToGenerate())];
    const testFolder = extensionsToGenerate.some(isTest) ? 'test' : '';
    const outDir = path.join(absoluteSrcDir, 'components', dir, componentName);
    await config.sys.createDir(path.join(outDir, testFolder), { recursive: true });
    const writtenFiles = await Promise.all(extensionsToGenerate.map(extension => writeFileByExtension(coreCompiler, config, outDir, componentName, extension, extensionsToGenerate.includes('css')))).catch(error => config.logger.error(error));
    if (!writtenFiles) {
        return config.sys.exit(1);
    }
    console.log();
    console.log(`${config.logger.gray('$')} stencil generate ${input}`);
    console.log();
    console.log(config.logger.bold('The following files have been generated:'));
    const absoluteRootDir = config.rootDir;
    writtenFiles.map(file => console.log(`  - ${path.relative(absoluteRootDir, file)}`));
};
/**
 * Show a checkbox prompt to select the files to be generated.
 */
const chooseFilesToGenerate = async () => {
    const { prompt } = await import('../sys/node/prompts.js');
    return (await prompt({
        name: 'filesToGenerate',
        type: 'multiselect',
        message: 'Which additional files do you want to generate?',
        choices: [
            { value: 'css', title: 'Stylesheet (.css)', selected: true },
            { value: 'spec.tsx', title: 'Spec Test  (.spec.tsx)', selected: true },
            { value: 'e2e.ts', title: 'E2E Test (.e2e.ts)', selected: true },
        ],
    })).filesToGenerate;
};
/**
 * Get a file's boilerplate by its extension and write it to disk.
 */
const writeFileByExtension = async (coreCompiler, config, path, name, extension, withCss) => {
    if (isTest(extension)) {
        path = coreCompiler.path.join(path, 'test');
    }
    const outFile = coreCompiler.path.join(path, `${name}.${extension}`);
    const boilerplate = getBoilerplateByExtension(name, extension, withCss);
    await config.sys.writeFile(outFile, boilerplate);
    return outFile;
};
const isTest = (extension) => {
    return extension === 'e2e.ts' || extension === 'spec.tsx';
};
/**
 * Get the boilerplate for a file by its extension.
 */
const getBoilerplateByExtension = (tagName, extension, withCss) => {
    switch (extension) {
        case 'tsx':
            return getComponentBoilerplate(tagName, withCss);
        case 'css':
            return getStyleUrlBoilerplate();
        case 'spec.tsx':
            return getSpecTestBoilerplate(tagName);
        case 'e2e.ts':
            return getE2eTestBoilerplate(tagName);
        default:
            throw new Error(`Unkown extension "${extension}".`);
    }
};
/**
 * Get the boilerplate for a component.
 */
const getComponentBoilerplate = (tagName, hasStyle) => {
    const decorator = [`{`];
    decorator.push(`  tag: '${tagName}',`);
    if (hasStyle) {
        decorator.push(`  styleUrl: '${tagName}.css',`);
    }
    decorator.push(`  shadow: true,`);
    decorator.push(`}`);
    return `import { Component, Host, h } from '@stencil/core';

@Component(${decorator.join('\n')})
export class ${toPascalCase(tagName)} {

  render() {
    return (
      <Host>
        <slot></slot>
      </Host>
    );
  }

}
`;
};
/**
 * Get the boilerplate for style.
 */
const getStyleUrlBoilerplate = () => `:host {
  display: block;
}
`;
/**
 * Get the boilerplate for a spec test.
 */
const getSpecTestBoilerplate = (tagName) => `import { newSpecPage } from '@stencil/core/testing';
import { ${toPascalCase(tagName)} } from '../${tagName}';

describe('${tagName}', () => {
  it('renders', async () => {
    const page = await newSpecPage({
      components: [${toPascalCase(tagName)}],
      html: \`<${tagName}></${tagName}>\`,
    });
    expect(page.root).toEqualHtml(\`
      <${tagName}>
        <mock:shadow-root>
          <slot></slot>
        </mock:shadow-root>
      </${tagName}>
    \`);
  });
});
`;
/**
 * Get the boilerplate for an E2E test.
 */
const getE2eTestBoilerplate = (name) => `import { newE2EPage } from '@stencil/core/testing';

describe('${name}', () => {
  it('renders', async () => {
    const page = await newE2EPage();
    await page.setContent('<${name}></${name}>');

    const element = await page.find('${name}');
    expect(element).toHaveClass('hydrated');
  });
});
`;
/**
 * Convert a dash case string to pascal case.
 */
const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.substr(1), '');

const taskHelp = (sys, logger) => {
    const p = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
    console.log(`
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}

    ${p} ${logger.green('stencil build [--dev] [--watch] [--prerender] [--debug]')}

      ${logger.cyan('--dev')} ${logger.dim('.............')} Development build
      ${logger.cyan('--watch')} ${logger.dim('...........')} Rebuild when files update
      ${logger.cyan('--serve')} ${logger.dim('...........')} Start the dev-server
      ${logger.cyan('--prerender')} ${logger.dim('.......')} Prerender the application
      ${logger.cyan('--docs')} ${logger.dim('............')} Generate component readme.md docs
      ${logger.cyan('--config')} ${logger.dim('..........')} Set stencil config file
      ${logger.cyan('--stats')} ${logger.dim('...........')} Write stencil-stats.json file
      ${logger.cyan('--log')} ${logger.dim('.............')} Write stencil-build.log file
      ${logger.cyan('--debug')} ${logger.dim('...........')} Set the log level to debug


  ${logger.bold('Test:')} ${logger.dim('Run unit and end-to-end tests.')}

    ${p} ${logger.green('stencil test [--spec] [--e2e]')}

      ${logger.cyan('--spec')} ${logger.dim('............')} Run unit tests with Jest
      ${logger.cyan('--e2e')} ${logger.dim('.............')} Run e2e tests with Puppeteer


  ${logger.bold('Generate:')} ${logger.dim('Bootstrap components.')}

    ${p} ${logger.green('stencil generate')} or ${logger.green('stencil g')}


  ${logger.bold('Examples:')}

    ${p} ${logger.green('stencil build --dev --watch --serve')}
    ${p} ${logger.green('stencil build --prerender')}
    ${p} ${logger.green('stencil test --spec --e2e')}
    ${p} ${logger.green('stencil generate')}
    ${p} ${logger.green('stencil g my-component')}

`);
};

const taskInfo = (coreCompiler, sys, logger) => {
    const details = sys.details;
    const versions = coreCompiler.versions;
    console.log(``);
    console.log(`${logger.cyan('      System:')} ${sys.name} ${sys.version}`);
    console.log(`${logger.cyan('     Plaform:')} ${details.platform} (${details.release})`);
    console.log(`${logger.cyan('   CPU Model:')} ${details.cpuModel} (${sys.hardwareConcurrency} cpu${sys.hardwareConcurrency !== 1 ? 's' : ''})`);
    console.log(`${logger.cyan('    Compiler:')} ${sys.getCompilerExecutingPath()}`);
    console.log(`${logger.cyan('       Build:')} ${coreCompiler.buildId}`);
    console.log(`${logger.cyan('     Stencil:')} ${coreCompiler.version}${logger.emoji(' ' + coreCompiler.vermoji)}`);
    console.log(`${logger.cyan('  TypeScript:')} ${versions.typescript}`);
    console.log(`${logger.cyan('      Rollup:')} ${versions.rollup}`);
    console.log(`${logger.cyan('      Parse5:')} ${versions.parse5}`);
    console.log(`${logger.cyan('      Sizzle:')} ${versions.sizzle}`);
    console.log(`${logger.cyan('      Terser:')} ${versions.terser}`);
    console.log(``);
};

const taskServe = async (config) => {
    config.suppressLogs = true;
    config.flags.serve = true;
    config.devServer.openBrowser = config.flags.open;
    config.devServer.reloadStrategy = null;
    config.devServer.initialLoadUrl = '/';
    config.devServer.websocket = false;
    config.maxConcurrentWorkers = 1;
    config.devServer.root = isString(config.flags.root) ? config.flags.root : config.sys.getCurrentDirectory();
    const devServerPath = config.sys.getDevServerExecutingPath();
    const { start } = await config.sys.dynamicImport(devServerPath);
    const devServer = await start(config.devServer, config.logger);
    console.log(`${config.logger.cyan('     Root:')} ${devServer.root}`);
    console.log(`${config.logger.cyan('  Address:')} ${devServer.address}`);
    console.log(`${config.logger.cyan('     Port:')} ${devServer.port}`);
    console.log(`${config.logger.cyan('   Server:')} ${devServer.browserUrl}`);
    console.log(``);
    config.sys.onProcessInterrupt(() => {
        if (devServer) {
            config.logger.debug(`dev server close: ${devServer.browserUrl}`);
            devServer.close();
        }
    });
};

const taskTest = async (config) => {
    if (!IS_NODE_ENV) {
        config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
        return config.sys.exit(1);
    }
    try {
        config.buildDocs = false;
        const testingRunOpts = {
            e2e: !!config.flags.e2e,
            screenshot: !!config.flags.screenshot,
            spec: !!config.flags.spec,
            updateScreenshot: !!config.flags.updateScreenshot,
        };
        // always ensure we have jest modules installed
        const ensureModuleIds = ['@types/jest', 'jest', 'jest-cli'];
        if (testingRunOpts.e2e) {
            // if it's an e2e test, also make sure we're got
            // puppeteer modules installed and if browserExecutablePath is provided don't download Chromium use only puppeteer-core instead
            const puppeteer = config.testing.browserExecutablePath ? 'puppeteer-core' : 'puppeteer';
            ensureModuleIds.push('@types/puppeteer', puppeteer);
            if (testingRunOpts.screenshot) {
                // ensure we've got pixelmatch for screenshots
                config.logger.warn(config.logger.yellow(`EXPERIMENTAL: screenshot visual diff testing is currently under heavy development and has not reached a stable status. However, any assistance testing would be appreciated.`));
            }
        }
        // ensure we've got the required modules installed
        const diagnostics = await config.sys.lazyRequire.ensure(config.rootDir, ensureModuleIds);
        if (diagnostics.length > 0) {
            config.logger.printDiagnostics(diagnostics);
            return config.sys.exit(1);
        }
        // let's test!
        const { createTesting } = await import('../testing/index.js');
        const testing = await createTesting(config);
        const passed = await testing.run(testingRunOpts);
        await testing.destroy();
        if (!passed) {
            return config.sys.exit(1);
        }
    }
    catch (e) {
        config.logger.error(e);
        return config.sys.exit(1);
    }
};

const run = async (init) => {
    const { args, logger, sys } = init;
    try {
        const flags = parseFlags(args, sys);
        const task = flags.task;
        if (flags.debug || flags.verbose) {
            logger.setLevel('debug');
        }
        if (flags.ci) {
            logger.enableColors(false);
        }
        if (isFunction(sys.applyGlobalPatch)) {
            sys.applyGlobalPatch(sys.getCurrentDirectory());
        }
        if (task === 'help' || flags.help) {
            taskHelp(sys, logger);
            return;
        }
        startupLog(logger, task);
        const findConfigResults = await findConfig({ sys, configPath: flags.config });
        if (hasError(findConfigResults.diagnostics)) {
            logger.printDiagnostics(findConfigResults.diagnostics);
            return sys.exit(1);
        }
        const ensureDepsResults = await sys.ensureDependencies({
            rootDir: findConfigResults.rootDir,
            logger,
            dependencies: dependencies,
        });
        if (hasError(ensureDepsResults.diagnostics)) {
            logger.printDiagnostics(ensureDepsResults.diagnostics);
            return sys.exit(1);
        }
        const coreCompiler = await loadCoreCompiler(sys);
        if (task === 'version' || flags.version) {
            console.log(coreCompiler.version);
            return;
        }
        startupLogVersion(logger, task, coreCompiler);
        loadedCompilerLog(sys, logger, flags, coreCompiler);
        if (task === 'info') {
            taskInfo(coreCompiler, sys, logger);
            return;
        }
        const validated = await coreCompiler.loadConfig({
            config: {
                flags,
            },
            configPath: findConfigResults.configPath,
            logger,
            sys,
        });
        if (validated.diagnostics.length > 0) {
            logger.printDiagnostics(validated.diagnostics);
            if (hasError(validated.diagnostics)) {
                return sys.exit(1);
            }
        }
        if (isFunction(sys.applyGlobalPatch)) {
            sys.applyGlobalPatch(validated.config.rootDir);
        }
        await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
        await runTask(coreCompiler, validated.config, task);
    }
    catch (e) {
        if (!shouldIgnoreError(e)) {
            logger.error(`uncaught cli error: ${e}${logger.getLevel() === 'debug' ? e.stack : ''}`);
            return sys.exit(1);
        }
    }
};
const runTask = async (coreCompiler, config, task) => {
    config.flags = config.flags || {};
    config.outputTargets = config.outputTargets || [];
    switch (task) {
        case 'build':
            await taskBuild(coreCompiler, config);
            break;
        case 'docs':
            await taskDocs(coreCompiler, config);
            break;
        case 'help':
            taskHelp(config.sys, config.logger);
            break;
        case 'generate':
        case 'g':
            await taskGenerate(coreCompiler, config);
            break;
        case 'prerender':
            await taskPrerender(coreCompiler, config);
            break;
        case 'serve':
            await taskServe(config);
            break;
        case 'test':
            await taskTest(config);
            break;
        case 'version':
            console.log(coreCompiler.version);
            break;
        default:
            config.logger.error(`${config.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
            taskHelp(config.sys, config.logger);
            return config.sys.exit(1);
    }
};

export { parseFlags, run, runTask };
