@stencil/core 2.15.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/cli/index.cjs +157 -70
  2. package/cli/index.js +157 -70
  3. package/cli/package.json +1 -1
  4. package/compiler/package.json +1 -1
  5. package/compiler/stencil.js +719 -174
  6. package/compiler/stencil.min.js +2 -2
  7. package/dependencies.json +1 -1
  8. package/dev-server/client/index.js +3 -3
  9. package/dev-server/client/package.json +1 -1
  10. package/dev-server/client/test/hmr-util.spec.d.ts +1 -0
  11. package/dev-server/client/test/status.spec.d.ts +1 -0
  12. package/dev-server/connector.html +3 -3
  13. package/dev-server/index.js +1 -1
  14. package/dev-server/package.json +1 -1
  15. package/dev-server/server-process.js +3 -3
  16. package/internal/app-data/package.json +1 -1
  17. package/internal/client/css-shim.js +2 -2
  18. package/internal/client/dom.js +1 -1
  19. package/internal/client/index.js +11 -9
  20. package/internal/client/package.json +1 -1
  21. package/internal/client/patch-browser.js +1 -1
  22. package/internal/client/patch-esm.js +1 -1
  23. package/internal/client/polyfills/css-shim.js +1 -1
  24. package/internal/client/shadow-css.js +2 -3
  25. package/internal/hydrate/index.js +34 -29
  26. package/internal/hydrate/package.json +1 -1
  27. package/internal/hydrate/runner.js +1 -1
  28. package/internal/hydrate/shadow-css.js +9 -9
  29. package/internal/package.json +1 -1
  30. package/internal/stencil-private.d.ts +39 -3
  31. package/internal/stencil-public-compiler.d.ts +69 -3
  32. package/internal/stencil-public-docs.d.ts +3 -0
  33. package/internal/testing/index.js +34 -29
  34. package/internal/testing/package.json +1 -1
  35. package/internal/testing/shadow-css.js +9 -9
  36. package/mock-doc/index.cjs +18 -4
  37. package/mock-doc/index.d.ts +12 -2
  38. package/mock-doc/index.js +18 -4
  39. package/mock-doc/package.json +1 -1
  40. package/package.json +24 -17
  41. package/screenshot/package.json +1 -1
  42. package/sys/node/autoprefixer.js +1 -1
  43. package/sys/node/index.js +2046 -1338
  44. package/sys/node/node-fetch.js +1 -1
  45. package/sys/node/package.json +1 -1
  46. package/sys/node/worker.js +1 -1
  47. package/testing/index.js +8 -8
  48. package/testing/jest/test/jest-config.spec.d.ts +1 -0
  49. package/testing/jest/test/jest-preprocessor.spec.d.ts +1 -0
  50. package/testing/jest/test/jest-runner.spec.d.ts +1 -0
  51. package/testing/jest/test/jest-serializer.spec.d.ts +1 -0
  52. package/testing/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Stencil Compiler v2.15.0 | MIT Licensed | https://stenciljs.com
2
+ Stencil Compiler v2.16.0 | MIT Licensed | https://stenciljs.com
3
3
  */
4
4
  (function(exports) {
5
5
  'use strict';
@@ -872,7 +872,7 @@ const normalizeDiagnostic = (compilerCtx, diagnostic) => {
872
872
  diagnostic.messageText = diagnostic.messageText.message;
873
873
  }
874
874
  else if (typeof diagnostic.messageText === 'string' && diagnostic.messageText.indexOf('Error: ') === 0) {
875
- diagnostic.messageText = diagnostic.messageText.substr(7);
875
+ diagnostic.messageText = diagnostic.messageText.slice(7);
876
876
  }
877
877
  }
878
878
  if (diagnostic.messageText) {
@@ -1161,7 +1161,7 @@ const loadRollupDiagnostics = (config, compilerCtx, buildCtx, rollupError) => {
1161
1161
  };
1162
1162
  diagnostic.lineNumber = errorLine.lineNumber;
1163
1163
  diagnostic.columnNumber = errorLine.errorCharStart;
1164
- const highlightLine = errorLine.text.substr(loc.column);
1164
+ const highlightLine = errorLine.text.slice(loc.column);
1165
1165
  for (let i = 0; i < highlightLine.length; i++) {
1166
1166
  if (charBreak.has(highlightLine.charAt(i))) {
1167
1167
  break;
@@ -1588,7 +1588,7 @@ const createJsVarName = (fileName) => {
1588
1588
  fileName = fileName.replace(/[|;$%@"<>()+,.{}_\!\/\\]/g, '-');
1589
1589
  fileName = dashToPascalCase$1(fileName);
1590
1590
  if (fileName.length > 1) {
1591
- fileName = fileName[0].toLowerCase() + fileName.substr(1);
1591
+ fileName = fileName[0].toLowerCase() + fileName.slice(1);
1592
1592
  }
1593
1593
  else {
1594
1594
  fileName = fileName.toLowerCase();
@@ -1711,15 +1711,16 @@ const SKIP_DEPS = ['@stencil/core'];
1711
1711
  * @returns an error message if the tag has an invalid name, undefined if the tag name passes all checks
1712
1712
  */
1713
1713
  const validateComponentTag = (tag) => {
1714
+ // we want to check this first since we call some String.prototype methods below
1715
+ if (typeof tag !== 'string') {
1716
+ return `Tag "${tag}" must be a string type`;
1717
+ }
1714
1718
  if (tag !== tag.trim()) {
1715
1719
  return `Tag can not contain white spaces`;
1716
1720
  }
1717
1721
  if (tag !== tag.toLowerCase()) {
1718
1722
  return `Tag can not contain upper case characters`;
1719
1723
  }
1720
- if (typeof tag !== 'string') {
1721
- return `Tag "${tag}" must be a string type`;
1722
- }
1723
1724
  if (tag.length === 0) {
1724
1725
  return `Received empty tag value`;
1725
1726
  }
@@ -2235,10 +2236,36 @@ const createWebWorkerMainController = (sys, maxConcurrentWorkers) => {
2235
2236
 
2236
2237
  const COMMON_DIR_MODULE_EXTS = ['.tsx', '.ts', '.mjs', '.js', '.jsx', '.json', '.md'];
2237
2238
  const COMMON_DIR_FILENAMES = ['package.json', 'index.js', 'index.mjs'];
2239
+ /**
2240
+ * Determine if a stringified file path is a TypeScript declaration file based on the extension at the end of the path.
2241
+ * @param p the path to evaluate
2242
+ * @returns `true` if the path ends in `.d.ts` (case-sensitive), `false` otherwise.
2243
+ */
2238
2244
  const isDtsFile = (p) => p.endsWith('.d.ts');
2245
+ /**
2246
+ * Determine if a stringified file path is a TypeScript file based on the extension at the end of the path. This
2247
+ * function does _not_ consider type declaration files (`.d.ts` files) to be TypeScript files.
2248
+ * @param p the path to evaluate
2249
+ * @returns `true` if the path ends in `.ts` (case-sensitive) but does _not_ end in `.d.ts`, `false` otherwise.
2250
+ */
2239
2251
  const isTsFile = (p) => !isDtsFile(p) && p.endsWith('.ts');
2252
+ /**
2253
+ * Determine if a stringified file path is a TSX file based on the extension at the end of the path
2254
+ * @param p the path to evaluate
2255
+ * @returns `true` if the path ends in `.tsx` (case-sensitive), `false` otherwise.
2256
+ */
2240
2257
  const isTsxFile = (p) => p.endsWith('.tsx');
2258
+ /**
2259
+ * Determine if a stringified file path is a JSX file based on the extension at the end of the path
2260
+ * @param p the path to evaluate
2261
+ * @returns `true` if the path ends in `.jsx` (case-sensitive), `false` otherwise.
2262
+ */
2241
2263
  const isJsxFile = (p) => p.endsWith('.jsx');
2264
+ /**
2265
+ * Determine if a stringified file path is a JavaScript file based on the extension at the end of the path
2266
+ * @param p the path to evaluate
2267
+ * @returns `true` if the path ends in `.js` (case-sensitive), `false` otherwise.
2268
+ */
2242
2269
  const isJsFile = (p) => p.endsWith('.js');
2243
2270
  const isJsonFile = (p) => p.endsWith('.json');
2244
2271
  const getCommonDirName = (dirPath, fileName) => dirPath + '/' + fileName;
@@ -3973,16 +4000,16 @@ const createCustomResolverAsync = (sys, inMemoryFs, exts) => {
3973
4000
  };
3974
4001
  };
3975
4002
 
3976
- const buildId = '20220328165835';
4003
+ const buildId = '20220531163847';
3977
4004
  const minfyJsId = 'terser5.6.1_7';
3978
- const optimizeCssId = 'autoprefixer10.2.5_postcss8.2.8_7';
4005
+ const optimizeCssId = 'autoprefixer10.2.5_postcss8.2.13_7';
3979
4006
  const parse5Version = '6.0.1';
3980
4007
  const rollupVersion = '2.42.3';
3981
4008
  const sizzleVersion = '2.42.3';
3982
4009
  const terserVersion = '5.6.1';
3983
4010
  const typescriptVersion = '4.5.4';
3984
- const vermoji = '';
3985
- const version$3 = '2.15.0';
4011
+ const vermoji = '🎉';
4012
+ const version$3 = '2.16.0';
3986
4013
  const versions = {
3987
4014
  stencil: version$3,
3988
4015
  parse5: parse5Version,
@@ -4442,7 +4469,7 @@ const createSystem = (c) => {
4442
4469
  const hashArray = Array.from(new Uint8Array(arrayBuffer)); // convert buffer to byte array
4443
4470
  let hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
4444
4471
  if (typeof hashLength === 'number') {
4445
- hashHex = hashHex.substr(0, hashLength);
4472
+ hashHex = hashHex.slice(0, hashLength);
4446
4473
  }
4447
4474
  return hashHex;
4448
4475
  };
@@ -5067,13 +5094,13 @@ const getCssSelectors = (sel) => {
5067
5094
  if (items[i].length === 0)
5068
5095
  continue;
5069
5096
  if (items[i].charAt(0) === '.') {
5070
- SELECTORS.classNames.push(items[i].substr(1));
5097
+ SELECTORS.classNames.push(items[i].slice(1));
5071
5098
  }
5072
5099
  else if (items[i].charAt(0) === '#') {
5073
- SELECTORS.ids.push(items[i].substr(1));
5100
+ SELECTORS.ids.push(items[i].slice(1));
5074
5101
  }
5075
5102
  else if (items[i].charAt(0) === '[') {
5076
- items[i] = items[i].substr(1).split('=')[0].split(']')[0].trim();
5103
+ items[i] = items[i].slice(1).split('=')[0].split(']')[0].trim();
5077
5104
  SELECTORS.attrs.push(items[i].toLowerCase());
5078
5105
  }
5079
5106
  else if (/[a-z]/g.test(items[i].charAt(0))) {
@@ -8662,7 +8689,7 @@ const loadMinifyJsDiagnostics = (sourceText, diagnostics, error) => {
8662
8689
  };
8663
8690
  d.lineNumber = errorLine.lineNumber;
8664
8691
  d.columnNumber = errorLine.errorCharStart;
8665
- const highlightLine = errorLine.text.substr(d.columnNumber);
8692
+ const highlightLine = errorLine.text.slice(d.columnNumber);
8666
8693
  for (let i = 0; i < highlightLine.length; i++) {
8667
8694
  if (MINIFY_CHAR_BREAK.has(highlightLine.charAt(i))) {
8668
8695
  break;
@@ -9516,7 +9543,7 @@ const standardNormalizeHref = (prerenderConfig, diagnostics, url) => {
9516
9543
  // url should NOT have a trailing slash
9517
9544
  if (href.endsWith('/') && url.pathname !== '/') {
9518
9545
  // this has a trailing slash and it's not the root path
9519
- href = href.substr(0, href.length - 1);
9546
+ href = href.slice(0, -1);
9520
9547
  }
9521
9548
  }
9522
9549
  return href;
@@ -10999,7 +11026,6 @@ const CSS_PROP_ANNOTATION = `@prop`;
10999
11026
  const safeSelector = (selector) => {
11000
11027
  const placeholders = [];
11001
11028
  let index = 0;
11002
- let content;
11003
11029
  // Replaces attribute selectors with placeholders.
11004
11030
  // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.
11005
11031
  selector = selector.replace(/(\[[^\]]*\])/g, (_, keep) => {
@@ -11010,7 +11036,7 @@ const safeSelector = (selector) => {
11010
11036
  });
11011
11037
  // Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
11012
11038
  // WS and "+" would otherwise be interpreted as selector separators.
11013
- content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
11039
+ const content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
11014
11040
  const replaceBy = `__ph-${index}__`;
11015
11041
  placeholders.push(exp);
11016
11042
  index++;
@@ -12031,25 +12057,33 @@ const isOutputTargetWww = (o) => o.type === WWW;
12031
12057
  const isOutputTargetStats = (o) => o.type === STATS;
12032
12058
  const isOutputTargetDistTypes = (o) => o.type === DIST_TYPES;
12033
12059
  const getComponentsFromModules = (moduleFiles) => sortBy(flatOne(moduleFiles.map((m) => m.cmps)), (c) => c.tagName);
12034
- const ANGULAR = `angular`;
12060
+ const ANGULAR = 'angular';
12035
12061
  const COPY = 'copy';
12036
- const CUSTOM = `custom`;
12037
- const DIST = `dist`;
12038
- const DIST_COLLECTION = `dist-collection`;
12039
- const DIST_CUSTOM_ELEMENTS = `dist-custom-elements`;
12040
- const DIST_CUSTOM_ELEMENTS_BUNDLE = `dist-custom-elements-bundle`;
12041
- const DIST_TYPES = `dist-types`;
12042
- const DIST_HYDRATE_SCRIPT = `dist-hydrate-script`;
12043
- const DIST_LAZY = `dist-lazy`;
12044
- const DIST_LAZY_LOADER = `dist-lazy-loader`;
12062
+ const CUSTOM = 'custom';
12063
+ const DIST = 'dist';
12064
+ const DIST_COLLECTION = 'dist-collection';
12065
+ const DIST_CUSTOM_ELEMENTS = 'dist-custom-elements';
12066
+ const DIST_CUSTOM_ELEMENTS_BUNDLE = 'dist-custom-elements-bundle';
12067
+ const DIST_TYPES = 'dist-types';
12068
+ const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
12069
+ const DIST_LAZY = 'dist-lazy';
12070
+ const DIST_LAZY_LOADER = 'dist-lazy-loader';
12045
12071
  const DIST_GLOBAL_STYLES = 'dist-global-styles';
12046
12072
  const DOCS_CUSTOM = 'docs-custom';
12047
- const DOCS_JSON = `docs-json`;
12048
- const DOCS_README = `docs-readme`;
12049
- const DOCS_VSCODE = `docs-vscode`;
12050
- const STATS = `stats`;
12051
- const WWW = `www`;
12052
- const VALID_TYPES = [
12073
+ const DOCS_JSON = 'docs-json';
12074
+ const DOCS_README = 'docs-readme';
12075
+ const DOCS_VSCODE = 'docs-vscode';
12076
+ const STATS = 'stats';
12077
+ const WWW = 'www';
12078
+ /**
12079
+ * Valid output targets to specify in a Stencil config.
12080
+ *
12081
+ * Note that there are some output targets (e.g. `DIST_TYPES`) which are
12082
+ * programmatically set as output targets by the compiler when other output
12083
+ * targets (in that case `DIST`) are set, but which are _not_ supported in a
12084
+ * Stencil config. This is enforced in the output target validation code.
12085
+ */
12086
+ const VALID_CONFIG_OUTPUT_TARGETS = [
12053
12087
  // DIST
12054
12088
  WWW,
12055
12089
  DIST,
@@ -12069,6 +12103,20 @@ const VALID_TYPES = [
12069
12103
  CUSTOM,
12070
12104
  STATS,
12071
12105
  ];
12106
+ /**
12107
+ * Check whether a given output target is a valid one to be set in a Stencil config
12108
+ *
12109
+ * @param targetType the type which we want to check
12110
+ * @returns whether or not the targetType is a valid, configurable output target.
12111
+ */
12112
+ function isValidConfigOutputTarget(targetType) {
12113
+ // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
12114
+ // boolean` so a `string` cannot be passed to `includes` on a
12115
+ // `ReadonlyArray` 😢 thus we `as any`
12116
+ //
12117
+ // see microsoft/TypeScript#31018 for some discussion of this
12118
+ return VALID_CONFIG_OUTPUT_TARGETS.includes(targetType);
12119
+ }
12072
12120
  const GENERATED_DTS$1 = 'components.d.ts';
12073
12121
 
12074
12122
  var concatMap$1 = function (xs, fn) {
@@ -13674,6 +13722,7 @@ const generateBuildResults = (config, compilerCtx, buildCtx) => {
13674
13722
  * @returns CompilerBuildStats or an Object including diagnostics.
13675
13723
  */
13676
13724
  function generateBuildStats(config, buildCtx) {
13725
+ // TODO(STENCIL-461): Investigate making this return only a single type
13677
13726
  const buildResults = buildCtx.buildResults;
13678
13727
  let jsonData;
13679
13728
  try {
@@ -13766,7 +13815,7 @@ function sanitizeBundlesForStats(bundleArray) {
13766
13815
  });
13767
13816
  }
13768
13817
  function getSourceGraph(config, buildCtx) {
13769
- let sourceGraph = {};
13818
+ const sourceGraph = {};
13770
13819
  sortBy(buildCtx.moduleFiles, (m) => m.sourceFilePath).forEach((moduleFile) => {
13771
13820
  const key = relativePath$1(config, moduleFile.sourceFilePath);
13772
13821
  sourceGraph[key] = moduleFile.localImports.map((localImport) => relativePath$1(config, localImport)).sort();
@@ -14367,7 +14416,7 @@ function toDataAttribute(str) {
14367
14416
  .toLowerCase());
14368
14417
  }
14369
14418
  function dashToPascalCase(str) {
14370
- str = String(str).substr(5);
14419
+ str = String(str).slice(5);
14371
14420
  return str
14372
14421
  .split('-')
14373
14422
  .map((segment, index) => {
@@ -14571,7 +14620,7 @@ function cssCaseToJsCase(str) {
14571
14620
  .split('-')
14572
14621
  .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
14573
14622
  .join('');
14574
- str = str.substr(0, 1).toLowerCase() + str.substr(1);
14623
+ str = str.slice(0, 1).toLowerCase() + str.slice(1);
14575
14624
  }
14576
14625
  return str;
14577
14626
  }
@@ -15514,7 +15563,11 @@ class MockNode {
15514
15563
  if (otherNode === this) {
15515
15564
  return true;
15516
15565
  }
15517
- return this.childNodes.includes(otherNode);
15566
+ const childNodes = Array.from(this.childNodes);
15567
+ if (childNodes.includes(otherNode)) {
15568
+ return true;
15569
+ }
15570
+ return childNodes.some((node) => this.contains.bind(node)(otherNode));
15518
15571
  }
15519
15572
  removeChild(childNode) {
15520
15573
  const index = this.childNodes.indexOf(childNode);
@@ -16924,6 +16977,17 @@ MockElement.prototype.cloneNode = function (deep) {
16924
16977
  return cloned;
16925
16978
  };
16926
16979
 
16980
+ let sharedDocument;
16981
+ function parseHtmlToDocument(html, ownerDocument = null) {
16982
+ if (ownerDocument == null) {
16983
+ if (sharedDocument == null) {
16984
+ sharedDocument = new MockDocument();
16985
+ }
16986
+ ownerDocument = sharedDocument;
16987
+ }
16988
+ return parseDocumentUtil(ownerDocument, html);
16989
+ }
16990
+
16927
16991
  class MockHeaders {
16928
16992
  constructor(init) {
16929
16993
  this._values = [];
@@ -17131,6 +17195,15 @@ class MockResponse {
17131
17195
  }
17132
17196
  }
17133
17197
 
17198
+ class MockDOMParser {
17199
+ parseFromString(htmlToParse, mimeType) {
17200
+ if (mimeType !== 'text/html') {
17201
+ console.error('XML parsing not implemented yet, continuing as html');
17202
+ }
17203
+ return parseHtmlToDocument(htmlToParse);
17204
+ }
17205
+ }
17206
+
17134
17207
  function addGlobalsToWindowPrototype(mockWinPrototype) {
17135
17208
  GLOBAL_CONSTRUCTORS.forEach(([cstrName, Cstr]) => {
17136
17209
  Object.defineProperty(mockWinPrototype, cstrName, {
@@ -17153,6 +17226,7 @@ const GLOBAL_CONSTRUCTORS = [
17153
17226
  ['MouseEvent', MockMouseEvent],
17154
17227
  ['Request', MockRequest],
17155
17228
  ['Response', MockResponse],
17229
+ ['DOMParser', MockDOMParser],
17156
17230
  ['HTMLAnchorElement', MockAnchorElement],
17157
17231
  ['HTMLBaseElement', MockBaseElement],
17158
17232
  ['HTMLButtonElement', MockButtonElement],
@@ -40751,7 +40825,7 @@ const parseDevModuleUrl = (config, u) => {
40751
40825
  const url = new URL(u, 'https://stenciljs.com');
40752
40826
  let reqPath = basename(url.pathname);
40753
40827
  reqPath = reqPath.substring(0, reqPath.length - 3);
40754
- let splt = reqPath.split('@');
40828
+ const splt = reqPath.split('@');
40755
40829
  if (splt.length === 2) {
40756
40830
  parsedUrl.nodeModuleId = decodeURIComponent(splt[0]);
40757
40831
  parsedUrl.nodeModuleVersion = decodeURIComponent(splt[1]);
@@ -42636,7 +42710,14 @@ const parse$1 = (input, options) => {
42636
42710
  }
42637
42711
 
42638
42712
  if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
42639
- output = token.close = `)${rest})${extglobStar})`;
42713
+ // Any non-magical string (`.ts`) or even nested expression (`.{ts,tsx}`) can follow after the closing parenthesis.
42714
+ // In this case, we need to parse the string and use it in the output of the original pattern.
42715
+ // Suitable patterns: `/!(*.d).ts`, `/!(*.d).{ts,tsx}`, `**/!(*-dbg).@(js)`.
42716
+ //
42717
+ // Disabling the `fastpaths` option due to a problem with parsing strings as `.ts` in the pattern like `**/!(*.d).ts`.
42718
+ const expression = parse$1(rest, { ...options, fastpaths: false }).output;
42719
+
42720
+ output = token.close = `)${expression})${extglobStar})`;
42640
42721
  }
42641
42722
 
42642
42723
  if (token.prev.type === 'bos') {
@@ -54300,31 +54381,55 @@ const getTextOfPropertyName = (propName) => {
54300
54381
  }
54301
54382
  return undefined;
54302
54383
  };
54384
+ /**
54385
+ * Generate a series of type references for a given AST node
54386
+ * @param baseNode the AST node to pull type references from
54387
+ * @param sourceFile the source file in which the provided `baseNode` exists
54388
+ * @returns the generated series of type references
54389
+ */
54303
54390
  const getAttributeTypeInfo = (baseNode, sourceFile) => {
54304
54391
  const allReferences = {};
54305
- getAllTypeReferences(baseNode).forEach((rt) => {
54306
- allReferences[rt] = getTypeReferenceLocation(rt, sourceFile);
54392
+ getAllTypeReferences(baseNode).forEach((typeName) => {
54393
+ allReferences[typeName] = getTypeReferenceLocation(typeName, sourceFile);
54307
54394
  });
54308
54395
  return allReferences;
54309
54396
  };
54397
+ /**
54398
+ * Get the text-based name from a TypeScript `EntityName`, which is an identifier of some form
54399
+ * @param entity a TypeScript `EntityName` to retrieve the name of an entity from
54400
+ * @returns the entity's name
54401
+ */
54310
54402
  const getEntityName = (entity) => {
54311
54403
  if (t.isIdentifier(entity)) {
54312
54404
  return entity.escapedText.toString();
54313
54405
  }
54314
54406
  else {
54407
+ // We have qualified name - e.g. const x: Foo.Bar.Baz;
54408
+ // Recurse until we find the 'base' of the qualified name
54315
54409
  return getEntityName(entity.left);
54316
54410
  }
54317
54411
  };
54412
+ /**
54413
+ * Recursively walks the provided AST to collect all TypeScript type references that are found
54414
+ * @param node the node to walk to retrieve type information
54415
+ * @returns the collected type references
54416
+ */
54318
54417
  const getAllTypeReferences = (node) => {
54319
54418
  const referencedTypes = [];
54320
54419
  const visit = (node) => {
54420
+ /**
54421
+ * A type reference node will refer to some type T.
54422
+ * e.g: In `const foo: Bar = {...}` the reference node will contain semantic information about `Bar`.
54423
+ * In TypeScript, types that are also keywords (e.g. `number` in `const foo: number`) are not `TypeReferenceNode`s.
54424
+ */
54321
54425
  if (t.isTypeReferenceNode(node)) {
54322
54426
  referencedTypes.push(getEntityName(node.typeName));
54323
54427
  if (node.typeArguments) {
54428
+ // a type may contain types itself (e.g. generics - Foo<Bar>)
54324
54429
  node.typeArguments
54325
- .filter((ta) => t.isTypeReferenceNode(ta))
54326
- .forEach((tr) => {
54327
- const typeName = tr.typeName;
54430
+ .filter((typeArg) => t.isTypeReferenceNode(typeArg))
54431
+ .forEach((typeRef) => {
54432
+ const typeName = typeRef.typeName;
54328
54433
  if (typeName && typeName.escapedText) {
54329
54434
  referencedTypes.push(typeName.escapedText.toString());
54330
54435
  }
@@ -54345,6 +54450,22 @@ const validateReferences = (diagnostics, references, node) => {
54345
54450
  }
54346
54451
  });
54347
54452
  };
54453
+ /**
54454
+ * Determine where a TypeScript type reference originates from. This is accomplished by interrogating the AST node in
54455
+ * which the type's name appears
54456
+ *
54457
+ * A type may originate:
54458
+ * - from the same file where it is used (a type is declared in some file, `foo.ts`, and later used in the same file)
54459
+ * - from another file (I.E. it is imported and should have an import statement somewhere in the file)
54460
+ * - from a global context
54461
+ * - etc.
54462
+ *
54463
+ * The type may be declared using the `type` or `interface` keywords.
54464
+ *
54465
+ * @param typeName the name of the type to find the origination of
54466
+ * @param tsNode the TypeScript AST node being searched for the provided `typeName`
54467
+ * @returns the context stating where the type originates from
54468
+ */
54348
54469
  const getTypeReferenceLocation = (typeName, tsNode) => {
54349
54470
  const sourceFileObj = tsNode.getSourceFile();
54350
54471
  // Loop through all top level imports to find any reference to the type for 'import' reference location
@@ -55352,9 +55473,9 @@ const proxyCustomElement = (compilerCtx, transformOpts) => {
55352
55473
  return tsSourceFile;
55353
55474
  }
55354
55475
  const principalComponent = moduleFile.cmps[0];
55355
- for (let [stmtIndex, stmt] of tsSourceFile.statements.entries()) {
55476
+ for (const [stmtIndex, stmt] of tsSourceFile.statements.entries()) {
55356
55477
  if (t.isVariableStatement(stmt)) {
55357
- for (let [declarationIndex, declaration] of stmt.declarationList.declarations.entries()) {
55478
+ for (const [declarationIndex, declaration] of stmt.declarationList.declarations.entries()) {
55358
55479
  if (declaration.name.getText() !== principalComponent.componentClassName) {
55359
55480
  continue;
55360
55481
  }
@@ -56519,6 +56640,20 @@ const strickCheckDocs = (config, docsData) => {
56519
56640
  });
56520
56641
  };
56521
56642
 
56643
+ /**
56644
+ * Generate [custom data](https://github.com/microsoft/vscode-custom-data) to augment existing HTML types in VS Code.
56645
+ * This function writes the custom data as a JSON file to disk, which can be used in VS Code to inform the IDE about
56646
+ * custom elements generated by Stencil.
56647
+ *
56648
+ * The JSON generated by this function must conform to the
56649
+ * [HTML custom data schema](https://github.com/microsoft/vscode-html-languageservice/blob/e7ae8a7170df5e721a13cee1b86e293b24eb3b20/docs/customData.schema.json).
56650
+ *
56651
+ * This function generates custom data for HTML only at this time (it does not generate custom data for CSS).
56652
+ *
56653
+ * @param compilerCtx the current compiler context
56654
+ * @param docsData an intermediate representation documentation derived from compiled Stencil components
56655
+ * @param outputTargets the output target(s) the associated with the current build
56656
+ */
56522
56657
  const generateVscodeDocs = async (compilerCtx, docsData, outputTargets) => {
56523
56658
  const vsCodeOutputTargets = outputTargets.filter(isOutputTargetDocsVscode);
56524
56659
  if (vsCodeOutputTargets.length === 0) {
@@ -56526,6 +56661,13 @@ const generateVscodeDocs = async (compilerCtx, docsData, outputTargets) => {
56526
56661
  }
56527
56662
  await Promise.all(vsCodeOutputTargets.map(async (outputTarget) => {
56528
56663
  const json = {
56664
+ /**
56665
+ * the 'version' top-level field is required by the schema. changes to the JSON generated by Stencil must:
56666
+ * - comply with v1.X of the schema _OR_
56667
+ * - increment this field as a part of updating the JSON generation. This should be considered a breaking change
56668
+ *
56669
+ * {@link https://github.com/microsoft/vscode-html-languageservice/blob/e7ae8a7170df5e721a13cee1b86e293b24eb3b20/src/htmlLanguageTypes.ts#L184}
56670
+ */
56529
56671
  version: 1.1,
56530
56672
  tags: docsData.components.map((cmp) => ({
56531
56673
  name: cmp.tag,
@@ -56533,20 +56675,31 @@ const generateVscodeDocs = async (compilerCtx, docsData, outputTargets) => {
56533
56675
  kind: 'markdown',
56534
56676
  value: cmp.docs,
56535
56677
  },
56536
- attributes: cmp.props.filter((p) => p.attr).map(serializeAttribute),
56678
+ attributes: cmp.props
56679
+ .filter((p) => p.attr !== undefined && p.attr.length > 0)
56680
+ .map(serializeAttribute),
56537
56681
  references: getReferences(cmp, outputTarget.sourceCodeBaseUrl),
56538
56682
  })),
56539
56683
  };
56684
+ // fields in the custom data may have a value of `undefined`. calling `stringify` will remove such fields.
56540
56685
  const jsonContent = JSON.stringify(json, null, 2);
56541
56686
  await compilerCtx.fs.writeFile(outputTarget.file, jsonContent);
56542
56687
  }));
56543
56688
  };
56689
+ /**
56690
+ * Generate a 'references' section for a component's documentation.
56691
+ * @param cmp the Stencil component to generate a references section for
56692
+ * @param repoBaseUrl an optional URL, that when provided, will add a reference to the source code for the component
56693
+ * @returns the generated references section, or undefined if no references could be generated
56694
+ */
56544
56695
  const getReferences = (cmp, repoBaseUrl) => {
56696
+ var _a;
56697
+ // collect any `@reference` JSDoc tags on the component
56545
56698
  const references = getNameText('reference', cmp.docsTags).map(([name, url]) => ({ name, url }));
56546
56699
  if (repoBaseUrl) {
56547
56700
  references.push({
56548
56701
  name: 'Source code',
56549
- url: join(repoBaseUrl, cmp.filePath),
56702
+ url: join(repoBaseUrl, (_a = cmp.filePath) !== null && _a !== void 0 ? _a : ''),
56550
56703
  });
56551
56704
  }
56552
56705
  if (references.length > 0) {
@@ -56554,14 +56707,19 @@ const getReferences = (cmp, repoBaseUrl) => {
56554
56707
  }
56555
56708
  return undefined;
56556
56709
  };
56710
+ /**
56711
+ * Serialize a component's class member decorated with `@Prop` to be written to disk
56712
+ * @param prop the intermediate representation of the documentation to serialize
56713
+ * @returns the serialized data
56714
+ */
56557
56715
  const serializeAttribute = (prop) => {
56558
56716
  const attribute = {
56559
56717
  name: prop.attr,
56560
56718
  description: prop.docs,
56561
56719
  };
56562
56720
  const values = prop.values
56563
- .filter(({ type, value }) => type === 'string' && value !== undefined)
56564
- .map(({ value }) => ({ name: value }));
56721
+ .filter((jsonDocValue) => jsonDocValue.type === 'string' && jsonDocValue.value !== undefined)
56722
+ .map((jsonDocValue) => ({ name: jsonDocValue.value }));
56565
56723
  if (values.length > 0) {
56566
56724
  attribute.values = values;
56567
56725
  }
@@ -57372,6 +57530,7 @@ const getBundleId = async (config, entryKey, shouldHash, code, sufix) => {
57372
57530
  };
57373
57531
 
57374
57532
  const generateLazyModules = async (config, compilerCtx, buildCtx, outputTargetType, destinations, results, sourceTarget, isBrowserBuild, sufix) => {
57533
+ var _a;
57375
57534
  if (!Array.isArray(destinations) || destinations.length === 0) {
57376
57535
  return [];
57377
57536
  }
@@ -57379,14 +57538,15 @@ const generateLazyModules = async (config, compilerCtx, buildCtx, outputTargetTy
57379
57538
  const rollupResults = results.filter((r) => r.type === 'chunk');
57380
57539
  const entryComponentsResults = rollupResults.filter((rollupResult) => rollupResult.isComponent);
57381
57540
  const chunkResults = rollupResults.filter((rollupResult) => !rollupResult.isComponent && !rollupResult.isEntry);
57382
- const [bundleModules] = await Promise.all([
57383
- Promise.all(entryComponentsResults.map((rollupResult) => {
57384
- return generateLazyEntryModule(config, compilerCtx, buildCtx, rollupResult, outputTargetType, destinations, sourceTarget, shouldMinify, isBrowserBuild, sufix);
57385
- })),
57386
- Promise.all(chunkResults.map((rollupResult) => {
57387
- return writeLazyChunk(config, compilerCtx, buildCtx, rollupResult, outputTargetType, destinations, sourceTarget, shouldMinify, isBrowserBuild);
57388
- })),
57389
- ]);
57541
+ const bundleModules = await Promise.all(entryComponentsResults.map((rollupResult) => {
57542
+ return generateLazyEntryModule(config, compilerCtx, buildCtx, rollupResult, outputTargetType, destinations, sourceTarget, shouldMinify, isBrowserBuild, sufix);
57543
+ }));
57544
+ if (!!((_a = config.extras) === null || _a === void 0 ? void 0 : _a.experimentalImportInjection) && !isBrowserBuild) {
57545
+ addStaticImports(rollupResults, bundleModules);
57546
+ }
57547
+ await Promise.all(chunkResults.map((rollupResult) => {
57548
+ return writeLazyChunk(config, compilerCtx, buildCtx, rollupResult, outputTargetType, destinations, sourceTarget, shouldMinify, isBrowserBuild);
57549
+ }));
57390
57550
  const lazyRuntimeData = formatLazyBundlesRuntimeMeta(bundleModules);
57391
57551
  const entryResults = rollupResults.filter((rollupResult) => !rollupResult.isComponent && rollupResult.isEntry);
57392
57552
  await Promise.all(entryResults.map((rollupResult) => {
@@ -57401,6 +57561,74 @@ const generateLazyModules = async (config, compilerCtx, buildCtx, outputTargetTy
57401
57561
  }));
57402
57562
  return bundleModules;
57403
57563
  };
57564
+ /**
57565
+ * Add imports for each bundle to Stencil's lazy loader. Some bundlers that are built atop of Rollup strictly impose
57566
+ * the limitations that are laid out in https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations.
57567
+ * This function injects an explicit import statement for each bundle that can be lazily loaded.
57568
+ * @param rollupChunkResults the results of running Rollup across a Stencil project
57569
+ * @param bundleModules lazy-loadable modules that can be resolved at runtime
57570
+ */
57571
+ const addStaticImports = (rollupChunkResults, bundleModules) => {
57572
+ rollupChunkResults.filter(isStencilCoreResult).forEach((index) => {
57573
+ const generateCjs = isCjsFormat(index) ? generateCaseClauseCjs : generateCaseClause;
57574
+ index.code = index.code.replace('/*!__STENCIL_STATIC_IMPORT_SWITCH__*/', `
57575
+ if (!hmrVersionId || !BUILD.hotModuleReplacement) {
57576
+ const processMod = importedModule => {
57577
+ cmpModules.set(bundleId, importedModule);
57578
+ return importedModule[exportName];
57579
+ }
57580
+ switch(bundleId) {
57581
+ ${bundleModules.map((mod) => generateCjs(mod.output.bundleId)).join('')}
57582
+ }
57583
+ }`);
57584
+ });
57585
+ };
57586
+ /**
57587
+ * Determine if a Rollup output chunk contains Stencil runtime code
57588
+ * @param rollupChunkResult the rollup chunk output to test
57589
+ * @returns true if the output chunk contains Stencil runtime code, false otherwise
57590
+ */
57591
+ const isStencilCoreResult = (rollupChunkResult) => {
57592
+ return (rollupChunkResult.isCore &&
57593
+ rollupChunkResult.entryKey === 'index' &&
57594
+ (rollupChunkResult.moduleFormat === 'es' ||
57595
+ rollupChunkResult.moduleFormat === 'esm' ||
57596
+ isCjsFormat(rollupChunkResult)));
57597
+ };
57598
+ /**
57599
+ * Helper function to determine if a Rollup chunk has a commonjs module format
57600
+ * @param rollupChunkResult the Rollup result to test
57601
+ * @returns true if the Rollup chunk has a commonjs module format, false otherwise
57602
+ */
57603
+ const isCjsFormat = (rollupChunkResult) => {
57604
+ return rollupChunkResult.moduleFormat === 'cjs' || rollupChunkResult.moduleFormat === 'commonjs';
57605
+ };
57606
+ /**
57607
+ * Generate a 'case' clause to be used within a `switch` statement. The case clause generated will key-off the provided
57608
+ * bundle ID for a component, and load a file (tied to that ID) at runtime.
57609
+ * @param bundleId the name of the bundle to load
57610
+ * @returns the case clause that will load the component's file at runtime
57611
+ */
57612
+ const generateCaseClause = (bundleId) => {
57613
+ return `
57614
+ case '${bundleId}':
57615
+ return import(
57616
+ /* webpackMode: "lazy" */
57617
+ './${bundleId}.entry.js').then(processMod, consoleError);`;
57618
+ };
57619
+ /**
57620
+ * Generate a 'case' clause to be used within a `switch` statement. The case clause generated will key-off the provided
57621
+ * bundle ID for a component, and load a CommonJS file (tied to that ID) at runtime.
57622
+ * @param bundleId the name of the bundle to load
57623
+ * @returns the case clause that will load the component's file at runtime
57624
+ */
57625
+ const generateCaseClauseCjs = (bundleId) => {
57626
+ return `
57627
+ case '${bundleId}':
57628
+ return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(
57629
+ /* webpackMode: "lazy" */
57630
+ './${bundleId}.entry.js')); }).then(processMod, consoleError);`;
57631
+ };
57404
57632
  const generateLazyEntryModule = async (config, compilerCtx, buildCtx, rollupResult, outputTargetType, destinations, sourceTarget, shouldMinify, isBrowserBuild, sufix) => {
57405
57633
  const entryModule = buildCtx.entryModules.find((entryModule) => entryModule.entryKey === rollupResult.entryKey);
57406
57634
  const shouldHash = config.hashFileNames && isBrowserBuild;
@@ -57429,7 +57657,7 @@ const writeLazyEntry = async (config, compilerCtx, buildCtx, rollupResult, outpu
57429
57657
  if (isBrowserBuild && ['loader'].includes(rollupResult.entryKey)) {
57430
57658
  return;
57431
57659
  }
57432
- let inputCode = rollupResult.code.replace(`[/*!__STENCIL_LAZY_DATA__*/]`, `${lazyRuntimeData}`);
57660
+ const inputCode = rollupResult.code.replace(`[/*!__STENCIL_LAZY_DATA__*/]`, `${lazyRuntimeData}`);
57433
57661
  const { code, sourceMap } = await convertChunk(config, compilerCtx, buildCtx, sourceTarget, shouldMinify, false, isBrowserBuild, inputCode, rollupResult.map);
57434
57662
  await Promise.all(destinations.map((dst) => {
57435
57663
  const filePath = join(dst, rollupResult.fileName);
@@ -57447,7 +57675,7 @@ const formatLazyBundlesRuntimeMeta = (bundleModules) => {
57447
57675
  return stringifyRuntimeData(lazyBundles);
57448
57676
  };
57449
57677
  const formatLazyRuntimeBundle = (bundleModule) => {
57450
- let bundleId = bundleModule.output.bundleId;
57678
+ const bundleId = bundleModule.output.bundleId;
57451
57679
  const bundleCmps = bundleModule.cmps.slice().sort(sortBundleComponents);
57452
57680
  return [bundleId, bundleCmps.map((cmp) => formatComponentRuntimeMeta(cmp, true))];
57453
57681
  };
@@ -58867,11 +59095,73 @@ const updateStencilTypesImports = (typesDir, dtsFilePath, dtsContent) => {
58867
59095
  }
58868
59096
  return dtsContent;
58869
59097
  };
59098
+ /**
59099
+ * Utility for ensuring that naming collisions do not appear in type declaration files for a component's class members
59100
+ * decorated with @Prop, @Event, and @Method
59101
+ * @param typeReferences all type names used by a component class member
59102
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
59103
+ * @param sourceFilePath the path to the source file of a component using the type being inspected
59104
+ * @param initialType the name of the type that may be updated
59105
+ * @returns the updated type name, which may be the same as the initial type name provided as an argument to this
59106
+ * function
59107
+ */
59108
+ const updateTypeIdentifierNames = (typeReferences, typeImportData, sourceFilePath, initialType) => {
59109
+ let currentTypeName = initialType;
59110
+ // iterate over each of the type references, as there may be >1 reference to inspect
59111
+ for (const typeReference of Object.values(typeReferences)) {
59112
+ const importResolvedFile = getTypeImportPath(typeReference.path, sourceFilePath);
59113
+ if (!typeImportData.hasOwnProperty(importResolvedFile)) {
59114
+ continue;
59115
+ }
59116
+ for (const typesImportDatumElement of typeImportData[importResolvedFile]) {
59117
+ currentTypeName = updateTypeName(currentTypeName, typesImportDatumElement);
59118
+ }
59119
+ }
59120
+ return currentTypeName;
59121
+ };
59122
+ /**
59123
+ * Determine the path of a given type reference, relative to the path of a source file
59124
+ * @param importResolvedFile the path to the file containing the resolve type. may be absolute or relative
59125
+ * @param sourceFilePath the component source file path to resolve against
59126
+ * @returns the path of the type import
59127
+ */
59128
+ const getTypeImportPath = (importResolvedFile, sourceFilePath) => {
59129
+ const isPathRelative = importResolvedFile && importResolvedFile.startsWith('.');
59130
+ if (isPathRelative) {
59131
+ importResolvedFile = resolve$1(dirname(sourceFilePath), importResolvedFile);
59132
+ }
59133
+ return importResolvedFile;
59134
+ };
59135
+ /**
59136
+ * Determine whether the string representation of a type should be replaced with an alias
59137
+ * @param currentTypeName the current string representation of a type
59138
+ * @param typeAlias a type member and a potential different name associated with the type member
59139
+ * @returns the updated string representation of a type. If the type is not updated, the original type name is returned
59140
+ */
59141
+ const updateTypeName = (currentTypeName, typeAlias) => {
59142
+ if (!typeAlias.importName) {
59143
+ return currentTypeName;
59144
+ }
59145
+ // TODO(STENCIL-419): Update this functionality to no longer use a regex
59146
+ // negative lookahead specifying that quotes that designate a string in JavaScript cannot follow some expression
59147
+ const endingStrChar = '(?!("|\'|`))';
59148
+ /**
59149
+ * A regular expression that looks at type names along a [word boundary](https://www.regular-expressions.info/wordboundaries.html).
59150
+ * This is used as the best approximation for replacing type collisions, as this stage of compilation has only
59151
+ * 'flattened' type information in the form of a String.
59152
+ *
59153
+ * This regex should be expected to capture types that are found in generics, unions, intersections, etc., but not
59154
+ * those in string literals. We do not check for a starting quote (" | ' | `) here as some browsers do not support
59155
+ * negative lookbehind. This works "well enough" until STENCIL-419 is completed.
59156
+ */
59157
+ const typeNameRegex = new RegExp(`${typeAlias.localName}\\b${endingStrChar}`, 'g');
59158
+ return currentTypeName.replace(typeNameRegex, typeAlias.importName);
59159
+ };
58870
59160
  /**
58871
59161
  * Writes Stencil core typings file to disk for a dist-* output target
58872
59162
  * @param config the Stencil configuration associated with the project being compiled
58873
59163
  * @param compilerCtx the current compiler context
58874
- * @returns
59164
+ * @returns the results of writing one or more type declaration files to disk
58875
59165
  */
58876
59166
  const copyStencilCoreDts = async (config, compilerCtx) => {
58877
59167
  const typesOutputTargets = config.outputTargets.filter(isOutputTargetDistTypes).filter((o) => o.typesDir);
@@ -58905,13 +59195,19 @@ const sortImportNames = (a, b) => {
58905
59195
  return 0;
58906
59196
  };
58907
59197
 
58908
- const generateEventTypes = (cmpEvents) => {
58909
- return cmpEvents.map((cmpEvent) => {
59198
+ /**
59199
+ * Generates the individual event types for all @Event() decorated events in a component
59200
+ * @param cmpMeta component runtime metadata for a single component
59201
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
59202
+ * @param cmpClassName The pascal cased name of the component class
59203
+ * @returns the generated type metadata
59204
+ */
59205
+ const generateEventTypes = (cmpMeta, typeImportData, cmpClassName) => {
59206
+ return cmpMeta.events.map((cmpEvent) => {
58910
59207
  const name = `on${toTitleCase(cmpEvent.name)}`;
58911
- const type = cmpEvent.complexType.original
58912
- ? `(event: CustomEvent<${cmpEvent.complexType.original}>) => void`
58913
- : `CustomEvent`;
58914
- return {
59208
+ const cmpEventDetailInterface = `${cmpClassName}CustomEvent`;
59209
+ const type = getEventType$1(cmpEvent, cmpEventDetailInterface, typeImportData, cmpMeta.sourceFilePath);
59210
+ const typeInfo = {
58915
59211
  name,
58916
59212
  type,
58917
59213
  optional: false,
@@ -58919,25 +59215,63 @@ const generateEventTypes = (cmpEvents) => {
58919
59215
  internal: cmpEvent.internal,
58920
59216
  jsdoc: getTextDocs(cmpEvent.docs),
58921
59217
  };
59218
+ return typeInfo;
58922
59219
  });
58923
59220
  };
59221
+ /**
59222
+ * Determine the correct type name for all type(s) used by a class member annotated with `@Event()`
59223
+ * @param cmpEvent the compiler metadata for a single `@Event()`
59224
+ * @param cmpEventDetailInterface the name of the custom event type to use in the generated type
59225
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
59226
+ * @param componentSourcePath the path to the component on disk
59227
+ * @returns the type associated with a `@Event()`
59228
+ */
59229
+ const getEventType$1 = (cmpEvent, cmpEventDetailInterface, typeImportData, componentSourcePath) => {
59230
+ if (!cmpEvent.complexType.original) {
59231
+ return 'CustomEvent';
59232
+ }
59233
+ const updatedTypeName = updateTypeIdentifierNames(cmpEvent.complexType.references, typeImportData, componentSourcePath, cmpEvent.complexType.original);
59234
+ return `(event: ${cmpEventDetailInterface}<${updatedTypeName}>) => void`;
59235
+ };
58924
59236
 
58925
- const generateMethodTypes = (cmpMethods) => {
58926
- return cmpMethods.map((cmpMethod) => ({
59237
+ /**
59238
+ * Generates the individual event types for all @Method() decorated events in a component
59239
+ * @param cmpMeta component runtime metadata for a single component
59240
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
59241
+ * @returns the generated type metadata
59242
+ */
59243
+ const generateMethodTypes = (cmpMeta, typeImportData) => {
59244
+ return cmpMeta.methods.map((cmpMethod) => ({
58927
59245
  name: cmpMethod.name,
58928
- type: cmpMethod.complexType.signature,
59246
+ type: getType$1(cmpMethod, typeImportData, cmpMeta.sourceFilePath),
58929
59247
  optional: false,
58930
59248
  required: false,
58931
59249
  internal: cmpMethod.internal,
58932
59250
  jsdoc: getTextDocs(cmpMethod.docs),
58933
59251
  }));
58934
59252
  };
59253
+ /**
59254
+ * Determine the correct type name for all type(s) used by a class member annotated with `@Method()`
59255
+ * @param cmpMethod the compiler metadata for a single `@Method()`
59256
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
59257
+ * @param componentSourcePath the path to the component on disk
59258
+ * @returns the type associated with a `@Method()`
59259
+ */
59260
+ function getType$1(cmpMethod, typeImportData, componentSourcePath) {
59261
+ return updateTypeIdentifierNames(cmpMethod.complexType.references, typeImportData, componentSourcePath, cmpMethod.complexType.signature);
59262
+ }
58935
59263
 
58936
- const generatePropTypes = (cmpMeta) => {
59264
+ /**
59265
+ * Generates the individual event types for all @Prop() decorated events in a component
59266
+ * @param cmpMeta component runtime metadata for a single component
59267
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
59268
+ * @returns the generated type metadata
59269
+ */
59270
+ const generatePropTypes = (cmpMeta, typeImportData) => {
58937
59271
  return [
58938
59272
  ...cmpMeta.properties.map((cmpProp) => ({
58939
59273
  name: cmpProp.name,
58940
- type: cmpProp.complexType.original,
59274
+ type: getType(cmpProp, typeImportData, cmpMeta.sourceFilePath),
58941
59275
  optional: cmpProp.optional,
58942
59276
  required: cmpProp.required,
58943
59277
  internal: cmpProp.internal,
@@ -58953,20 +59287,31 @@ const generatePropTypes = (cmpMeta) => {
58953
59287
  })),
58954
59288
  ];
58955
59289
  };
59290
+ /**
59291
+ * Determine the correct type name for all type(s) used by a class member annotated with `@Prop()`
59292
+ * @param cmpProp the compiler metadata for a single `@Prop()`
59293
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
59294
+ * @param componentSourcePath the path to the component on disk
59295
+ * @returns the type associated with a `@Prop()`
59296
+ */
59297
+ function getType(cmpProp, typeImportData, componentSourcePath) {
59298
+ return updateTypeIdentifierNames(cmpProp.complexType.references, typeImportData, componentSourcePath, cmpProp.complexType.original);
59299
+ }
58956
59300
 
58957
59301
  /**
58958
59302
  * Generate a string based on the types that are defined within a component
58959
59303
  * @param cmp the metadata for the component that a type definition string is generated for
59304
+ * @param typeImportData locally/imported/globally used type names, which may be used to prevent naming collisions
58960
59305
  * @param areTypesInternal `true` if types being generated are for a project's internal purposes, `false` otherwise
58961
59306
  * @returns the generated types string alongside additional metadata
58962
59307
  */
58963
- const generateComponentTypes = (cmp, areTypesInternal) => {
59308
+ const generateComponentTypes = (cmp, typeImportData, areTypesInternal) => {
58964
59309
  const tagName = cmp.tagName.toLowerCase();
58965
59310
  const tagNameAsPascal = dashToPascalCase$1(tagName);
58966
59311
  const htmlElementName = `HTML${tagNameAsPascal}Element`;
58967
- const propAttributes = generatePropTypes(cmp);
58968
- const methodAttributes = generateMethodTypes(cmp.methods);
58969
- const eventAttributes = generateEventTypes(cmp.events);
59312
+ const propAttributes = generatePropTypes(cmp, typeImportData);
59313
+ const methodAttributes = generateMethodTypes(cmp, typeImportData);
59314
+ const eventAttributes = generateEventTypes(cmp, typeImportData, tagNameAsPascal);
58970
59315
  const componentAttributes = attributesToMultiLineString([...propAttributes, ...methodAttributes], false, areTypesInternal);
58971
59316
  const isDep = cmp.isCollectionDependency;
58972
59317
  const jsxAttributes = attributesToMultiLineString([...propAttributes, ...eventAttributes], true, areTypesInternal);
@@ -59010,66 +59355,111 @@ const attributesToMultiLineString = (attributes, jsxAttributes, internal) => {
59010
59355
  return attributesStr !== '' ? `${attributesStr}\n` : '';
59011
59356
  };
59012
59357
 
59358
+ /**
59359
+ * Generates the custom event interface for each component that combines the `CustomEvent` interface with
59360
+ * the HTMLElement target. This is used to allow implementers to use strict typings on event handlers.
59361
+ *
59362
+ * The generated interface accepts a generic for the event detail type. This allows implementers to use
59363
+ * custom typings for individual events without Stencil needing to generate an interface for each event.
59364
+ *
59365
+ * @param cmp The component compiler metadata
59366
+ * @returns The generated interface type definition.
59367
+ */
59368
+ const generateEventDetailTypes = (cmp) => {
59369
+ const tagName = cmp.tagName.toLowerCase();
59370
+ const tagNameAsPascal = dashToPascalCase$1(tagName);
59371
+ const htmlElementName = `HTML${tagNameAsPascal}Element`;
59372
+ const isDep = cmp.isCollectionDependency;
59373
+ const cmpEventInterface = `${tagNameAsPascal}CustomEvent`;
59374
+ const cmpInterface = [
59375
+ `export interface ${cmpEventInterface}<T> extends CustomEvent<T> {`,
59376
+ ` detail: T;`,
59377
+ ` target: ${htmlElementName};`,
59378
+ `}`,
59379
+ ];
59380
+ return {
59381
+ isDep,
59382
+ tagName,
59383
+ tagNameAsPascal,
59384
+ htmlElementName,
59385
+ component: cmpInterface.join('\n'),
59386
+ jsx: cmpInterface.join('\n'),
59387
+ element: cmpInterface.join('\n'),
59388
+ };
59389
+ };
59390
+
59013
59391
  /**
59014
59392
  * Find all referenced types by a component and add them to the `importDataObj` parameter
59015
- * @param importDataObj key/value of type import file, each value is an array of imported types
59016
- * @param allTypes an output parameter containing a map of seen types and the number of times the type has been seen
59393
+ * @param importDataObj an output parameter that contains the imported types seen thus far by the compiler
59394
+ * @param typeCounts a map of seen types and the number of times the type has been seen
59017
59395
  * @param cmp the metadata associated with the component whose types are being inspected
59018
59396
  * @param filePath the path of the component file
59019
59397
  * @returns the updated import data
59020
59398
  */
59021
- const updateReferenceTypeImports = (importDataObj, allTypes, cmp, filePath) => {
59022
- const updateImportReferences = updateImportReferenceFactory(allTypes, filePath);
59399
+ const updateReferenceTypeImports = (importDataObj, typeCounts, cmp, filePath) => {
59400
+ const updateImportReferences = updateImportReferenceFactory(typeCounts, filePath);
59023
59401
  return [...cmp.properties, ...cmp.events, ...cmp.methods]
59024
59402
  .filter((cmpProp) => cmpProp.complexType && cmpProp.complexType.references)
59025
- .reduce((obj, cmpProp) => {
59026
- return updateImportReferences(obj, cmpProp.complexType.references);
59403
+ .reduce((typesImportData, cmpProp) => {
59404
+ return updateImportReferences(typesImportData, cmpProp.complexType.references);
59027
59405
  }, importDataObj);
59028
59406
  };
59029
- const updateImportReferenceFactory = (allTypes, filePath) => {
59407
+ /**
59408
+ * Factory function to create an `ImportReferenceUpdater` instance
59409
+ * @param typeCounts a key-value store of seen type names and the number of times the type name has been seen
59410
+ * @param filePath the path of the file containing the component whose imports are being inspected
59411
+ * @returns an `ImportReferenceUpdater` instance for updating import references in the provided `filePath`
59412
+ */
59413
+ const updateImportReferenceFactory = (typeCounts, filePath) => {
59414
+ /**
59415
+ * Determines the number of times that a type identifier (name) has been used. If an identifier has been used before,
59416
+ * append the number of times the identifier has been seen to its name to avoid future naming collisions
59417
+ * @param name the identifier name to check for previous usages
59418
+ * @returns the identifier name, potentially with an integer appended to its name if it has been seen before.
59419
+ */
59030
59420
  function getIncrementTypeName(name) {
59031
- const counter = allTypes.get(name);
59421
+ const counter = typeCounts.get(name);
59032
59422
  if (counter === undefined) {
59033
- allTypes.set(name, 1);
59423
+ typeCounts.set(name, 1);
59034
59424
  return name;
59035
59425
  }
59036
- allTypes.set(name, counter + 1);
59426
+ typeCounts.set(name, counter + 1);
59037
59427
  return `${name}${counter}`;
59038
59428
  }
59039
- return (obj, typeReferences) => {
59429
+ return (existingTypeImportData, typeReferences) => {
59040
59430
  Object.keys(typeReferences)
59041
59431
  .map((typeName) => {
59042
59432
  return [typeName, typeReferences[typeName]];
59043
59433
  })
59044
- .forEach(([typeName, type]) => {
59045
- let importFileLocation;
59434
+ .forEach(([typeName, typeReference]) => {
59435
+ let importResolvedFile;
59046
59436
  // If global then there is no import statement needed
59047
- if (type.location === 'global') {
59437
+ if (typeReference.location === 'global') {
59048
59438
  return;
59049
59439
  // If local then import location is the current file
59050
59440
  }
59051
- else if (type.location === 'local') {
59052
- importFileLocation = filePath;
59441
+ else if (typeReference.location === 'local') {
59442
+ importResolvedFile = filePath;
59053
59443
  }
59054
- else if (type.location === 'import') {
59055
- importFileLocation = type.path;
59444
+ else if (typeReference.location === 'import') {
59445
+ importResolvedFile = typeReference.path;
59056
59446
  }
59057
59447
  // If this is a relative path make it absolute
59058
- if (importFileLocation.startsWith('.')) {
59059
- importFileLocation = resolve$1(dirname(filePath), importFileLocation);
59448
+ if (importResolvedFile.startsWith('.')) {
59449
+ importResolvedFile = resolve$1(dirname(filePath), importResolvedFile);
59060
59450
  }
59061
- obj[importFileLocation] = obj[importFileLocation] || [];
59451
+ existingTypeImportData[importResolvedFile] = existingTypeImportData[importResolvedFile] || [];
59062
59452
  // If this file already has a reference to this type move on
59063
- if (obj[importFileLocation].find((df) => df.localName === typeName)) {
59453
+ if (existingTypeImportData[importResolvedFile].find((df) => df.localName === typeName)) {
59064
59454
  return;
59065
59455
  }
59066
59456
  const newTypeName = getIncrementTypeName(typeName);
59067
- obj[importFileLocation].push({
59457
+ existingTypeImportData[importResolvedFile].push({
59068
59458
  localName: typeName,
59069
59459
  importName: newTypeName,
59070
59460
  });
59071
59461
  });
59072
- return obj;
59462
+ return existingTypeImportData;
59073
59463
  };
59074
59464
  };
59075
59465
 
@@ -59107,7 +59497,7 @@ const generateAppTypes = async (config, compilerCtx, buildCtx, destination) => {
59107
59497
  return hasComponentsDtsChanged;
59108
59498
  };
59109
59499
  /**
59110
- * Generates a `component.d.ts` file's contents, which contains the typings for all components in a Stencil project
59500
+ * Generates a `components.d.ts` file's contents, which contains the typings for all components in a Stencil project
59111
59501
  * @param config the Stencil configuration associated with the project being compiled
59112
59502
  * @param buildCtx the context associated with the current build
59113
59503
  * @param areTypesInternal determines if non-exported type definitions are being generated or not
@@ -59118,12 +59508,26 @@ const generateComponentTypesFile = (config, buildCtx, areTypesInternal) => {
59118
59508
  const c = [];
59119
59509
  const allTypes = new Map();
59120
59510
  const components = buildCtx.components.filter((m) => !m.isCollectionDependency);
59511
+ const componentEventDetailTypes = [];
59121
59512
  const modules = components.map((cmp) => {
59513
+ /**
59514
+ * Generate a key-value store that uses the path to the file where an import is defined as the key, and an object
59515
+ * containing the import's original name and any 'new' name we give it to avoid collisions. We're generating this
59516
+ * data structure for each Stencil component in series, therefore the memory footprint of this entity will likely
59517
+ * grow as more components (with additional types) are processed.
59518
+ */
59122
59519
  typeImportData = updateReferenceTypeImports(typeImportData, allTypes, cmp, cmp.sourceFilePath);
59123
- return generateComponentTypes(cmp, areTypesInternal);
59520
+ if (cmp.events.length > 0) {
59521
+ /**
59522
+ * Only generate event detail types for components that have events.
59523
+ */
59524
+ componentEventDetailTypes.push(generateEventDetailTypes(cmp));
59525
+ }
59526
+ return generateComponentTypes(cmp, typeImportData, areTypesInternal);
59124
59527
  });
59125
59528
  c.push(COMPONENTS_DTS_HEADER);
59126
59529
  c.push(`import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";`);
59530
+ // write the import statements for our type declaration file
59127
59531
  c.push(...Object.keys(typeImportData).map((filePath) => {
59128
59532
  const typeData = typeImportData[filePath];
59129
59533
  let importFilePath;
@@ -59145,7 +59549,10 @@ const generateComponentTypesFile = (config, buildCtx, areTypesInternal) => {
59145
59549
  })
59146
59550
  .join(`, `)} } from "${importFilePath}";`;
59147
59551
  }));
59148
- c.push(`export namespace Components {\n${modules.map((m) => `${m.component}`).join('\n')}\n}`);
59552
+ c.push(`export namespace Components {`);
59553
+ c.push(...modules.map((m) => `${m.component}`));
59554
+ c.push(`}`);
59555
+ c.push(...componentEventDetailTypes.map((m) => `${m.component}`));
59149
59556
  c.push(`declare global {`);
59150
59557
  c.push(...modules.map((m) => m.element));
59151
59558
  c.push(` interface HTMLElementTagNameMap {`);
@@ -62066,7 +62473,7 @@ const validateManifestJsonIcon = async (compilerCtx, buildCtx, manifestFilePath,
62066
62473
  return;
62067
62474
  }
62068
62475
  if (iconSrc.startsWith('/')) {
62069
- iconSrc = iconSrc.substr(1);
62476
+ iconSrc = iconSrc.slice(1);
62070
62477
  }
62071
62478
  const manifestDir = dirname(manifestFilePath);
62072
62479
  const iconPath = join(manifestDir, iconSrc);
@@ -62176,52 +62583,114 @@ const getTsOptionsToExtend = (config) => {
62176
62583
  return tsOptions;
62177
62584
  };
62178
62585
 
62586
+ /**
62587
+ * Create a TypeScript Program ({@link ts.Program}) to perform builds of a Stencil project using the provided
62588
+ * `buildCallback` entity
62589
+ * @param config a Stencil configuration to apply to a full build of a Stencil project
62590
+ * @param buildCallback a callback that invokes the actual transpilation of a Stencil project
62591
+ * @returns a Program that marries the TypeScript and Stencil compilers together.
62592
+ */
62179
62593
  const createTsBuildProgram = async (config, buildCallback) => {
62180
- let isRunning = false;
62181
- let timeoutId;
62594
+ let isBuildRunning = false;
62595
+ let currentBuildTimeoutId;
62182
62596
  const optionsToExtend = getTsOptionsToExtend(config);
62597
+ /**
62598
+ * Create a {@link ts.System}. The System is responsible for handling all interactions between the TypeScript compiler
62599
+ * and the host operating system.
62600
+ */
62183
62601
  const tsWatchSys = {
62184
62602
  ...t.sys,
62185
- watchFile(path, callback) {
62186
- if (path.endsWith(`/${GENERATED_DTS$1}`)) {
62187
- return t.sys.watchFile(path, callback);
62188
- }
62603
+ /**
62604
+ * Watch changes in source files, missing files needed to update the program or config file
62605
+ * @returns a no-op file watcher
62606
+ */
62607
+ watchFile() {
62189
62608
  return {
62190
62609
  close() { },
62191
62610
  };
62192
62611
  },
62612
+ /**
62613
+ * Watch a resolved module's failed lookup locations, config file specs, type roots where auto type reference
62614
+ * directives are added
62615
+ * @returns a no-op file watcher
62616
+ */
62193
62617
  watchDirectory() {
62194
62618
  return {
62195
62619
  close() { },
62196
62620
  };
62197
62621
  },
62198
- setTimeout(callback, time) {
62199
- timeoutId = setInterval(() => {
62200
- if (!isRunning) {
62622
+ /**
62623
+ * Set delayed compilation, so that multiple changes in short span are compiled together
62624
+ * @param callback a callback to invoke upon the completion of compilation. this function is provided to Stencil by
62625
+ * the TypeScript compiler.
62626
+ * @param timeoutMs the minimum time to wait (in milliseconds) before checking if compilation is complete or not
62627
+ * @returns the identifier for the interval that's created
62628
+ */
62629
+ setTimeout(callback, timeoutMs) {
62630
+ currentBuildTimeoutId = setInterval(() => {
62631
+ if (!isBuildRunning) {
62201
62632
  callback();
62202
- clearInterval(timeoutId);
62633
+ clearInterval(currentBuildTimeoutId);
62203
62634
  }
62204
- }, config.sys.watchTimeout || time);
62205
- return timeoutId;
62635
+ }, config.sys.watchTimeout || timeoutMs);
62636
+ return currentBuildTimeoutId;
62206
62637
  },
62207
- clearTimeout(id) {
62208
- return clearInterval(id);
62638
+ /**
62639
+ * Reset existing delayed compilation
62640
+ * @param timeoutId the current build timeout identifier to clear
62641
+ */
62642
+ clearTimeout(timeoutId) {
62643
+ clearInterval(timeoutId);
62209
62644
  },
62210
62645
  };
62211
- config.sys.addDestory(() => tsWatchSys.clearTimeout(timeoutId));
62646
+ config.sys.addDestory(() => tsWatchSys.clearTimeout(currentBuildTimeoutId));
62647
+ /**
62648
+ * Create a {@link ts.WatchCompilerHost}. A CompilerHost allows a {@link ts.Program} to interact with the
62649
+ * {@link ts.System}, by acting as an intermediary:
62650
+ * ```
62651
+ * ┌────────────┐ ┌──────────────────────┐ ┌───────────┐ ┌──────────────────┐
62652
+ * │ ts.Program │<->│ ts.WatchCompilerHost │<->│ ts.System │<->│ Operating System │
62653
+ * └────────────┘ └──────────────────────┘ └───────────┘ └──────────────────┘
62654
+ * ```
62655
+ *
62656
+ * Strictly speaking, the created entity is a subclass of a WatchCompilerHost. The
62657
+ * {@link ts.WatchCompilerHostOfConfigFile} class has the following features that makes it useful to Stencil (even
62658
+ * when Stencil is performing a single, full build):
62659
+ * - it provides the opportunity to extend/alter an existing tsconfig file, allowing users to override specific
62660
+ * configuration options via {@link ts.WatchCompilerHostOfConfigFile#optionsToExtend}, which is a provided as an
62661
+ * argument in the constructor
62662
+ * - it includes the {@link ts.WatchCompilerHost#afterProgramCreate} function in its interface, which Stencil
62663
+ * overrides to invoke a build callback (not as a part of this object's creation)
62664
+ */
62212
62665
  const tsWatchHost = t.createWatchCompilerHost(config.tsconfig, optionsToExtend, tsWatchSys, t.createEmitAndSemanticDiagnosticsBuilderProgram, (reportDiagnostic) => {
62213
62666
  config.logger.debug('watch reportDiagnostic:' + reportDiagnostic.messageText);
62214
62667
  }, (reportWatchStatus) => {
62215
62668
  config.logger.debug(reportWatchStatus.messageText);
62216
62669
  });
62670
+ /**
62671
+ * Override {@link ts.WatchCompilerHost#afterProgramCreate} to invoke the build callback that was provided as an
62672
+ * argument to this function.
62673
+ * @param tsBuilder a {@link ts.BuilderProgram} to manage the {@link ts.Program} in the provided build context
62674
+ */
62217
62675
  tsWatchHost.afterProgramCreate = async (tsBuilder) => {
62218
- isRunning = true;
62676
+ isBuildRunning = true;
62219
62677
  await buildCallback(tsBuilder);
62220
- isRunning = false;
62678
+ isBuildRunning = false;
62221
62679
  };
62680
+ /**
62681
+ * Create the initial {@link ts.Program} using Stencil's custom {@link ts.WatchCompilerHostOfConfigFile}. The Program
62682
+ * represents the _TypeScript_ compiler context, that will work in tandem with Stencil's compiler context and build
62683
+ * context
62684
+ */
62222
62685
  return t.createWatchProgram(tsWatchHost);
62223
62686
  };
62224
62687
 
62688
+ /**
62689
+ * Build a callable function to perform a full build of a Stencil project
62690
+ * @param config a Stencil configuration to apply to a full build of a Stencil project
62691
+ * @param compilerCtx the current Stencil compiler context
62692
+ * @returns the results of a full build of Stencil
62693
+ */
62225
62694
  const createFullBuild = async (config, compilerCtx) => {
62226
62695
  return new Promise((resolve) => {
62227
62696
  let tsWatchProgram = null;
@@ -62229,6 +62698,10 @@ const createFullBuild = async (config, compilerCtx) => {
62229
62698
  config.logger.debug(`fileUpdate: ${p}`);
62230
62699
  compilerCtx.fs.clearFileCache(p);
62231
62700
  });
62701
+ /**
62702
+ * A function that kicks off the transpilation process for both the TypeScript and Stencil compilers
62703
+ * @param tsBuilder the manager of the {@link ts.Program} state
62704
+ */
62232
62705
  const onBuild = async (tsBuilder) => {
62233
62706
  const buildCtx = new BuildContext(config, compilerCtx);
62234
62707
  buildCtx.isRebuild = false;
@@ -63383,6 +63856,11 @@ const patchFs = (userSys) => {
63383
63856
  Object.assign(fsObj.__sys, userSys);
63384
63857
  };
63385
63858
 
63859
+ /**
63860
+ * Generate a Stencil compiler instance
63861
+ * @param config a Stencil configuration to apply to the compiler instance
63862
+ * @returns a new instance of a Stencil compiler
63863
+ */
63386
63864
  const createCompiler = async (config) => {
63387
63865
  // actual compiler code
63388
63866
  // could be in a web worker on the browser
@@ -64063,7 +64541,7 @@ const getComponentPathContent = (componentGraph, outputTarget) => {
64063
64541
  const dependencies = [
64064
64542
  {
64065
64543
  name: "@stencil/core",
64066
- version: "2.15.0",
64544
+ version: "2.16.0",
64067
64545
  main: "compiler/stencil.js",
64068
64546
  resources: [
64069
64547
  "package.json",
@@ -64177,29 +64655,55 @@ const getAbsolutePath = (config, dir) => {
64177
64655
  }
64178
64656
  return dir;
64179
64657
  };
64658
+ /**
64659
+ * This function does two things:
64660
+ *
64661
+ * 1. If you pass a `flagName`, it will hoist that `flagName` out of the
64662
+ * `ConfigFlags` object and onto the 'root' level (if you will) of the
64663
+ * `config` under the `configName` (`keyof d.Config`) that you pass.
64664
+ * 2. If you _don't_ pass a `flagName` it will just set the value you supply
64665
+ * on the config.
64666
+ *
64667
+ * @param config the config that we want to update
64668
+ * @param configName the key we're setting on the config
64669
+ * @param flagName either the name of a ConfigFlag prop we want to hoist up or null
64670
+ * @param defaultValue the default value we should set!
64671
+ */
64180
64672
  const setBooleanConfig = (config, configName, flagName, defaultValue) => {
64673
+ var _a;
64181
64674
  if (flagName) {
64182
- if (typeof config.flags[flagName] === 'boolean') {
64183
- config[configName] = config.flags[flagName];
64675
+ const flagValue = (_a = config.flags) === null || _a === void 0 ? void 0 : _a[flagName];
64676
+ if (isBoolean$1(flagValue)) {
64677
+ config[configName] = flagValue;
64184
64678
  }
64185
64679
  }
64186
64680
  const userConfigName = getUserConfigName(config, configName);
64187
64681
  if (typeof config[userConfigName] === 'function') {
64188
64682
  config[userConfigName] = !!config[userConfigName]();
64189
64683
  }
64190
- if (typeof config[userConfigName] === 'boolean') {
64684
+ if (isBoolean$1(config[userConfigName])) {
64191
64685
  config[configName] = config[userConfigName];
64192
64686
  }
64193
64687
  else {
64194
64688
  config[configName] = defaultValue;
64195
64689
  }
64196
64690
  };
64691
+ /**
64692
+ * Find any possibly mis-capitalized configuration names on the config, logging
64693
+ * and warning if one is found.
64694
+ *
64695
+ * @param config the user-supplied config that we're dealing with
64696
+ * @param correctConfigName the configuration name that we're checking for right now
64697
+ * @returns a string container a mis-capitalized config name found on the
64698
+ * config object, if any.
64699
+ */
64197
64700
  const getUserConfigName = (config, correctConfigName) => {
64701
+ var _a;
64198
64702
  const userConfigNames = Object.keys(config);
64199
64703
  for (const userConfigName of userConfigNames) {
64200
64704
  if (userConfigName.toLowerCase() === correctConfigName.toLowerCase()) {
64201
64705
  if (userConfigName !== correctConfigName) {
64202
- config.logger.warn(`config "${userConfigName}" should be "${correctConfigName}"`);
64706
+ (_a = config.logger) === null || _a === void 0 ? void 0 : _a.warn(`config "${userConfigName}" should be "${correctConfigName}"`);
64203
64707
  return userConfigName;
64204
64708
  }
64205
64709
  break;
@@ -64209,19 +64713,20 @@ const getUserConfigName = (config, correctConfigName) => {
64209
64713
  };
64210
64714
 
64211
64715
  const validateDevServer = (config, diagnostics) => {
64212
- var _a;
64716
+ var _a, _b, _c, _d, _e, _f, _g;
64213
64717
  if ((config.devServer === null || config.devServer) === false) {
64214
- return null;
64718
+ return undefined;
64215
64719
  }
64216
- const flags = config.flags;
64720
+ const flags = (_a = config.flags) !== null && _a !== void 0 ? _a : {};
64217
64721
  const devServer = { ...config.devServer };
64218
- if (isString$1(flags.address)) {
64722
+ if (flags.address && isString$1(flags.address)) {
64219
64723
  devServer.address = flags.address;
64220
64724
  }
64221
64725
  else if (!isString$1(devServer.address)) {
64222
64726
  devServer.address = '0.0.0.0';
64223
64727
  }
64224
- let addressProtocol;
64728
+ // default to http for localdev
64729
+ let addressProtocol = 'http';
64225
64730
  if (devServer.address.toLowerCase().startsWith('http://')) {
64226
64731
  devServer.address = devServer.address.substring(7);
64227
64732
  addressProtocol = 'http';
@@ -64231,8 +64736,13 @@ const validateDevServer = (config, diagnostics) => {
64231
64736
  addressProtocol = 'https';
64232
64737
  }
64233
64738
  devServer.address = devServer.address.split('/')[0];
64234
- let addressPort;
64739
+ // split on `:` to get the domain and the (possibly present) port
64740
+ // separately. we've already sliced off the protocol (if present) above
64741
+ // so we can safely split on `:` here.
64235
64742
  const addressSplit = devServer.address.split(':');
64743
+ const isLocalhost = addressSplit[0] === 'localhost' || !isNaN(addressSplit[0].split('.')[0]);
64744
+ // if localhost we use 3333 as a default port
64745
+ let addressPort = isLocalhost ? 3333 : undefined;
64236
64746
  if (addressSplit.length > 1) {
64237
64747
  if (!isNaN(addressSplit[1])) {
64238
64748
  devServer.address = addressSplit[0];
@@ -64246,12 +64756,6 @@ const validateDevServer = (config, diagnostics) => {
64246
64756
  if (isNumber$1(addressPort)) {
64247
64757
  devServer.port = addressPort;
64248
64758
  }
64249
- else if (devServer.address === 'localhost' || !isNaN(devServer.address.split('.')[0])) {
64250
- devServer.port = 3333;
64251
- }
64252
- else {
64253
- devServer.port = null;
64254
- }
64255
64759
  }
64256
64760
  if (devServer.reloadStrategy === undefined) {
64257
64761
  devServer.reloadStrategy = 'hmr';
@@ -64271,14 +64775,14 @@ const validateDevServer = (config, diagnostics) => {
64271
64775
  if (!isBoolean$1(devServer.websocket)) {
64272
64776
  devServer.websocket = true;
64273
64777
  }
64274
- if ((_a = config === null || config === void 0 ? void 0 : config.flags) === null || _a === void 0 ? void 0 : _a.ssr) {
64778
+ if ((_b = config === null || config === void 0 ? void 0 : config.flags) === null || _b === void 0 ? void 0 : _b.ssr) {
64275
64779
  devServer.ssr = true;
64276
64780
  }
64277
64781
  else {
64278
64782
  devServer.ssr = !!devServer.ssr;
64279
64783
  }
64280
64784
  if (devServer.ssr) {
64281
- const wwwOutput = config.outputTargets.find(isOutputTargetWww);
64785
+ const wwwOutput = ((_c = config.outputTargets) !== null && _c !== void 0 ? _c : []).find(isOutputTargetWww);
64282
64786
  devServer.prerenderConfig = wwwOutput === null || wwwOutput === void 0 ? void 0 : wwwOutput.prerenderConfig;
64283
64787
  }
64284
64788
  if (isString$1(config.srcIndexHtml)) {
@@ -64302,16 +64806,17 @@ const validateDevServer = (config, diagnostics) => {
64302
64806
  else if (flags.prerender && !config.watch) {
64303
64807
  devServer.openBrowser = false;
64304
64808
  }
64305
- let serveDir = null;
64306
- let basePath = null;
64307
- const wwwOutputTarget = config.outputTargets.find(isOutputTargetWww);
64809
+ let serveDir;
64810
+ let basePath;
64811
+ const wwwOutputTarget = ((_d = config.outputTargets) !== null && _d !== void 0 ? _d : []).find(isOutputTargetWww);
64308
64812
  if (wwwOutputTarget) {
64309
- const baseUrl = new URL(wwwOutputTarget.baseUrl, 'http://config.stenciljs.com');
64813
+ const baseUrl = new URL((_e = wwwOutputTarget.baseUrl) !== null && _e !== void 0 ? _e : '', 'http://config.stenciljs.com');
64310
64814
  basePath = baseUrl.pathname;
64311
- serveDir = wwwOutputTarget.appDir;
64815
+ serveDir = (_f = wwwOutputTarget.appDir) !== null && _f !== void 0 ? _f : '';
64312
64816
  }
64313
64817
  else {
64314
- serveDir = config.rootDir;
64818
+ basePath = '';
64819
+ serveDir = (_g = config.rootDir) !== null && _g !== void 0 ? _g : '';
64315
64820
  }
64316
64821
  if (!isString$1(basePath) || basePath.trim() === '') {
64317
64822
  basePath = `/`;
@@ -64393,7 +64898,8 @@ const validateNamespace = (c, diagnostics) => {
64393
64898
  }
64394
64899
  };
64395
64900
  const validateDistNamespace = (config, diagnostics) => {
64396
- const hasDist = config.outputTargets.some(isOutputTargetDist);
64901
+ var _a;
64902
+ const hasDist = ((_a = config.outputTargets) !== null && _a !== void 0 ? _a : []).some(isOutputTargetDist);
64397
64903
  if (hasDist) {
64398
64904
  if (!isString$1(config.namespace) || config.namespace.toLowerCase() === 'app') {
64399
64905
  const err = buildError(diagnostics);
@@ -64403,10 +64909,25 @@ const validateDistNamespace = (config, diagnostics) => {
64403
64909
  };
64404
64910
  const DEFAULT_NAMESPACE = 'App';
64405
64911
 
64912
+ /**
64913
+ * Check the provided `.hydratedFlag` prop and return a properly-validated value.
64914
+ *
64915
+ * @param config the configuration we're examining
64916
+ * @returns a suitable value for the hydratedFlag property
64917
+ */
64406
64918
  const validateHydrated = (config) => {
64919
+ /**
64920
+ * If `config.hydratedFlag` is set to `null` that is an explicit signal that we
64921
+ * should _not_ create a default configuration when validating and should instead
64922
+ * just return `undefined`.
64923
+ *
64924
+ * See {@link HydratedFlag} for more details.
64925
+ */
64407
64926
  if (config.hydratedFlag === null || config.hydratedFlag === false) {
64408
- return null;
64927
+ return undefined;
64409
64928
  }
64929
+ // Here we start building up a default config since `.hydratedFlag` wasn't set to
64930
+ // `null` on the provided config.
64410
64931
  const hydratedFlag = { ...config.hydratedFlag };
64411
64932
  if (!isString$1(hydratedFlag.name) || hydratedFlag.property === '') {
64412
64933
  hydratedFlag.name = `hydrated`;
@@ -64520,9 +65041,19 @@ const validateCustomOutput = (config, diagnostics, userOutputs) => {
64520
65041
  });
64521
65042
  };
64522
65043
 
65044
+ /**
65045
+ * Validate that the "dist" output targets are valid and ready to go.
65046
+ *
65047
+ * This function will also add in additional output targets to its output, based on the input supplied.
65048
+ *
65049
+ * @param config the compiler config, what else?
65050
+ * @param userOutputs a user-supplied list of output targets.
65051
+ * @returns a list of OutputTargets which have been validated for us.
65052
+ */
64523
65053
  const validateDist = (config, userOutputs) => {
64524
65054
  const distOutputTargets = userOutputs.filter(isOutputTargetDist);
64525
65055
  return distOutputTargets.reduce((outputs, o) => {
65056
+ var _a;
64526
65057
  const distOutputTarget = validateOutputTargetDist(config, o);
64527
65058
  outputs.push(distOutputTarget);
64528
65059
  const namespace = config.fsNamespace || 'app';
@@ -64542,7 +65073,7 @@ const validateDist = (config, userOutputs) => {
64542
65073
  type: COPY,
64543
65074
  dir: lazyDir,
64544
65075
  copyAssets: 'dist',
64545
- copy: [...distOutputTarget.copy],
65076
+ copy: ((_a = distOutputTarget.copy) !== null && _a !== void 0 ? _a : []).concat(),
64546
65077
  });
64547
65078
  outputs.push({
64548
65079
  type: DIST_GLOBAL_STYLES,
@@ -64552,7 +65083,6 @@ const validateDist = (config, userOutputs) => {
64552
65083
  type: DIST_TYPES,
64553
65084
  dir: distOutputTarget.dir,
64554
65085
  typesDir: distOutputTarget.typesDir,
64555
- empty: distOutputTarget.empty,
64556
65086
  });
64557
65087
  if (config.buildDist) {
64558
65088
  if (distOutputTarget.collectionDir) {
@@ -64597,39 +65127,44 @@ const validateDist = (config, userOutputs) => {
64597
65127
  return outputs;
64598
65128
  }, []);
64599
65129
  };
65130
+ /**
65131
+ * Validate that an OutputTargetDist object has what it needs to do it's job.
65132
+ * To enforce this, we have this function return
65133
+ * `Required<d.OutputTargetDist>`, giving us a compile-time check that all
65134
+ * properties are defined (with either user-supplied or default values).
65135
+ *
65136
+ * @param config the current config
65137
+ * @param o the OutputTargetDist object we want to validate
65138
+ * @returns `Required<d.OutputTargetDist>`, i.e. `d.OutputTargetDist` with all
65139
+ * optional properties rendered un-optional.
65140
+ */
64600
65141
  const validateOutputTargetDist = (config, o) => {
65142
+ var _a;
65143
+ // we need to create an object with a bunch of default values here so that
65144
+ // the typescript compiler can infer their types correctly
64601
65145
  const outputTarget = {
64602
65146
  ...o,
64603
65147
  dir: getAbsolutePath(config, o.dir || DEFAULT_DIR),
65148
+ buildDir: isString$1(o.buildDir) ? o.buildDir : DEFAULT_BUILD_DIR,
65149
+ collectionDir: o.collectionDir !== undefined ? o.collectionDir : DEFAULT_COLLECTION_DIR,
65150
+ typesDir: o.typesDir || DEFAULT_TYPES_DIR,
65151
+ esmLoaderPath: o.esmLoaderPath || DEFAULT_ESM_LOADER_DIR,
65152
+ copy: validateCopy((_a = o.copy) !== null && _a !== void 0 ? _a : [], []),
65153
+ polyfills: isBoolean$1(o.polyfills) ? o.polyfills : undefined,
65154
+ empty: isBoolean$1(o.empty) ? o.empty : true,
64604
65155
  };
64605
- if (!isString$1(outputTarget.buildDir)) {
64606
- outputTarget.buildDir = DEFAULT_BUILD_DIR;
64607
- }
64608
65156
  if (!isAbsolute$1(outputTarget.buildDir)) {
64609
65157
  outputTarget.buildDir = join(outputTarget.dir, outputTarget.buildDir);
64610
65158
  }
64611
- if (outputTarget.collectionDir === undefined) {
64612
- outputTarget.collectionDir = DEFAULT_COLLECTION_DIR;
64613
- }
64614
65159
  if (outputTarget.collectionDir && !isAbsolute$1(outputTarget.collectionDir)) {
64615
65160
  outputTarget.collectionDir = join(outputTarget.dir, outputTarget.collectionDir);
64616
65161
  }
64617
- if (!outputTarget.esmLoaderPath) {
64618
- outputTarget.esmLoaderPath = DEFAULT_ESM_LOADER_DIR;
64619
- }
64620
65162
  if (!isAbsolute$1(outputTarget.esmLoaderPath)) {
64621
65163
  outputTarget.esmLoaderPath = resolve$1(outputTarget.dir, outputTarget.esmLoaderPath);
64622
65164
  }
64623
- if (!outputTarget.typesDir) {
64624
- outputTarget.typesDir = DEFAULT_TYPES_DIR;
64625
- }
64626
65165
  if (!isAbsolute$1(outputTarget.typesDir)) {
64627
65166
  outputTarget.typesDir = join(outputTarget.dir, outputTarget.typesDir);
64628
65167
  }
64629
- if (!isBoolean$1(outputTarget.empty)) {
64630
- outputTarget.empty = true;
64631
- }
64632
- outputTarget.copy = validateCopy(outputTarget.copy, []);
64633
65168
  return outputTarget;
64634
65169
  };
64635
65170
  const DEFAULT_DIR = 'dist';
@@ -65029,9 +65564,9 @@ const validateCustomElementBundle = (config, userOutputs) => {
65029
65564
  const validateOutputTargets = (config, diagnostics) => {
65030
65565
  const userOutputs = (config.outputTargets || []).slice();
65031
65566
  userOutputs.forEach((outputTarget) => {
65032
- if (!VALID_TYPES.includes(outputTarget.type)) {
65567
+ if (!isValidConfigOutputTarget(outputTarget.type)) {
65033
65568
  const err = buildError(diagnostics);
65034
- err.messageText = `Invalid outputTarget type "${outputTarget.type}". Valid outputTarget types include: ${VALID_TYPES.map((t) => `"${t}"`).join(', ')}`;
65569
+ err.messageText = `Invalid outputTarget type "${outputTarget.type}". Valid outputTarget types include: ${VALID_CONFIG_OUTPUT_TARGETS.map((t) => `"${t}"`).join(', ')}`;
65035
65570
  }
65036
65571
  else if (outputTarget.type === DIST_CUSTOM_ELEMENTS_BUNDLE) {
65037
65572
  // TODO(STENCIL-260): Remove this check when the 'dist-custom-elements-bundle' is removed
@@ -65167,6 +65702,7 @@ const DEFAULT_ROLLUP_CONFIG = {
65167
65702
  };
65168
65703
 
65169
65704
  const validateTesting = (config, diagnostics) => {
65705
+ var _a;
65170
65706
  const testing = (config.testing = Object.assign({}, config.testing || {}));
65171
65707
  if (!config.flags || (!config.flags.e2e && !config.flags.spec)) {
65172
65708
  return;
@@ -65221,10 +65757,11 @@ const validateTesting = (config, diagnostics) => {
65221
65757
  testing.testPathIgnorePatterns = DEFAULT_IGNORE_PATTERNS.map((ignorePattern) => {
65222
65758
  return join(testing.rootDir, ignorePattern);
65223
65759
  });
65224
- config.outputTargets
65760
+ ((_a = config.outputTargets) !== null && _a !== void 0 ? _a : [])
65225
65761
  .filter((o) => (isOutputTargetDist(o) || isOutputTargetWww(o)) && o.dir)
65226
65762
  .forEach((outputTarget) => {
65227
- testing.testPathIgnorePatterns.push(outputTarget.dir);
65763
+ var _a;
65764
+ (_a = testing.testPathIgnorePatterns) === null || _a === void 0 ? void 0 : _a.push(outputTarget.dir);
65228
65765
  });
65229
65766
  }
65230
65767
  if (typeof testing.preset !== 'string') {
@@ -65341,7 +65878,15 @@ const validateWorkers = (config) => {
65341
65878
  }
65342
65879
  };
65343
65880
 
65344
- const validateConfig = (userConfig) => {
65881
+ /**
65882
+ * Validate a Config object, ensuring that all its field are present and
65883
+ * consistent with our expectations. This function transforms an
65884
+ * `UnvalidatedConfig` to a `Config`.
65885
+ *
65886
+ * @param userConfig an unvalidated config that we've gotten from a user
65887
+ * @returns an object with config and diagnostics props
65888
+ */
65889
+ const validateConfig = (userConfig = {}) => {
65345
65890
  const config = Object.assign({}, userConfig || {}); // not positive it's json safe
65346
65891
  const diagnostics = [];
65347
65892
  // copy flags (we know it'll be json safe)
@@ -65374,7 +65919,7 @@ const validateConfig = (userConfig) => {
65374
65919
  setBooleanConfig(config, 'sourceMap', null, typeof config.sourceMap === 'undefined' ? false : config.sourceMap);
65375
65920
  setBooleanConfig(config, 'watch', 'watch', false);
65376
65921
  setBooleanConfig(config, 'buildDocs', 'docs', !config.devMode);
65377
- setBooleanConfig(config, 'buildDist', 'esm', !config.devMode || config.buildEs5);
65922
+ setBooleanConfig(config, 'buildDist', null, !config.devMode || config.buildEs5);
65378
65923
  setBooleanConfig(config, 'profile', 'profile', config.devMode);
65379
65924
  setBooleanConfig(config, 'writeLog', 'log', false);
65380
65925
  setBooleanConfig(config, 'buildAppCore', null, true);