UNPKG

@stencil/core

Version:

A Compiler for Web Components and Progressive Web Apps

1,622 lines (1,556 loc) 5.85 MB
/*! Stencil Compiler v2.14.0 | MIT Licensed | https://stenciljs.com */ (function(exports) { 'use strict'; if (typeof globalThis === 'undefined') { if (typeof self !== 'undefined') { self.globalThis = self; } else if (typeof window !== 'undefined') { window.globalThis = window; } else if (typeof global !== 'undefined') { global.globalThis = global; } } const Buffer = globalThis.Buffer || {}; const process = globalThis.process || {}; if (!process.argv) { process.argv = ['']; } let __cwd = '/'; if (!process.cwd) { process.cwd = () => __cwd; } if (!process.chdir) { process.chdir = (v) => __cwd = v; } if (!process.nextTick) { const resolved = Promise.resolve(); process.nextTick = (cb) => resolved.then(cb); } if (!process.platform) { process.platform = 'stencil'; } if (!process.version) { process.version = 'v12.0.0'; } process.browser = !!globalThis.location; // 'path' module extracted from Node.js v8.11.1 (only the posix part) function assertPath(path) { if (typeof path !== 'string') { throw new TypeError('Path must be a string. Received ' + JSON.stringify(path)); } } // Resolves . and .. elements in a path with directory names function normalizeStringPosix(path, allowAboveRoot) { var res = ''; var lastSegmentLength = 0; var lastSlash = -1; var dots = 0; var code; for (var i = 0; i <= path.length; ++i) { if (i < path.length) code = path.charCodeAt(i); else if (code === 47 /*/*/) break; else code = 47 /*/*/; if (code === 47 /*/*/) { if (lastSlash === i - 1 || dots === 1) ; else if (lastSlash !== i - 1 && dots === 2) { if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 /*.*/ || res.charCodeAt(res.length - 2) !== 46 /*.*/) { if (res.length > 2) { var lastSlashIndex = res.lastIndexOf('/'); if (lastSlashIndex !== res.length - 1) { if (lastSlashIndex === -1) { res = ''; lastSegmentLength = 0; } else { res = res.slice(0, lastSlashIndex); lastSegmentLength = res.length - 1 - res.lastIndexOf('/'); } lastSlash = i; dots = 0; continue; } } else if (res.length === 2 || res.length === 1) { res = ''; lastSegmentLength = 0; lastSlash = i; dots = 0; continue; } } if (allowAboveRoot) { if (res.length > 0) res += '/..'; else res = '..'; lastSegmentLength = 2; } } else { if (res.length > 0) res += '/' + path.slice(lastSlash + 1, i); else res = path.slice(lastSlash + 1, i); lastSegmentLength = i - lastSlash - 1; } lastSlash = i; dots = 0; } else if (code === 46 /*.*/ && dots !== -1) { ++dots; } else { dots = -1; } } return res; } function _format(sep, pathObject) { var dir = pathObject.dir || pathObject.root; var base = pathObject.base || (pathObject.name || '') + (pathObject.ext || ''); if (!dir) { return base; } if (dir === pathObject.root) { return dir + base; } return dir + sep + base; } var posix$2 = { // path.resolve([from ...], to) resolve: function resolve() { var resolvedPath = ''; var resolvedAbsolute = false; var cwd; for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path; if (i >= 0) path = arguments[i]; else { if (cwd === undefined) cwd = process.cwd(); path = cwd; } assertPath(path); // Skip empty entries if (path.length === 0) { continue; } resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path.charCodeAt(0) === 47 /*/*/; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) // Normalize the path resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute); if (resolvedAbsolute) { if (resolvedPath.length > 0) return '/' + resolvedPath; else return '/'; } else if (resolvedPath.length > 0) { return resolvedPath; } else { return '.'; } }, normalize: function normalize(path) { assertPath(path); if (path.length === 0) return '.'; var isAbsolute = path.charCodeAt(0) === 47 /*/*/; var trailingSeparator = path.charCodeAt(path.length - 1) === 47 /*/*/; // Normalize the path path = normalizeStringPosix(path, !isAbsolute); if (path.length === 0 && !isAbsolute) path = '.'; if (path.length > 0 && trailingSeparator) path += '/'; if (isAbsolute) return '/' + path; return path; }, isAbsolute: function isAbsolute(path) { assertPath(path); return path.length > 0 && path.charCodeAt(0) === 47 /*/*/; }, join: function join() { if (arguments.length === 0) return '.'; var joined; for (var i = 0; i < arguments.length; ++i) { var arg = arguments[i]; assertPath(arg); if (arg.length > 0) { if (joined === undefined) joined = arg; else joined += '/' + arg; } } if (joined === undefined) return '.'; return posix$2.normalize(joined); }, relative: function relative(from, to) { assertPath(from); assertPath(to); if (from === to) return ''; from = posix$2.resolve(from); to = posix$2.resolve(to); if (from === to) return ''; // Trim any leading backslashes var fromStart = 1; for (; fromStart < from.length; ++fromStart) { if (from.charCodeAt(fromStart) !== 47 /*/*/) break; } var fromEnd = from.length; var fromLen = fromEnd - fromStart; // Trim any leading backslashes var toStart = 1; for (; toStart < to.length; ++toStart) { if (to.charCodeAt(toStart) !== 47 /*/*/) break; } var toEnd = to.length; var toLen = toEnd - toStart; // Compare paths to find the longest common path from root var length = fromLen < toLen ? fromLen : toLen; var lastCommonSep = -1; var i = 0; for (; i <= length; ++i) { if (i === length) { if (toLen > length) { if (to.charCodeAt(toStart + i) === 47 /*/*/) { // We get here if `from` is the exact base path for `to`. // For example: from='/foo/bar'; to='/foo/bar/baz' return to.slice(toStart + i + 1); } else if (i === 0) { // We get here if `from` is the root // For example: from='/'; to='/foo' return to.slice(toStart + i); } } else if (fromLen > length) { if (from.charCodeAt(fromStart + i) === 47 /*/*/) { // We get here if `to` is the exact base path for `from`. // For example: from='/foo/bar/baz'; to='/foo/bar' lastCommonSep = i; } else if (i === 0) { // We get here if `to` is the root. // For example: from='/foo'; to='/' lastCommonSep = 0; } } break; } var fromCode = from.charCodeAt(fromStart + i); var toCode = to.charCodeAt(toStart + i); if (fromCode !== toCode) break; else if (fromCode === 47 /*/*/) lastCommonSep = i; } var out = ''; // Generate the relative path based on the path difference between `to` // and `from` for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { if (i === fromEnd || from.charCodeAt(i) === 47 /*/*/) { if (out.length === 0) out += '..'; else out += '/..'; } } // Lastly, append the rest of the destination (`to`) path that comes after // the common path parts if (out.length > 0) return out + to.slice(toStart + lastCommonSep); else { toStart += lastCommonSep; if (to.charCodeAt(toStart) === 47 /*/*/) ++toStart; return to.slice(toStart); } }, _makeLong: function _makeLong(path) { return path; }, dirname: function dirname(path) { assertPath(path); if (path.length === 0) return '.'; var code = path.charCodeAt(0); var hasRoot = code === 47 /*/*/; var end = -1; var matchedSlash = true; for (var i = path.length - 1; i >= 1; --i) { code = path.charCodeAt(i); if (code === 47 /*/*/) { if (!matchedSlash) { end = i; break; } } else { // We saw the first non-path separator matchedSlash = false; } } if (end === -1) return hasRoot ? '/' : '.'; if (hasRoot && end === 1) return '//'; return path.slice(0, end); }, basename: function basename(path, ext) { if (ext !== undefined && typeof ext !== 'string') throw new TypeError('"ext" argument must be a string'); assertPath(path); var start = 0; var end = -1; var matchedSlash = true; var i; if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { if (ext.length === path.length && ext === path) return ''; var extIdx = ext.length - 1; var firstNonSlashEnd = -1; for (i = path.length - 1; i >= 0; --i) { var code = path.charCodeAt(i); if (code === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { start = i + 1; break; } } else { if (firstNonSlashEnd === -1) { // We saw the first non-path separator, remember this index in case // we need it if the extension ends up not matching matchedSlash = false; firstNonSlashEnd = i + 1; } if (extIdx >= 0) { // Try to match the explicit extension if (code === ext.charCodeAt(extIdx)) { if (--extIdx === -1) { // We matched the extension, so mark this as the end of our path // component end = i; } } else { // Extension does not match, so our result is the entire path // component extIdx = -1; end = firstNonSlashEnd; } } } } if (start === end) end = firstNonSlashEnd;else if (end === -1) end = path.length; return path.slice(start, end); } else { for (i = path.length - 1; i >= 0; --i) { if (path.charCodeAt(i) === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { start = i + 1; break; } } else if (end === -1) { // We saw the first non-path separator, mark this as the end of our // path component matchedSlash = false; end = i + 1; } } if (end === -1) return ''; return path.slice(start, end); } }, extname: function extname(path) { assertPath(path); var startDot = -1; var startPart = 0; var end = -1; var matchedSlash = true; // Track the state of characters (if any) we see before our first dot and // after any path separator we find var preDotState = 0; for (var i = path.length - 1; i >= 0; --i) { var code = path.charCodeAt(i); if (code === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { // We saw the first non-path separator, mark this as the end of our // extension matchedSlash = false; end = i + 1; } if (code === 46 /*.*/) { // If this is our first dot, mark it as the start of our extension if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension preDotState = -1; } } if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot preDotState === 0 || // The (right-most) trimmed path component is exactly '..' preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { return ''; } return path.slice(startDot, end); }, format: function format(pathObject) { if (pathObject === null || typeof pathObject !== 'object') { throw new TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject); } return _format('/', pathObject); }, parse: function parse(path) { assertPath(path); var ret = { root: '', dir: '', base: '', ext: '', name: '' }; if (path.length === 0) return ret; var code = path.charCodeAt(0); var isAbsolute = code === 47 /*/*/; var start; if (isAbsolute) { ret.root = '/'; start = 1; } else { start = 0; } var startDot = -1; var startPart = 0; var end = -1; var matchedSlash = true; var i = path.length - 1; // Track the state of characters (if any) we see before our first dot and // after any path separator we find var preDotState = 0; // Get non-dir info for (; i >= start; --i) { code = path.charCodeAt(i); if (code === 47 /*/*/) { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { // We saw the first non-path separator, mark this as the end of our // extension matchedSlash = false; end = i + 1; } if (code === 46 /*.*/) { // If this is our first dot, mark it as the start of our extension if (startDot === -1) startDot = i;else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { // We saw a non-dot and non-path separator before our dot, so we should // have a good chance at having a non-empty extension preDotState = -1; } } if (startDot === -1 || end === -1 || // We saw a non-dot character immediately before the dot preDotState === 0 || // The (right-most) trimmed path component is exactly '..' preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) { if (end !== -1) { if (startPart === 0 && isAbsolute) ret.base = ret.name = path.slice(1, end);else ret.base = ret.name = path.slice(startPart, end); } } else { if (startPart === 0 && isAbsolute) { ret.name = path.slice(1, startDot); ret.base = path.slice(1, end); } else { ret.name = path.slice(startPart, startDot); ret.base = path.slice(startPart, end); } ret.ext = path.slice(startDot, end); } if (startPart > 0) ret.dir = path.slice(0, startPart - 1);else if (isAbsolute) ret.dir = '/'; return ret; }, sep: '/', delimiter: ':', win32: null, posix: null }; posix$2.posix = posix$2; var pathBrowserify = posix$2; const IS_NODE_ENV = typeof global !== 'undefined' && typeof require === 'function' && !!global.process && typeof __filename === 'string' && (!global.origin || typeof global.origin !== 'string'); const OS_PLATFORM = IS_NODE_ENV ? process.platform : ''; const IS_WINDOWS_ENV = OS_PLATFORM === 'win32'; const IS_CASE_SENSITIVE_FILE_NAMES = !IS_WINDOWS_ENV; const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined'; const IS_WEB_WORKER_ENV = IS_BROWSER_ENV && typeof self !== 'undefined' && typeof self.importScripts === 'function'; const HAS_WEB_WORKER = IS_BROWSER_ENV && typeof Worker === 'function'; const IS_FETCH_ENV = typeof fetch === 'function'; const requireFunc = IS_NODE_ENV ? require : () => { }; const getCurrentDirectory = IS_NODE_ENV ? process.cwd : () => '/'; /** * Default style mode id */ const DEFAULT_STYLE_MODE = '$'; /** * File names and value */ const COLLECTION_MANIFEST_FILE_NAME = 'collection-manifest.json'; const formatComponentRuntimeMeta = (compilerMeta, includeMethods) => { let flags = 0; if (compilerMeta.encapsulation === 'shadow') { flags |= 1 /* shadowDomEncapsulation */; if (compilerMeta.shadowDelegatesFocus) { flags |= 16 /* shadowDelegatesFocus */; } } else if (compilerMeta.encapsulation === 'scoped') { flags |= 2 /* scopedCssEncapsulation */; } if (compilerMeta.encapsulation !== 'shadow' && compilerMeta.htmlTagNames.includes('slot')) { flags |= 4 /* hasSlotRelocation */; } if (compilerMeta.hasMode) { flags |= 32 /* hasMode */; } const members = formatComponentRuntimeMembers(compilerMeta, includeMethods); const hostListeners = formatHostListeners(compilerMeta); return trimFalsy([ flags, compilerMeta.tagName, Object.keys(members).length > 0 ? members : undefined, hostListeners.length > 0 ? hostListeners : undefined, ]); }; const stringifyRuntimeData = (data) => { const json = JSON.stringify(data); if (json.length > 10000) { // JSON metadata is big, JSON.parse() is faster // https://twitter.com/mathias/status/1143551692732030979 return `JSON.parse(${JSON.stringify(json)})`; } return json; }; const formatComponentRuntimeMembers = (compilerMeta, includeMethods = true) => { return { ...formatPropertiesRuntimeMember(compilerMeta.properties), ...formatStatesRuntimeMember(compilerMeta.states), ...(includeMethods ? formatMethodsRuntimeMember(compilerMeta.methods) : {}), }; }; const formatPropertiesRuntimeMember = (properties) => { const runtimeMembers = {}; properties.forEach((member) => { runtimeMembers[member.name] = trimFalsy([ /** * [0] member type */ formatFlags(member), formatAttrName(member), ]); }); return runtimeMembers; }; const formatFlags = (compilerProperty) => { let type = formatPropType(compilerProperty.type); if (compilerProperty.mutable) { type |= 1024 /* Mutable */; } if (compilerProperty.reflect) { type |= 512 /* ReflectAttr */; } return type; }; const formatAttrName = (compilerProperty) => { if (typeof compilerProperty.attribute === 'string') { // string attr name means we should observe this attribute if (compilerProperty.name === compilerProperty.attribute) { // property name and attribute name are the exact same // true value means to use the property name for the attribute name return undefined; } // property name and attribute name are not the same // so we need to return the actual string value // example: "multiWord" !== "multi-word" return compilerProperty.attribute; } // we shouldn't even observe an attribute for this property return undefined; }; const formatPropType = (type) => { if (type === 'string') { return 1 /* String */; } if (type === 'number') { return 2 /* Number */; } if (type === 'boolean') { return 4 /* Boolean */; } if (type === 'any') { return 8 /* Any */; } return 16 /* Unknown */; }; const formatStatesRuntimeMember = (states) => { const runtimeMembers = {}; states.forEach((member) => { runtimeMembers[member.name] = [ 32 /* State */, ]; }); return runtimeMembers; }; const formatMethodsRuntimeMember = (methods) => { const runtimeMembers = {}; methods.forEach((member) => { runtimeMembers[member.name] = [ 64 /* Method */, ]; }); return runtimeMembers; }; const formatHostListeners = (compilerMeta) => { return compilerMeta.listeners.map((compilerListener) => { const hostListener = [ computeListenerFlags(compilerListener), compilerListener.name, compilerListener.method, ]; return hostListener; }); }; const computeListenerFlags = (listener) => { let flags = 0; if (listener.capture) { flags |= 2 /* Capture */; } if (listener.passive) { flags |= 1 /* Passive */; } switch (listener.target) { case 'document': flags |= 4 /* TargetDocument */; break; case 'window': flags |= 8 /* TargetWindow */; break; case 'body': flags |= 16 /* TargetBody */; break; case 'parent': flags |= 32 /* TargetParent */; break; } return flags; }; const trimFalsy = (data) => { const arr = data; for (var i = arr.length - 1; i >= 0; i--) { if (arr[i]) { break; } // if falsy, safe to pop() arr.pop(); } return arr; }; const toLowerCase = (str) => str.toLowerCase(); const toDashCase = (str) => toLowerCase(str .replace(/([A-Z0-9])/g, (g) => ' ' + g[0]) .trim() .replace(/ /g, '-')); const dashToPascalCase$1 = (str) => toLowerCase(str) .split('-') .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) .join(''); const toTitleCase = (str) => str.charAt(0).toUpperCase() + str.slice(1); const noop$1 = () => { /* noop*/ }; const sortBy = (array, prop) => { return array.slice().sort((a, b) => { const nameA = prop(a); const nameB = prop(b); if (nameA < nameB) return -1; if (nameA > nameB) return 1; return 0; }); }; const flatOne = (array) => { if (array.flat) { return array.flat(1); } return array.reduce((result, item) => { result.push(...item); return result; }, []); }; const unique = (array, predicate = (i) => i) => { const set = new Set(); return array.filter((item) => { const key = predicate(item); if (key == null) { return true; } if (set.has(key)) { return false; } set.add(key); return true; }); }; const fromEntries = (entries) => { const object = {}; for (const [key, value] of entries) { object[key] = value; } return object; }; const pluck = (obj, keys) => { return keys.reduce((final, key) => { if (obj[key]) { final[key] = obj[key]; } return final; }, {}); }; const isBoolean$1 = (v) => typeof v === 'boolean'; const isDefined = (v) => v !== null && v !== undefined; const isFunction = (v) => typeof v === 'function'; const isNumber$1 = (v) => typeof v === 'number'; const isObject$4 = (val) => val != null && typeof val === 'object' && Array.isArray(val) === false; const isString$1 = (v) => typeof v === 'string'; const isIterable = (v) => isDefined(v) && isFunction(v[Symbol.iterator]); const isPromise = (v) => !!v && (typeof v === 'object' || typeof v === 'function') && typeof v.then === 'function'; const isGlob = (str) => { const chars = { '{': '}', '(': ')', '[': ']' }; /* eslint-disable-next-line max-len */ const regex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; if (str === '') { return false; } let match; while ((match = regex.exec(str))) { if (match[2]) return true; let idx = match.index + match[0].length; // if an open bracket/brace/paren is escaped, // set the index to the next closing character const open = match[1]; const close = open ? chars[open] : null; if (open && close) { const n = str.indexOf(close, idx); if (n !== -1) { idx = n + 1; } } str = str.slice(idx); } return false; }; /** * Checks if the path is the OS root path, such as "/" or "C:\" */ const isRootPath = (p) => p === '/' || windowsPathRegex.test(p); // https://github.com/nodejs/node/blob/5883a59b21a97e8b7339f435c977155a2c29ba8d/lib/path.js#L43 const windowsPathRegex = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/]+[^\\/]+)?[\\/]$/; /** * Iterate through a series of diagnostics to provide minor fix-ups for various edge cases, deduplicate messages, etc. * @param compilerCtx the current compiler context * @param diagnostics the diagnostics to normalize * @returns the normalize documents */ const normalizeDiagnostics = (compilerCtx, diagnostics) => { const normalizedErrors = []; const normalizedOthers = []; const dups = new Set(); for (let i = 0; i < diagnostics.length; i++) { const d = normalizeDiagnostic(compilerCtx, diagnostics[i]); const key = d.absFilePath + d.code + d.messageText + d.type; if (dups.has(key)) { continue; } dups.add(key); const total = normalizedErrors.length + normalizedOthers.length; if (d.level === 'error') { normalizedErrors.push(d); } else if (total < MAX_ERRORS) { normalizedOthers.push(d); } } return [...normalizedErrors, ...normalizedOthers]; }; /** * Perform post-processing on a `Diagnostic` to handle a few message edge cases, massaging error message text and * updating build failure contexts * @param compilerCtx the current compiler * @param diagnostic the diagnostic to normalize * @returns the altered diagnostic */ const normalizeDiagnostic = (compilerCtx, diagnostic) => { if (diagnostic.messageText) { if (typeof diagnostic.messageText.message === 'string') { diagnostic.messageText = diagnostic.messageText.message; } else if (typeof diagnostic.messageText === 'string' && diagnostic.messageText.indexOf('Error: ') === 0) { diagnostic.messageText = diagnostic.messageText.substr(7); } } if (diagnostic.messageText) { if (diagnostic.messageText.includes(`Cannot find name 'h'`)) { diagnostic.header = `Missing "h" import for JSX types`; diagnostic.messageText = `In order to load accurate JSX types for components, the "h" function must be imported from "@stencil/core" by each component using JSX. For example: import { Component, h } from '@stencil/core';`; try { const sourceText = compilerCtx.fs.readFileSync(diagnostic.absFilePath); const srcLines = splitLineBreaks(sourceText); for (let i = 0; i < srcLines.length; i++) { const srcLine = srcLines[i]; if (srcLine.includes('@stencil/core')) { const msgLines = []; const beforeLineIndex = i - 1; if (beforeLineIndex > -1) { const beforeLine = { lineIndex: beforeLineIndex, lineNumber: beforeLineIndex + 1, text: srcLines[beforeLineIndex], errorCharStart: -1, errorLength: -1, }; msgLines.push(beforeLine); } const errorLine = { lineIndex: i, lineNumber: i + 1, text: srcLine, errorCharStart: 0, errorLength: -1, }; msgLines.push(errorLine); diagnostic.lineNumber = errorLine.lineNumber; diagnostic.columnNumber = srcLine.indexOf('}'); const afterLineIndex = i + 1; if (afterLineIndex < srcLines.length) { const afterLine = { lineIndex: afterLineIndex, lineNumber: afterLineIndex + 1, text: srcLines[afterLineIndex], errorCharStart: -1, errorLength: -1, }; msgLines.push(afterLine); } diagnostic.lines = msgLines; break; } } } catch (e) { } } } return diagnostic; }; /** * Split a corpus by newlines. Carriage returns are treated a newlines. * @param sourceText the corpus to split * @returns the split text */ const splitLineBreaks = (sourceText) => { if (typeof sourceText !== 'string') return []; sourceText = sourceText.replace(/\\r/g, '\n'); return sourceText.split('\n'); }; const escapeHtml = (unsafe) => { if (unsafe === undefined) return 'undefined'; if (unsafe === null) return 'null'; if (typeof unsafe !== 'string') { unsafe = unsafe.toString(); } return unsafe .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); }; const MAX_ERRORS = 25; /** * 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 template `Diagnostic` entity for a build warning. The created `Diagnostic` is returned, and have little * detail attached to it regarding the specifics of the warning - it is the responsibility of the caller of this method * to attach the specifics of the warning 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 buildWarn = (diagnostics) => { const diagnostic = { level: 'warn', type: 'build', header: 'Build Warn', messageText: 'build warn', relFilePath: null, absFilePath: null, lines: [], }; diagnostics.push(diagnostic); return diagnostic; }; const buildJsonFileError = (compilerCtx, diagnostics, jsonFilePath, msg, pkgKey) => { const err = buildError(diagnostics); err.messageText = msg; err.absFilePath = jsonFilePath; if (typeof pkgKey === 'string') { try { const jsonStr = compilerCtx.fs.readFileSync(jsonFilePath); const lines = jsonStr.replace(/\r/g, '\n').split('\n'); for (let i = 0; i < lines.length; i++) { const txtLine = lines[i]; const txtIndex = txtLine.indexOf(pkgKey); if (txtIndex > -1) { const warnLine = { lineIndex: i, lineNumber: i + 1, text: txtLine, errorCharStart: txtIndex, errorLength: pkgKey.length, }; err.lineNumber = warnLine.lineNumber; err.columnNumber = txtIndex + 1; err.lines.push(warnLine); if (i >= 0) { const beforeWarnLine = { lineIndex: warnLine.lineIndex - 1, lineNumber: warnLine.lineNumber - 1, text: lines[i - 1], errorCharStart: -1, errorLength: -1, }; err.lines.unshift(beforeWarnLine); } if (i < lines.length) { const afterWarnLine = { lineIndex: warnLine.lineIndex + 1, lineNumber: warnLine.lineNumber + 1, text: lines[i + 1], errorCharStart: -1, errorLength: -1, }; err.lines.push(afterWarnLine); } break; } } } catch (e) { } } return err; }; /** * 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$1(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'); }; /** * Determine if the provided diagnostics have any warnings * @param diagnostics the diagnostics to inspect * @returns true if any of the diagnostics in the list provided are warnings. false otherwise. */ const hasWarning = (diagnostics) => { if (diagnostics == null || diagnostics.length === 0) { return false; } return diagnostics.some((d) => d.level === 'warn'); }; const shouldIgnoreError = (msg) => { return msg === TASK_CANCELED_MSG; }; const TASK_CANCELED_MSG = `task canceled`; const loadRollupDiagnostics = (config, compilerCtx, buildCtx, rollupError) => { const formattedCode = formatErrorCode(rollupError.code); const diagnostic = { level: 'error', type: 'bundling', language: 'javascript', code: rollupError.code, header: `Rollup${formattedCode.length > 0 ? ': ' + formattedCode : ''}`, messageText: formattedCode, relFilePath: null, absFilePath: null, lines: [], }; if (config.logLevel === 'debug' && rollupError.stack) { diagnostic.messageText = rollupError.stack; } else if (rollupError.message) { diagnostic.messageText = rollupError.message; } if (rollupError.plugin) { diagnostic.messageText += ` (plugin: ${rollupError.plugin}${rollupError.hook ? `, ${rollupError.hook}` : ''})`; } const loc = rollupError.loc; if (loc != null) { const srcFile = loc.file || rollupError.id; if (isString$1(srcFile)) { try { const sourceText = compilerCtx.fs.readFileSync(srcFile); if (sourceText) { diagnostic.absFilePath = srcFile; try { const srcLines = splitLineBreaks(sourceText); const errorLine = { lineIndex: loc.line - 1, lineNumber: loc.line, text: srcLines[loc.line - 1], errorCharStart: loc.column, errorLength: 0, }; diagnostic.lineNumber = errorLine.lineNumber; diagnostic.columnNumber = errorLine.errorCharStart; const highlightLine = errorLine.text.substr(loc.column); for (let i = 0; i < highlightLine.length; i++) { if (charBreak.has(highlightLine.charAt(i))) { break; } errorLine.errorLength++; } diagnostic.lines.push(errorLine); if (errorLine.errorLength === 0 && errorLine.errorCharStart > 0) { errorLine.errorLength = 1; errorLine.errorCharStart--; } if (errorLine.lineIndex > 0) { const previousLine = { lineIndex: errorLine.lineIndex - 1, lineNumber: errorLine.lineNumber - 1, text: srcLines[errorLine.lineIndex - 1], errorCharStart: -1, errorLength: -1, }; diagnostic.lines.unshift(previousLine); } if (errorLine.lineIndex + 1 < srcLines.length) { const nextLine = { lineIndex: errorLine.lineIndex + 1, lineNumber: errorLine.lineNumber + 1, text: srcLines[errorLine.lineIndex + 1], errorCharStart: -1, errorLength: -1, }; diagnostic.lines.push(nextLine); } } catch (e) { diagnostic.messageText += `\nError parsing: ${diagnostic.absFilePath}, line: ${loc.line}, column: ${loc.column}`; diagnostic.debugText = sourceText; } } else if (typeof rollupError.frame === 'string') { diagnostic.messageText += '\n' + rollupError.frame; } } catch (e) { } } } buildCtx.diagnostics.push(diagnostic); }; const createOnWarnFn = (diagnostics, bundleModulesFiles) => { const previousWarns = new Set(); return function onWarningMessage(warning) { if (warning == null || ignoreWarnCodes.has(warning.code) || previousWarns.has(warning.message)) { return; } previousWarns.add(warning.message); let label = ''; if (bundleModulesFiles) { label = bundleModulesFiles .reduce((cmps, m) => { cmps.push(...m.cmps); return cmps; }, []) .join(', ') .trim(); if (label.length) { label += ': '; } } const diagnostic = buildWarn(diagnostics); diagnostic.header = `Bundling Warning ${warning.code}`; diagnostic.messageText = label + (warning.message || warning); }; }; const ignoreWarnCodes = new Set([ 'THIS_IS_UNDEFINED', 'NON_EXISTENT_EXPORT', 'CIRCULAR_DEPENDENCY', 'EMPTY_BUNDLE', 'UNUSED_EXTERNAL_IMPORT', ]); const charBreak = new Set([' ', '=', '.', ',', '?', ':', ';', '(', ')', '{', '}', '[', ']', '|', `'`, `"`, '`']); const formatErrorCode = (errorCode) => { if (typeof errorCode === 'string') { return errorCode .split('_') .map((c) => { return toTitleCase(c.toLowerCase()); }) .join(' '); } return (errorCode || '').trim(); }; /** * 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$1 = (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]; }; /** * Same as normalizePath(), expect it'll also strip any querystrings * from the path name. So /dir/file.css?tag=cmp-a becomes /dir/file.css */ const normalizeFsPath = (p) => normalizePath$1(p.split('?')[0].replace(/\0/g, '')); const normalizeFsPathQuery = (importPath) => { const pathParts = importPath.split('?'); const filePath = normalizePath$1(pathParts[0]); const ext = filePath.split('.').pop().toLowerCase(); const params = pathParts.length > 1 ? new URLSearchParams(pathParts[1]) : null; const format = params ? params.get('format') : null; return { filePath, ext, params, format, }; }; /** * Augment a `Diagnostic` with information from a `Node` in the AST to provide richer error information * @param d the diagnostic to augment * @param node the node to augment with additional information * @returns the augmented diagnostic */ const augmentDiagnosticWithNode = (d, node) => { if (!node) { return d; } const sourceFile = node.getSourceFile(); if (!sourceFile) { return d; } d.absFilePath = normalizePath$1(sourceFile.fileName); const sourceText = sourceFile.text; const srcLines = splitLineBreaks(sourceText); const start = node.getStart(); const end = node.getEnd(); const posStart = sourceFile.getLineAndCharacterOfPosition(start); const errorLine = { lineIndex: posStart.line, lineNumber: posStart.line + 1, text: srcLines[posStart.line], errorCharStart: posStart.character, errorLength: Math.max(end - start, 1), }; // store metadata for line number and character index where the error occurred d.lineNumber = errorLine.lineNumber; d.columnNumber = errorLine.errorCharStart + 1; d.lines.push(errorLine); if (errorLine.errorLength === 0 && errorLine.errorCharStart > 0) { errorLine.errorLength = 1; errorLine.errorCharStart--; } // if the error did not occur on the first line of the file, add metadata for the line of code preceding the line // where the error was detected to provide the user with additional context if (errorLine.lineIndex > 0) { const previousLine = { lineIndex: errorLine.lineIndex - 1, lineNumber: errorLine.lineNumber - 1, text: srcLines[errorLine.lineIndex - 1], errorCharStart: -1, errorLength: -1, }; d.lines.unshift(previousLine); } // if the error did not occur on the last line of the file, add metadata for the line of code following the line // where the error was detected to provide the user with additional context if (errorLine.lineIndex + 1 < srcLines.length) { const nextLine = { lineIndex: errorLine.lineIndex + 1, lineNumber: errorLine.lineNumber + 1, text: srcLines[errorLine.lineIndex + 1], errorCharStart: -1, errorLength: -1, }; d.lines.push(nextLine); } return d; }; /** * Ok, so formatting overkill, we know. But whatever, it makes for great * error reporting within a terminal. So, yeah, let's code it up, shall we? */ const loadTypeScriptDiagnostics = (tsDiagnostics) => { const diagnostics = []; const maxErrors = Math.min(tsDiagnostics.length, 50); for (let i = 0; i < maxErrors; i++) { diagnostics.push(loadTypeScriptDiagnostic(tsDiagnostics[i])); } return diagnostics; }; const loadTypeScriptDiagnostic = (tsDiagnostic) => { const d = { level: 'warn', type: 'typescript', language: 'typescript', header: 'TypeScript', code: tsDiagnostic.code.toString(), messageText: flattenDiagnosticMessageText(tsDiagnostic, tsDiagnostic.messageText), relFilePath: null, absFilePath: null, lines: [], }; if (tsDiagnostic.category === 1) { d.level = 'error'; } if (tsDiagnostic.file) { d.absFilePath = tsDiagnostic.file.fileName; const sourceText = tsDiagnostic.file.text; const srcLines = splitLineBreaks(sourceText); const posData = tsDiagnostic.file.getLineAndCharacterOfPosition(tsDiagnostic.start); const errorLine = { lineIndex: posData.line, lineNumber: posData.line + 1, text: srcLines[posData.line], errorCharStart: posData.character, errorLength: Math.max(tsDiagnostic.length, 1), }; d.lineNumber = errorLine.lineNumber; d.columnNumber = errorLine.errorCharStart + 1; d.lines.push(errorLine); if (errorLine.errorLength === 0 && errorLine.errorCharStart > 0) { errorLine.errorLength = 1; errorLine.errorCharStart--; } if (errorLine.lineIndex > 0) { const previousLine = { lineIndex: errorLine.lineIndex - 1, lineNumber: errorLine.lineNumber - 1, text: srcLines[errorLine.lineIndex - 1], errorCharStart: -1, errorLength: -1, }; d.lines.unshift(previousLine); } if (errorLine.lineIndex + 1 < srcLines.length) { const nextLine = { lineIndex: errorLine.lineIndex + 1, lineNumber: errorLine.lineNumber + 1, text: srcLines[errorLine.lineIndex + 1], errorCharStart: -1, errorLength: -1, }; d.lines.push(nextLine); } } return d; }; const flattenDiagnosticMessageText = (tsDiagnostic, diag) => { if (typeof diag === 'string') { return diag; } else if (diag === undefined) { return ''; } const ignoreCodes = []; const isStencilConfig = tsDiagnostic.file.fileName.includes('stencil.config'); if (isStencilConfig) { ignoreCodes.push(2322); } let result = ''; if (!ignoreCodes.includes(diag.code)) { result = diag.messageText; if (isIterable(diag.next)) { for (const kid of diag.next) { result += flattenDiagnosticMessageText(tsDiagnostic, kid); } } } if (isStencilConfig) { result = result.replace(`type 'StencilConfig'`, `Stencil Config`); result = result.replace(`Object literal may only specify known properties, but `, ``); result = result.replace(`Object literal may only specify known properties, and `, ``); } return result.trim(); }; const isRemoteUrl = (p) => { if (isString$1(p)) { p = p.toLowerCase(); return p.startsWith('https://') || p.startsWith('http://'); } return false; }; const createJsVarName = (fileName) => { if (isString$1(fileName)) { fileName = fileName.split('?')[0]; fileName = fileName.split('#')[0]; fileName = fileName.split('&')[0]; fileName = fileName.split('=')[0]; fileName = toDashCase(fileName); fileName = fileName.replace(/[|;$%@"<>()+,.{}_\!\/\\]/g, '-'); fileName = dashToPascalCase$1(fileName); if (fileName.length > 1) { fileName = fileName[0].toLowerCase() + fileName.substr(1); } else { fileName = fileName.toLowerCase(); } if (fileName.length > 0 && !isNaN(fileName[0])) { fileName = '_' + fileName; } } return fileName; }; const isDtsFile$1 = (filePath) => { const parts = filePath.toLowerCase().split('.'); if (parts.length > 2) { return parts[parts.length - 2] === 'd' && parts[parts.length - 1] === 'ts'; } return false; }; /** * Generate the preamble to be placed atop the main file of the build * @param config the Stencil configuration file * @return the generated preamble */ const generatePreamble = (config) => { const { preamble } = config; if (!preamble) { return ''; } // generate the body of the JSDoc-style comment const preambleComment = preamble.split('\n').map((l) => ` * ${l}`); preambleComment.unshift(`/*!`); preambleComme