/*! Stencil CLI v2.10.0 | 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.10.0", 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.sharedmemory.d.ts", "compiler/lib.es2020.string.d.ts", "compiler/lib.es2020.symbol.wellknown.d.ts", "compiler/lib.es2021.d.ts", "compiler/lib.es2021.full.d.ts", "compiler/lib.es2021.promise.d.ts", "compiler/lib.es2021.string.d.ts", "compiler/lib.es2021.weakref.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.esnext.weakref.d.ts", "compiler/lib.scripthost.d.ts", "compiler/lib.webworker.d.ts", "compiler/lib.webworker.importscripts.d.ts", "compiler/lib.webworker.iterable.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.42.3", main: "dist/es/rollup.browser.js" }, { name: "terser", version: "5.6.1", main: "dist/bundle.min.js" }, { name: "typescript", version: "4.3.5", 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 tryFn = async (fn, ...args) => { try { return await fn(...args); } catch (_a) { // ignore } return null; }; const isInteractive = (sys, config, object) => { var _a; const terminalInfo = object || Object.freeze({ tty: sys.isTTY() ? true : false, ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!((_a = config.flags) === null || _a === void 0 ? void 0 : _a.ci), }); return terminalInfo.tty && !terminalInfo.ci; }; const UUID_REGEX = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i); // Plucked from https://github.com/ionic-team/capacitor/blob/b893a57aaaf3a16e13db9c33037a12f1a5ac92e0/cli/src/util/uuid.ts function uuidv4() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } /** * Reads and parses a JSON file from the given `path` * @param sys The system where the command is invoked * @param path the path on the file system to read and parse * @returns the parsed JSON */ async function readJson(sys, path) { const file = await sys.readFile(path); return !!file && JSON.parse(file); } /** * Does the command have the debug flag? * @param config The config passed into the Stencil command * @returns true if --debug has been passed, otherwise false */ function hasDebug(config) { return config.flags.debug; } /** * Does the command have the verbose and debug flags? * @param config The config passed into the Stencil command * @returns true if both --debug and --verbose have been passed, otherwise false */ function hasVerbose(config) { return config.flags.verbose && hasDebug(config); } /** * Used to determine if tracking should occur. * @param config The config passed into the Stencil command * @param sys The system where the command is invoked * @param ci whether or not the process is running in a Continuous Integration (CI) environment * @returns true if telemetry should be sent, false otherwise */ async function shouldTrack(config, sys, ci) { return !ci && isInteractive(sys, config) && (await checkTelemetry(sys)); } const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined; const defaultConfig = (sys) => sys.resolvePath(`${sys.homeDir()}/.ionic/${isTest$1() ? 'tmp-config.json' : 'config.json'}`); const defaultConfigDirectory = (sys) => sys.resolvePath(`${sys.homeDir()}/.ionic`); /** * Reads an Ionic configuration file from disk, parses it, and performs any necessary corrections to it if certain * values are deemed to be malformed * @param sys The system where the command is invoked * @returns the config read from disk that has been potentially been updated */ async function readConfig(sys) { let config = await readJson(sys, defaultConfig(sys)); if (!config) { config = { 'tokens.telemetry': uuidv4(), 'telemetry.stencil': true, }; await writeConfig(sys, config); } else if (!UUID_REGEX.test(config['tokens.telemetry'])) { const newUuid = uuidv4(); await writeConfig(sys, { ...config, 'tokens.telemetry': newUuid }); config['tokens.telemetry'] = newUuid; } return config; } /** * Writes an Ionic configuration file to disk. * @param sys The system where the command is invoked * @param config The config passed into the Stencil command * @returns boolean If the command was successful */ async function writeConfig(sys, config) { let result = false; try { await sys.createDir(defaultConfigDirectory(sys), { recursive: true }); await sys.writeFile(defaultConfig(sys), JSON.stringify(config, null, 2)); result = true; } catch (error) { console.error(`Stencil Telemetry: couldn't write configuration file to ${defaultConfig(sys)} - ${error}.`); } return result; } /** * Update a subset of the Ionic config. * @param sys The system where the command is invoked * @param newOptions The new options to save * @returns boolean If the command was successful */ async function updateConfig(sys, newOptions) { const config = await readConfig(sys); return await writeConfig(sys, Object.assign(config, newOptions)); } 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 WWW = `www`; /** * Used to within taskBuild to provide the component_count property. * * @param sys The system where the command is invoked * @param config The config passed into the Stencil command * @param logger The tool used to do logging * @param coreCompiler The compiler used to do builds * @param result The results of a compiler build. */ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) { const tracking = await shouldTrack(config, sys, config.flags.ci); if (!tracking) { return; } const component_count = Object.keys(result.componentGraph).length; const data = await prepareData(coreCompiler, config, sys, result.duration, component_count); await sendMetric(sys, config, 'stencil_cli_command', data); logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`); } /** * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows. * @param sys The system where the command is invoked * @param config The config passed into the Stencil command * @param logger The tool used to do logging * @param coreCompiler The compiler used to do builds * @param action A Promise-based function to call in order to get the duration of any given command. * @returns void */ async function telemetryAction(sys, config, logger, coreCompiler, action) { var _a; const tracking = await shouldTrack(config, sys, !!((_a = config === null || config === void 0 ? void 0 : config.flags) === null || _a === void 0 ? void 0 : _a.ci)); let duration = undefined; let error; if (action) { const start = new Date(); try { await action(); } catch (e) { error = e; } const end = new Date(); duration = end.getTime() - start.getTime(); } // We'll get componentCount details inside the taskBuild, so let's not send two messages. if (!tracking || (config.flags.task == 'build' && !config.flags.args.includes('--watch'))) { return; } const data = await prepareData(coreCompiler, config, sys, duration); await sendMetric(sys, config, 'stencil_cli_command', data); logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`); if (error) { throw error; } } function hasAppTarget(config) { return config.outputTargets.some((target) => target.type === WWW && (!!target.serviceWorker || (!!target.baseUrl && target.baseUrl !== '/'))); } function isUsingYarn(sys) { var _a; return ((_a = sys.getEnvironmentVar('npm_execpath')) === null || _a === void 0 ? void 0 : _a.includes('yarn')) || false; } async function getActiveTargets(config) { const result = config.outputTargets.map((t) => t.type); return Array.from(new Set(result)); } const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => { const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' }; const { packages, packagesNoVersions } = await getInstalledPackages(sys, config); const targets = await getActiveTargets(config); const yarn = isUsingYarn(sys); const stencil = coreCompiler.version || 'unknown'; const system = `${sys.name} ${sys.version}`; const os_name = sys.details.platform; const os_version = sys.details.release; const cpu_model = sys.details.cpuModel; const build = coreCompiler.buildId || 'unknown'; const has_app_pwa_config = hasAppTarget(config); return { yarn, duration_ms, component_count, targets, packages, packages_no_versions: packagesNoVersions, arguments: config.flags.args, task: config.flags.task, stencil, system, system_major: getMajorVersion(system), os_name, os_version, cpu_model, build, typescript, rollup, has_app_pwa_config, }; }; /** * Reads package-lock.json, yarn.lock, and package.json files in order to cross reference * the dependencies and devDependencies properties. Pulls up the current installed version * of each package under the @stencil, @ionic, and @capacitor scopes. * @returns string[] */ async function getInstalledPackages(sys, config) { let packages = []; let packagesNoVersions = []; const yarn = isUsingYarn(sys); try { // Read package.json and package-lock.json const appRootDir = sys.getCurrentDirectory(); const packageJson = await tryFn(readJson, sys, sys.resolvePath(appRootDir + '/package.json')); // They don't have a package.json for some reason? Eject button. if (!packageJson) { return { packages, packagesNoVersions }; } const rawPackages = Object.entries({ ...packageJson.devDependencies, ...packageJson.dependencies, }); // Collect packages only in the stencil, ionic, or capacitor org's: // https://www.npmjs.com/org/stencil const ionicPackages = rawPackages.filter(([k]) => k.startsWith('@stencil/') || k.startsWith('@ionic/') || k.startsWith('@capacitor/')); try { packages = yarn ? await yarnPackages(sys, ionicPackages) : await npmPackages(sys, ionicPackages); } catch (e) { packages = ionicPackages.map(([k, v]) => `${k}@${v.replace('^', '')}`); } packagesNoVersions = ionicPackages.map(([k]) => `${k}`); return { packages, packagesNoVersions }; } catch (err) { hasDebug(config) && console.error(err); return { packages, packagesNoVersions }; } } /** * Visits the npm lock file to find the exact versions that are installed * @param sys The system where the command is invoked * @param ionicPackages a list of the found packages matching `@stencil`, `@capacitor`, or `@ionic` from the package.json file. * @returns an array of strings of all the packages and their versions. */ async function npmPackages(sys, ionicPackages) { const appRootDir = sys.getCurrentDirectory(); const packageLockJson = await tryFn(readJson, sys, sys.resolvePath(appRootDir + '/package-lock.json')); return ionicPackages.map(([k, v]) => { var _a, _b, _c, _d; let version = (_d = (_b = (_a = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.dependencies[k]) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : (_c = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.devDependencies[k]) === null || _c === void 0 ? void 0 : _c.version) !== null && _d !== void 0 ? _d : v; version = version.includes('file:') ? sanitizeDeclaredVersion(v) : version; return `${k}@${version}`; }); } /** * Visits the yarn lock file to find the exact versions that are installed * @param sys The system where the command is invoked * @param ionicPackages a list of the found packages matching `@stencil`, `@capacitor`, or `@ionic` from the package.json file. * @returns an array of strings of all the packages and their versions. */ async function yarnPackages(sys, ionicPackages) { const appRootDir = sys.getCurrentDirectory(); const yarnLock = sys.readFileSync(sys.resolvePath(appRootDir + '/yarn.lock')); const yarnLockYml = sys.parseYarnLockFile(yarnLock); return ionicPackages.map(([k, v]) => { var _a; const identifiedVersion = `${k}@${v}`; let version = (_a = yarnLockYml.object[identifiedVersion]) === null || _a === void 0 ? void 0 : _a.version; version = version.includes('undefined') ? sanitizeDeclaredVersion(identifiedVersion) : version; return `${k}@${version}`; }); } /** * This function is used for fallback purposes, where an npm or yarn lock file doesn't exist in the consumers directory. * This will strip away '*', '^' and '~' from the declared package versions in a package.json. * @param version the raw semver pattern identifier version string * @returns a cleaned up representation without any qualifiers */ function sanitizeDeclaredVersion(version) { return version.replace(/[*^~]/g, ''); } /** * If telemetry is enabled, send a metric via IPC to a forked process for uploading. */ async function sendMetric(sys, config, name, value) { const session_id = await getTelemetryToken(sys); const message = { name, timestamp: new Date().toISOString(), source: 'stencil_cli', value, session_id, }; await sendTelemetry(sys, config, { type: 'telemetry', message }); } /** * Used to read the config file's tokens.telemetry property. * @param sys The system where the command is invoked * @returns string */ async function getTelemetryToken(sys) { const config = await readConfig(sys); if (config['tokens.telemetry'] === undefined) { config['tokens.telemetry'] = uuidv4(); await writeConfig(sys, config); } return config['tokens.telemetry']; } /** * Issues a request to the telemetry server. * @param sys The system where the command is invoked * @param config The config passed into the Stencil command * @param data Data to be tracked */ async function sendTelemetry(sys, config, data) { try { const now = new Date().toISOString(); const body = { metrics: [data.message], sent_at: now, }; // This request is only made if telemetry is on. const response = await sys.fetch('https://api.ionicjs.com/events/metrics', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(body), }); hasVerbose(config) && console.debug('\nSent %O metric to events service (status: %O)', data.message.name, response.status, '\n'); if (response.status !== 204) { hasVerbose(config) && console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n'); } } catch (e) { hasVerbose(config) && console.debug('Telemetry request failed:', e); } } /** * Checks if telemetry is enabled on this machine * @param sys The system where the command is invoked * @returns true if telemetry is enabled, false otherwise */ async function checkTelemetry(sys) { const config = await readConfig(sys); if (config['telemetry.stencil'] === undefined) { config['telemetry.stencil'] = true; await writeConfig(sys, config); } return config['telemetry.stencil']; } /** * Writes to the config file, enabling telemetry for this machine. * @param sys The system where the command is invoked * @returns true if writing the file was successful, false otherwise */ async function enableTelemetry(sys) { return await updateConfig(sys, { 'telemetry.stencil': true }); } /** * Writes to the config file, disabling telemetry for this machine. * @param sys The system where the command is invoked * @returns true if writing the file was successful, false otherwise */ async function disableTelemetry(sys) { return await updateConfig(sys, { 'telemetry.stencil': false }); } /** * Takes in a semver string in order to return the major version. * @param version The fully qualified semver version * @returns a string of the major version */ function getMajorVersion(version) { const parts = version.split('.'); return parts[0]; } const taskBuild = async (coreCompiler, config, sys) => { 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(); // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement if (sys) { await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results); } 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 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_NODE_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 ( ); } } `; }; /** * 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}>\`, }); expect(page.root).toEqualHtml(\` <${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}>'); 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 taskTelemetry = async (config, sys, logger) => { const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$'); const isEnabling = config.flags.args.includes('on'); const isDisabling = config.flags.args.includes('off'); const INFORMATION = `Opt in or our of telemetry. Information about the data we collect is available on our website: ${logger.bold('https://stenciljs.com/telemetry')}`; const THANK_YOU = `Thank you for helping to make Stencil better! 💖`; const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`; const DISABLED_MESSAGE = `${logger.red('Disabled')}\n\n`; const hasTelemetry = await checkTelemetry(sys); if (isEnabling) { const result = await enableTelemetry(sys); result ? console.log(`\n ${logger.bold('Telemetry is now ') + ENABLED_MESSAGE}`) : console.log(`Something went wrong when enabling Telemetry.`); return; } if (isDisabling) { const result = await disableTelemetry(sys); result ? console.log(`\n ${logger.bold('Telemetry is now ') + DISABLED_MESSAGE}`) : console.log(`Something went wrong when disabling Telemetry.`); return; } console.log(` ${logger.bold('Telemetry:')} ${logger.dim(INFORMATION)}`); console.log(`\n ${logger.bold('Status')}: ${hasTelemetry ? ENABLED_MESSAGE : DISABLED_MESSAGE}`); console.log(` ${prompt} ${logger.green('stencil telemetry [off|on]')} ${logger.cyan('off')} ${logger.dim('.............')} Disable sharing anonymous usage data ${logger.cyan('on')} ${logger.dim('..............')} Enable sharing anonymous usage data `); }; const taskHelp = async (config, logger, sys) => { const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$'); console.log(` ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')} ${prompt} ${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.')} ${prompt} ${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.')} ${prompt} ${logger.green('stencil generate')} or ${logger.green('stencil g')} `); // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement if (sys) { await taskTelemetry(config, sys, logger); } console.log(` ${logger.bold('Examples:')} ${prompt} ${logger.green('stencil build --dev --watch --serve')} ${prompt} ${logger.green('stencil build --prerender')} ${prompt} ${logger.green('stencil test --spec --e2e')} ${prompt} ${logger.green('stencil telemetry on')} ${prompt} ${logger.green('stencil generate')} ${prompt} ${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(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) { await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys); 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') { await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => { await 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 telemetryAction(sys, validated.config, logger, coreCompiler, async () => { await runTask(coreCompiler, validated.config, task, sys); }); } 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, sys) => { config.flags = config.flags || { task }; config.outputTargets = config.outputTargets || []; switch (task) { case 'build': await taskBuild(coreCompiler, config, sys); break; case 'docs': await taskDocs(coreCompiler, config); break; case 'generate': case 'g': await taskGenerate(coreCompiler, config); break; case 'help': taskHelp(config, config.logger, sys); break; case 'prerender': await taskPrerender(coreCompiler, config); break; case 'serve': await taskServe(config); break; case 'telemetry': // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement if (sys) { await taskTelemetry(config, sys, config.logger); } 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, config.logger, sys); return config.sys.exit(1); } }; export { parseFlags, run, runTask }; //# sourceMappingURL=index.js.map