UNPKG

@stencil/core

Version:

A Compiler for Web Components and Progressive Web Apps

1,433 lines (1,410 loc) 92.1 kB
/*! Stencil CLI (CommonJS) v2.19.0 | MIT Licensed | https://stenciljs.com */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n['default'] = e; return Object.freeze(n); } /** * Convert a string from PascalCase to dash-case * * @param str the string to convert * @returns a converted string */ const toDashCase = (str) => str .replace(/([A-Z0-9])/g, (match) => ` ${match[0]}`) .trim() .split(' ') .join('-') .toLowerCase(); /** * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase, * or whatever you call it!) * * @param str a string to convert * @returns a converted string */ const dashToPascalCase = (str) => str .toLowerCase() .split('-') .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) .join(''); const isFunction = (v) => typeof v === 'function'; const isString = (v) => typeof v === 'string'; /** * Builds a template `Diagnostic` entity for a build error. The created `Diagnostic` is returned, and have little * detail attached to it regarding the specifics of the error - it is the responsibility of the caller of this method * to attach the specifics of the error message. * * The created `Diagnostic` is pushed to the `diagnostics` argument as a side effect of calling this method. * * @param diagnostics the existing diagnostics that the created template `Diagnostic` should be added to * @returns the created `Diagnostic` */ 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; }; /** * Builds a diagnostic from an `Error`, appends it to the `diagnostics` parameter, and returns the created diagnostic * @param diagnostics the series of diagnostics the newly created diagnostics should be added to * @param err the error to derive information from in generating the diagnostic * @param msg an optional message to use in place of `err` to generate the diagnostic * @returns the generated 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.length ? msg : 'UNKNOWN ERROR'; } else if (err != null) { if (err.stack != null) { diagnostic.messageText = err.stack.toString(); } else { if (err.message != null) { diagnostic.messageText = err.message.length ? err.message : 'UNKNOWN ERROR'; } else { diagnostic.messageText = err.toString(); } } } if (diagnostics != null && !shouldIgnoreError(diagnostic.messageText)) { diagnostics.push(diagnostic); } return diagnostic; }; /** * Determine if the provided diagnostics have any build errors * @param diagnostics the diagnostics to inspect * @returns true if any of the diagnostics in the list provided are errors that did not occur at runtime. false * otherwise. */ 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. * @param path the Windows-based path to convert * @returns the converted path */ 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 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) { if (path.charCodeAt(1) !== ch0) return 1; // POSIX: "/" (or non-normalized "\") const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.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 /* CharacterCodes.colon */) { const ch2 = path.charCodeAt(2); if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.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 /* CharacterCodes.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 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) || (charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */); const getFileUrlVolumeSeparatorEnd = (url, start) => { const ch0 = url.charCodeAt(start); if (ch0 === 58 /* CharacterCodes.colon */) return start + 1; if (ch0 === 37 /* CharacterCodes.percent */ && url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) { const ch2 = url.charCodeAt(start + 2); if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.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]; }; /** * Check whether a string is a member of a ReadonlyArray<string> * * We need a little helper for this because unfortunately `includes` is typed * on `ReadonlyArray<T>` as `(el: T): boolean` so a `string` cannot be passed * to `includes` on a `ReadonlyArray` 😢 thus we have a little helper function * where we do the type coercion just once. * * see microsoft/TypeScript#31018 for some discussion of this * * @param readOnlyArray the array we're checking * @param maybeMember a value which is possibly a member of the array * @returns whether the array contains the member or not */ const readOnlyArrayHasStringMember = (readOnlyArray, maybeMember) => readOnlyArray.includes(maybeMember); /** * Validates that a component tag meets required naming conventions to be used for a web component * @param tag the tag to validate * @returns an error message if the tag has an invalid name, undefined if the tag name passes all checks */ const validateComponentTag = (tag) => { // we want to check this first since we call some String.prototype methods below if (typeof tag !== 'string') { return `Tag "${tag}" must be a string type`; } if (tag !== tag.trim()) { return `Tag can not contain white spaces`; } if (tag !== tag.toLowerCase()) { return `Tag can not contain upper case characters`; } 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; }; /** * This sets the log level hierarchy for our terminal logger, ranging from * most to least verbose. * * Ordering the levels like this lets us easily check whether we should log a * message at a given time. For instance, if the log level is set to `'warn'`, * then anything passed to the logger with level `'warn'` or `'error'` should * be logged, but we should _not_ log anything with level `'info'` or `'debug'`. * * If we have a current log level `currentLevel` and a message with level * `msgLevel` is passed to the logger, we can determine whether or not we should * log it by checking if the log level on the message is further up or at the * same level in the hierarchy than `currentLevel`, like so: * * ```ts * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel) * ``` * * NOTE: for the reasons described above, do not change the order of the entries * in this array without good reason! */ const LOG_LEVELS = ['debug', 'info', 'warn', 'error']; /** * All the Boolean options supported by the Stencil CLI */ const BOOLEAN_CLI_ARGS = [ 'build', 'cache', 'checkVersion', 'ci', 'compare', 'debug', 'dev', 'devtools', 'docs', 'e2e', 'es5', 'esm', 'headless', 'help', 'log', 'open', 'prerender', 'prerenderExternal', 'prod', 'profile', 'serviceWorker', 'screenshot', 'serve', 'skipNodeCheck', 'spec', 'ssr', 'stats', 'updateScreenshot', 'verbose', 'version', 'watch', // JEST CLI OPTIONS 'all', 'automock', 'bail', // 'cache', Stencil already supports this argument 'changedFilesWithAncestor', // 'ci', Stencil already supports this argument 'clearCache', 'clearMocks', 'collectCoverage', 'color', 'colors', 'coverage', // 'debug', Stencil already supports this argument 'detectLeaks', 'detectOpenHandles', 'errorOnDeprecated', 'expand', 'findRelatedTests', 'forceExit', 'init', 'injectGlobals', 'json', 'lastCommit', 'listTests', 'logHeapUsage', 'noStackTrace', 'notify', 'onlyChanged', 'onlyFailures', 'passWithNoTests', 'resetMocks', 'resetModules', 'restoreMocks', 'runInBand', 'runTestsByPath', 'showConfig', 'silent', 'skipFilter', 'testLocationInResults', 'updateSnapshot', 'useStderr', // 'verbose', Stencil already supports this argument // 'version', Stencil already supports this argument // 'watch', Stencil already supports this argument 'watchAll', 'watchman', ]; /** * All the Number options supported by the Stencil CLI */ const NUMBER_CLI_ARGS = [ 'port', // JEST CLI ARGS 'maxConcurrency', 'testTimeout', ]; /** * All the String options supported by the Stencil CLI */ const STRING_CLI_ARGS = [ 'address', 'config', 'docsApi', 'docsJson', 'emulate', 'root', 'screenshotConnector', // JEST CLI ARGS 'cacheDirectory', 'changedSince', 'collectCoverageFrom', // 'config', Stencil already supports this argument 'coverageDirectory', 'coverageThreshold', 'env', 'filter', 'globalSetup', 'globalTeardown', 'globals', 'haste', 'moduleNameMapper', 'notifyMode', 'outputFile', 'preset', 'prettierPath', 'resolver', 'rootDir', 'runner', 'testEnvironment', 'testEnvironmentOptions', 'testFailureExitCode', 'testNamePattern', 'testResultsProcessor', 'testRunner', 'testSequencer', 'testURL', 'timers', 'transform', // ARRAY ARGS 'collectCoverageOnlyFrom', 'coveragePathIgnorePatterns', 'coverageReporters', 'moduleDirectories', 'moduleFileExtensions', 'modulePathIgnorePatterns', 'modulePaths', 'projects', 'reporters', 'roots', 'selectProjects', 'setupFiles', 'setupFilesAfterEnv', 'snapshotSerializers', 'testMatch', 'testPathIgnorePatterns', 'testPathPattern', 'testRegex', 'transformIgnorePatterns', 'unmockedModulePathPatterns', 'watchPathIgnorePatterns', ]; /** * All the CLI arguments which may have string or number values * * `maxWorkers` is an argument which is used both by Stencil _and_ by Jest, * which means that we need to support parsing both string and number values. */ const STRING_NUMBER_CLI_ARGS = ['maxWorkers']; /** * All the LogLevel-type options supported by the Stencil CLI * * This is a bit silly since there's only one such argument atm, * but this approach lets us make sure that we're handling all * our arguments in a type-safe way. */ const LOG_LEVEL_CLI_ARGS = ['logLevel']; /** * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'` */ const CLI_ARG_ALIASES = { config: 'c', help: 'h', port: 'p', version: 'v', }; /** * Helper function for initializing a `ConfigFlags` object. Provide any overrides * for default values and off you go! * * @param init an object with any overrides for default values * @returns a complete CLI flag object */ const createConfigFlags = (init = {}) => { const flags = { task: null, args: [], knownArgs: [], unknownArgs: [], ...init, }; return flags; }; /** * Parse command line arguments into a structured `ConfigFlags` object * * @param args an array of CLI flags * @param _sys an optional compiler system * @returns a structured ConfigFlags object */ const parseFlags = (args, _sys) => { // TODO(STENCIL-509): remove the _sys parameter here ^^ (for v3) const flags = createConfigFlags(); // cmd line has more priority over npm scripts cmd flags.args = Array.isArray(args) ? args.slice() : []; if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) { flags.task = flags.args[0]; } parseArgs(flags, flags.args); if (flags.task != null) { const i = flags.args.indexOf(flags.task); if (i > -1) { flags.args.splice(i, 1); } } // to find unknown / unrecognized arguments we filter `args`, including only // arguments whose normalized form is not found in `knownArgs`. `knownArgs` // is populated during the call to `parseArgs` above. For arguments like // `--foobar` the string `"--foobar"` will be added, while for more // complicated arguments like `--bizBoz=bop` or `--bizBoz bop` just the // string `"--bizBoz"` will be added. flags.unknownArgs = flags.args.filter((arg) => !flags.knownArgs.includes(parseEqualsArg(arg)[0])); return flags; }; /** * Parse command line arguments that are enumerated in the `config-flags` * module. Handles leading dashes on arguments, aliases that are defined for a * small number of arguments, and parsing values for non-boolean arguments * (e.g. port number for the dev server). * * @param flags a ConfigFlags object to which parsed arguments will be added * @param args an array of command-line arguments to parse */ const parseArgs = (flags, args) => { BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName)); STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName)); NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName)); STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName)); LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName)); }; /** * Parse a boolean CLI argument. For these, we support the following formats: * * - `--booleanArg` * - `--boolean-arg` * - `--noBooleanArg` * - `--no-boolean-arg` * * The final two variants should be parsed to a value of `false` on the config * object. * * @param flags the config flags object, while we'll modify * @param args our CLI arguments * @param configCaseName the argument we want to look at right now */ const parseBooleanArg = (flags, args, configCaseName) => { // we support both dash-case and PascalCase versions of the parameter // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS const alias = CLI_ARG_ALIASES[configCaseName]; const dashCaseName = toDashCase(configCaseName); if (typeof flags[configCaseName] !== 'boolean') { flags[configCaseName] = null; } args.forEach((cmdArg) => { let value; if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) { value = true; } else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) { value = false; } else if (alias && cmdArg === `-${alias}`) { value = true; } if (value !== undefined && cmdArg !== undefined) { flags[configCaseName] = value; flags.knownArgs.push(cmdArg); } }); }; /** * Parse a string CLI argument * * @param flags the config flags object, while we'll modify * @param args our CLI arguments * @param configCaseName the argument we want to look at right now */ const parseStringArg = (flags, args, configCaseName) => { if (typeof flags[configCaseName] !== 'string') { flags[configCaseName] = null; } const { value, matchingArg } = getValue(args, configCaseName); if (value !== undefined && matchingArg !== undefined) { flags[configCaseName] = value; flags.knownArgs.push(matchingArg); flags.knownArgs.push(value); } }; /** * Parse a number CLI argument * * @param flags the config flags object, while we'll modify * @param args our CLI arguments * @param configCaseName the argument we want to look at right now */ const parseNumberArg = (flags, args, configCaseName) => { if (typeof flags[configCaseName] !== 'number') { flags[configCaseName] = null; } const { value, matchingArg } = getValue(args, configCaseName); if (value !== undefined && matchingArg !== undefined) { flags[configCaseName] = parseInt(value, 10); flags.knownArgs.push(matchingArg); flags.knownArgs.push(value); } }; /** * Parse a CLI argument which may be either a string or a number * * @param flags the config flags object, while we'll modify * @param args our CLI arguments * @param configCaseName the argument we want to look at right now */ const parseStringNumberArg = (flags, args, configCaseName) => { if (!['number', 'string'].includes(typeof flags[configCaseName])) { flags[configCaseName] = null; } const { value, matchingArg } = getValue(args, configCaseName); if (value !== undefined && matchingArg !== undefined) { if (CLI_ARG_STRING_REGEX.test(value)) { // if it matches the regex we treat it like a string flags[configCaseName] = value; } else { // it was a number, great! flags[configCaseName] = Number(value); } flags.knownArgs.push(matchingArg); flags.knownArgs.push(value); } }; /** * We use this regular expression to detect CLI parameters which * should be parsed as string values (as opposed to numbers) for * the argument types for which we support both a string and a * number value. * * The regex tests for the presence of at least one character which is * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`, * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to * support the admittedly unlikely use of scientific notation, like `"4e+0"` * for `4`). * * Thus we'll match a string like `"50%"`, but not a string like `"50"` or * `"5.0"`. If it matches a given string we conclude that the string should * be parsed as a string literal, rather than using `Number` to convert it * to a number. */ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g; /** * Parse a LogLevel CLI argument. These can be only a specific * set of strings, so this function takes care of validating that * the value is correct. * * @param flags the config flags object, while we'll modify * @param args our CLI arguments * @param configCaseName the argument we want to look at right now */ const parseLogLevelArg = (flags, args, configCaseName) => { if (typeof flags[configCaseName] !== 'string') { flags[configCaseName] = null; } const { value, matchingArg } = getValue(args, configCaseName); if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) { flags[configCaseName] = value; flags.knownArgs.push(matchingArg); flags.knownArgs.push(value); } }; /** * Helper for pulling values out from the raw array of CLI arguments. This logic * is shared between a few different types of CLI args. * * We look for arguments in the following formats: * * - `--my-cli-argument value` * - `--my-cli-argument=value` * - `--myCliArgument value` * - `--myCliArgument=value` * * We also check for shortened aliases, which we define for a few arguments. * * @param args the CLI args we're dealing with * @param configCaseName the ConfigFlag key which we're looking to pull out a value for * @returns the value for the flag as well as the exact string which it matched from * the user input. */ const getValue = (args, configCaseName) => { // for some CLI args we have a short alias, like 'c' for 'config' const alias = CLI_ARG_ALIASES[configCaseName]; // we support supplying arguments in both dash-case and configCase // for ease of use const dashCaseName = toDashCase(configCaseName); let value; let matchingArg; args.forEach((arg, i) => { if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) { // our argument was passed at the command-line in the format --argName=arg-value [matchingArg, value] = parseEqualsArg(arg); } else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) { // the next value in the array is assumed to be a value for this argument value = args[i + 1]; matchingArg = arg; } else if (alias) { if (arg.startsWith(`-${alias}=`)) { [matchingArg, value] = parseEqualsArg(arg); } else if (arg === `-${alias}`) { value = args[i + 1]; matchingArg = arg; } } }); return { value, matchingArg }; }; /** * Parse an 'equals' argument, which is a CLI argument-value pair in the * format `--foobar=12` (as opposed to a space-separated format like * `--foobar 12`). * * To parse this we split on the `=`, returning the first part as the argument * name and the second part as the value. We join the value on `"="` in case * there is another `"="` in the argument. * * This function is safe to call with any arg, and can therefore be used as * an argument 'normalizer'. If CLI argument is not an 'equals' argument then * the return value will be a tuple of the original argument and an empty * string `""` for the value. * * In code terms, if you do: * * ```ts * const [arg, value] = parseEqualsArg("--myArgument") * ``` * * Then `arg` will be `"--myArgument"` and `value` will be `""`, whereas if * you do: * * * ```ts * const [arg, value] = parseEqualsArg("--myArgument=myValue") * ``` * * Then `arg` will be `"--myArgument"` and `value` will be `"myValue"`. * * @param arg the arg in question * @returns a tuple containing the arg name and the value (if present) */ const parseEqualsArg = (arg) => { const [originalArg, ...value] = arg.split('='); return [originalArg, value.join('=')]; }; /** * Small helper for getting type-system-level assurance that a `string` can be * narrowed to a `LogLevel` * * @param maybeLogLevel the string to check * @returns whether this is a `LogLevel` */ const isLogLevel = (maybeLogLevel) => readOnlyArrayHasStringMember(LOG_LEVELS, maybeLogLevel); const dependencies = [ { name: "@stencil/core", version: "2.19.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.date.d.ts", "compiler/lib.es2020.full.d.ts", "compiler/lib.es2020.intl.d.ts", "compiler/lib.es2020.number.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.intl.d.ts", "compiler/lib.es2021.promise.d.ts", "compiler/lib.es2021.string.d.ts", "compiler/lib.es2021.weakref.d.ts", "compiler/lib.es2022.array.d.ts", "compiler/lib.es2022.d.ts", "compiler/lib.es2022.error.d.ts", "compiler/lib.es2022.full.d.ts", "compiler/lib.es2022.intl.d.ts", "compiler/lib.es2022.object.d.ts", "compiler/lib.es2022.string.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.7.4", main: "lib/typescript.js" } ]; const IS_NODE_ENV = typeof global !== 'undefined' && typeof require === 'function' && !!global.process && typeof __filename === 'string' && (!global.origin || typeof global.origin !== 'string'); const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined'; /** * Creates an instance of a logger * @returns the new logger instance */ const createLogger = () => { let useColors = IS_BROWSER_ENV; let level = 'info'; return { enableColors: (uc) => (useColors = uc), getLevel: () => level, setLevel: (l) => (level = l), emoji: (e) => e, info: console.log.bind(console), warn: console.warn.bind(console), error: console.error.bind(console), debug: console.debug.bind(console), red: (msg) => msg, green: (msg) => msg, yellow: (msg) => msg, blue: (msg) => msg, magenta: (msg) => msg, cyan: (msg) => msg, gray: (msg) => msg, bold: (msg) => msg, dim: (msg) => msg, bgRed: (msg) => msg, createTimeSpan: (_startMsg, _debug = false) => ({ duration: () => 0, finish: () => 0, }), printDiagnostics(diagnostics) { diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors)); }, }; }; const logDiagnostic = (diagnostic, useColors) => { let color = BLUE; let prefix = 'Build'; let msg = ''; if (diagnostic.level === 'error') { color = RED; prefix = 'Error'; } else if (diagnostic.level === 'warn') { color = YELLOW; prefix = 'Warning'; } if (diagnostic.header) { prefix = diagnostic.header; } const filePath = diagnostic.relFilePath || diagnostic.absFilePath; if (filePath) { msg += filePath; if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) { msg += ', line ' + diagnostic.lineNumber; if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) { msg += ', column ' + diagnostic.columnNumber; } } msg += '\n'; } msg += diagnostic.messageText; if (diagnostic.lines && diagnostic.lines.length > 0) { diagnostic.lines.forEach((l) => { msg += '\n' + l.lineNumber + ': ' + l.text; }); msg += '\n'; } if (useColors) { const styledPrefix = [ '%c' + prefix, `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`, ]; console.log(...styledPrefix, msg); } else if (diagnostic.level === 'error') { console.error(msg); } else if (diagnostic.level === 'warn') { console.warn(msg); } else { console.log(msg); } }; const YELLOW = `#f39c12`; const RED = `#c0392b`; const BLUE = `#3498db`; /** * Attempt to find a Stencil configuration file on the file system * @param opts the options needed to find the configuration file * @returns the results of attempting to find a configuration file on disk */ const findConfig = async (opts) => { const sys = opts.sys; const cwd = sys.getCurrentDirectory(); const rootDir = normalizePath(cwd); 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 = rootDir; } const results = { configPath, rootDir: normalizePath(cwd), diagnostics: [], }; 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; }; /** * Log the name of this package (`@stencil/core`) to an output stream * * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function * * The name of the package may not be logged, by design, for certain `task` types and logging levels * * @param logger the logging entity to use to output the name of the package * @param task the current task */ const startupLog = (logger, task) => { if (task === 'info' || task === 'serve' || task === 'version') { return; } logger.info(logger.cyan(`@stencil/core`)); }; /** * Log this package's version to an output stream * * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function * * The package version may not be logged, by design, for certain `task` types and logging levels * * @param logger the logging entity to use for output * @param task the current task * @param coreCompiler the compiler instance to derive version information from */ 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); }; /** * Log details from a {@link CompilerSystem} used by Stencil to an output stream * * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function * * @param sys the `CompilerSystem` to report details on * @param logger the logging entity to use for output * @param flags user set flags for the current invocation of Stencil * @param coreCompiler the compiler instance being used for this invocation of Stencil */ const loadedCompilerLog = (sys, logger, flags, coreCompiler) => { const sysDetails = sys.details; const runtimeInfo = `${sys.name} ${sys.version}`; const platformInfo = sysDetails ? `${sysDetails.platform}, ${sysDetails.cpuModel}` : `Unknown Platform, Unknown CPU Model`; const statsInfo = sysDetails ? `cpus: ${sys.hardwareConcurrency}, freemem: ${Math.round(sysDetails.freemem() / 1000000)}MB, totalmem: ${Math.round(sysDetails.totalmem / 1000000)}MB` : 'Unknown CPU Core Count, Unknown Memory'; 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); } }; /** * Log various warnings to an output stream * * The output stream is determined by the {@link Logger} instance attached to the `config` argument to this function * * @param coreCompiler the compiler instance being used for this invocation of Stencil * @param config a validated configuration object to be used for this run of Stencil */ 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 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 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 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 isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT; const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE; const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script'; const DOCS_CUSTOM = 'docs-custom'; const DOCS_JSON = 'docs-json'; const DOCS_README = 'docs-readme'; const DOCS_VSCODE = 'docs-vscode'; const WWW = 'www'; const tryFn = async (fn, ...args) => { try { return await fn(...args); } catch (_a) { // ignore } return null; }; const isInteractive = (sys, flags, object) => { const terminalInfo = object || Object.freeze({ tty: sys.isTTY() ? true : false, ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => { var _a; return !!((_a = sys.getEnvironmentVar) === null || _a === void 0 ? void 0 : _a.call(sys, v)); }).length > 0 || !!flags.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 flags The configuration flags passed into the Stencil command * @returns true if --debug has been passed, otherwise false */ function hasDebug(flags) { return !!flags.debug; } /** * Does the command have the verbose and debug flags? * @param flags The configuration flags passed into the Stencil command * @returns true if both --debug and --verbose have been passed, otherwise false */ function hasVerbose(flags) { return !!flags.verbose && hasDebug(flags); } 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 (!config['tokens.telemetry'] || !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)); } /** * 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 * @retur