@stencil/core 2.16.1 → 2.17.2-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 (50) hide show
  1. package/cli/config-flags.d.ts +110 -0
  2. package/cli/index.cjs +710 -225
  3. package/cli/index.d.ts +2 -1
  4. package/cli/index.js +710 -225
  5. package/cli/package.json +1 -1
  6. package/compiler/package.json +1 -1
  7. package/compiler/stencil.js +905 -290
  8. package/compiler/stencil.min.js +2 -2
  9. package/dependencies.json +1 -1
  10. package/dev-server/client/index.js +1 -1
  11. package/dev-server/client/package.json +1 -1
  12. package/dev-server/connector.html +2 -2
  13. package/dev-server/index.js +1 -1
  14. package/dev-server/package.json +1 -1
  15. package/dev-server/server-process.js +2 -2
  16. package/internal/app-data/package.json +1 -1
  17. package/internal/client/css-shim.js +1 -1
  18. package/internal/client/dom.js +1 -1
  19. package/internal/client/index.js +11 -6
  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/shadow-css.js +1 -1
  24. package/internal/hydrate/index.js +2 -2
  25. package/internal/hydrate/package.json +1 -1
  26. package/internal/package.json +1 -1
  27. package/internal/stencil-private.d.ts +6 -2
  28. package/internal/stencil-public-compiler.d.ts +67 -48
  29. package/internal/testing/index.js +1 -1
  30. package/internal/testing/package.json +1 -1
  31. package/mock-doc/index.cjs +163 -5
  32. package/mock-doc/index.d.ts +86 -1
  33. package/mock-doc/index.js +163 -5
  34. package/mock-doc/package.json +1 -1
  35. package/package.json +2 -1
  36. package/screenshot/package.json +1 -1
  37. package/sys/node/index.js +325 -314
  38. package/sys/node/package.json +1 -1
  39. package/sys/node/worker.js +1 -1
  40. package/testing/index.d.ts +1 -1
  41. package/testing/index.js +445 -382
  42. package/testing/jest/jest-config.d.ts +1 -1
  43. package/testing/jest/jest-runner.d.ts +3 -2
  44. package/testing/jest/jest-screenshot.d.ts +1 -1
  45. package/testing/mocks.d.ts +48 -3
  46. package/testing/package.json +1 -1
  47. package/testing/puppeteer/puppeteer-browser.d.ts +2 -2
  48. package/testing/test/testing-utils.spec.d.ts +1 -0
  49. package/testing/testing-utils.d.ts +74 -2
  50. package/testing/testing.d.ts +2 -2
package/cli/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Stencil CLI (CommonJS) v2.16.1 | MIT Licensed | https://stenciljs.com
2
+ Stencil CLI (CommonJS) v2.17.2-0 | MIT Licensed | https://stenciljs.com
3
3
  */
4
4
  'use strict';
5
5
 
@@ -25,8 +25,50 @@ function _interopNamespace(e) {
25
25
  return Object.freeze(n);
26
26
  }
27
27
 
28
- const toLowerCase = (str) => str.toLowerCase();
29
- const dashToPascalCase = (str) => toLowerCase(str)
28
+ /**
29
+ * This sets the log level hierarchy for our terminal logger, ranging from
30
+ * most to least verbose.
31
+ *
32
+ * Ordering the levels like this lets us easily check whether we should log a
33
+ * message at a given time. For instance, if the log level is set to `'warn'`,
34
+ * then anything passed to the logger with level `'warn'` or `'error'` should
35
+ * be logged, but we should _not_ log anything with level `'info'` or `'debug'`.
36
+ *
37
+ * If we have a current log level `currentLevel` and a message with level
38
+ * `msgLevel` is passed to the logger, we can determine whether or not we should
39
+ * log it by checking if the log level on the message is further up or at the
40
+ * same level in the hierarchy than `currentLevel`, like so:
41
+ *
42
+ * ```ts
43
+ * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel)
44
+ * ```
45
+ *
46
+ * NOTE: for the reasons described above, do not change the order of the entries
47
+ * in this array without good reason!
48
+ */
49
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
50
+
51
+ /**
52
+ * Convert a string from PascalCase to dash-case
53
+ *
54
+ * @param str the string to convert
55
+ * @returns a converted string
56
+ */
57
+ const toDashCase = (str) => str
58
+ .replace(/([A-Z0-9])/g, (match) => ` ${match[0]}`)
59
+ .trim()
60
+ .split(' ')
61
+ .join('-')
62
+ .toLowerCase();
63
+ /**
64
+ * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase,
65
+ * or whatever you call it!)
66
+ *
67
+ * @param str a string to convert
68
+ * @returns a converted string
69
+ */
70
+ const dashToPascalCase = (str) => str
71
+ .toLowerCase()
30
72
  .split('-')
31
73
  .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
32
74
  .join('');
@@ -299,22 +341,223 @@ const validateComponentTag = (tag) => {
299
341
  return undefined;
300
342
  };
301
343
 
302
- const parseFlags = (args, sys) => {
344
+ /**
345
+ * All the Boolean options supported by the Stencil CLI
346
+ */
347
+ const BOOLEAN_CLI_ARGS = [
348
+ 'build',
349
+ 'cache',
350
+ 'checkVersion',
351
+ 'ci',
352
+ 'compare',
353
+ 'debug',
354
+ 'dev',
355
+ 'devtools',
356
+ 'docs',
357
+ 'e2e',
358
+ 'es5',
359
+ 'esm',
360
+ 'headless',
361
+ 'help',
362
+ 'log',
363
+ 'open',
364
+ 'prerender',
365
+ 'prerenderExternal',
366
+ 'prod',
367
+ 'profile',
368
+ 'serviceWorker',
369
+ 'screenshot',
370
+ 'serve',
371
+ 'skipNodeCheck',
372
+ 'spec',
373
+ 'ssr',
374
+ 'stats',
375
+ 'updateScreenshot',
376
+ 'verbose',
377
+ 'version',
378
+ 'watch',
379
+ // JEST CLI OPTIONS
380
+ 'all',
381
+ 'automock',
382
+ 'bail',
383
+ // 'cache', Stencil already supports this argument
384
+ 'changedFilesWithAncestor',
385
+ // 'ci', Stencil already supports this argument
386
+ 'clearCache',
387
+ 'clearMocks',
388
+ 'collectCoverage',
389
+ 'color',
390
+ 'colors',
391
+ 'coverage',
392
+ // 'debug', Stencil already supports this argument
393
+ 'detectLeaks',
394
+ 'detectOpenHandles',
395
+ 'errorOnDeprecated',
396
+ 'expand',
397
+ 'findRelatedTests',
398
+ 'forceExit',
399
+ 'init',
400
+ 'injectGlobals',
401
+ 'json',
402
+ 'lastCommit',
403
+ 'listTests',
404
+ 'logHeapUsage',
405
+ 'noStackTrace',
406
+ 'notify',
407
+ 'onlyChanged',
408
+ 'onlyFailures',
409
+ 'passWithNoTests',
410
+ 'resetMocks',
411
+ 'resetModules',
412
+ 'restoreMocks',
413
+ 'runInBand',
414
+ 'runTestsByPath',
415
+ 'showConfig',
416
+ 'silent',
417
+ 'skipFilter',
418
+ 'testLocationInResults',
419
+ 'updateSnapshot',
420
+ 'useStderr',
421
+ // 'verbose', Stencil already supports this argument
422
+ // 'version', Stencil already supports this argument
423
+ // 'watch', Stencil already supports this argument
424
+ 'watchAll',
425
+ 'watchman',
426
+ ];
427
+ /**
428
+ * All the Number options supported by the Stencil CLI
429
+ */
430
+ const NUMBER_CLI_ARGS = [
431
+ 'port',
432
+ // JEST CLI ARGS
433
+ 'maxConcurrency',
434
+ 'testTimeout',
435
+ ];
436
+ /**
437
+ * All the String options supported by the Stencil CLI
438
+ */
439
+ const STRING_CLI_ARGS = [
440
+ 'address',
441
+ 'config',
442
+ 'docsApi',
443
+ 'docsJson',
444
+ 'emulate',
445
+ 'root',
446
+ 'screenshotConnector',
447
+ // JEST CLI ARGS
448
+ 'cacheDirectory',
449
+ 'changedSince',
450
+ 'collectCoverageFrom',
451
+ // 'config', Stencil already supports this argument
452
+ 'coverageDirectory',
453
+ 'coverageThreshold',
454
+ 'env',
455
+ 'filter',
456
+ 'globalSetup',
457
+ 'globalTeardown',
458
+ 'globals',
459
+ 'haste',
460
+ 'moduleNameMapper',
461
+ 'notifyMode',
462
+ 'outputFile',
463
+ 'preset',
464
+ 'prettierPath',
465
+ 'resolver',
466
+ 'rootDir',
467
+ 'runner',
468
+ 'testEnvironment',
469
+ 'testEnvironmentOptions',
470
+ 'testFailureExitCode',
471
+ 'testNamePattern',
472
+ 'testResultsProcessor',
473
+ 'testRunner',
474
+ 'testSequencer',
475
+ 'testURL',
476
+ 'timers',
477
+ 'transform',
478
+ // ARRAY ARGS
479
+ 'collectCoverageOnlyFrom',
480
+ 'coveragePathIgnorePatterns',
481
+ 'coverageReporters',
482
+ 'moduleDirectories',
483
+ 'moduleFileExtensions',
484
+ 'modulePathIgnorePatterns',
485
+ 'modulePaths',
486
+ 'projects',
487
+ 'reporters',
488
+ 'roots',
489
+ 'selectProjects',
490
+ 'setupFiles',
491
+ 'setupFilesAfterEnv',
492
+ 'snapshotSerializers',
493
+ 'testMatch',
494
+ 'testPathIgnorePatterns',
495
+ 'testPathPattern',
496
+ 'testRegex',
497
+ 'transformIgnorePatterns',
498
+ 'unmockedModulePathPatterns',
499
+ 'watchPathIgnorePatterns',
500
+ ];
501
+ /**
502
+ * All the CLI arguments which may have string or number values
503
+ *
504
+ * `maxWorkers` is an argument which is used both by Stencil _and_ by Jest,
505
+ * which means that we need to support parsing both string and number values.
506
+ */
507
+ const STRING_NUMBER_CLI_ARGS = ['maxWorkers'];
508
+ /**
509
+ * All the LogLevel-type options supported by the Stencil CLI
510
+ *
511
+ * This is a bit silly since there's only one such argument atm,
512
+ * but this approach lets us make sure that we're handling all
513
+ * our arguments in a type-safe way.
514
+ */
515
+ const LOG_LEVEL_CLI_ARGS = ['logLevel'];
516
+ /**
517
+ * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'`
518
+ */
519
+ const CLI_ARG_ALIASES = {
520
+ config: 'c',
521
+ help: 'h',
522
+ port: 'p',
523
+ version: 'v',
524
+ };
525
+ /**
526
+ * Helper function for initializing a `ConfigFlags` object. Provide any overrides
527
+ * for default values and off you go!
528
+ *
529
+ * @param init an object with any overrides for default values
530
+ * @returns a complete CLI flag object
531
+ */
532
+ const createConfigFlags = (init = {}) => {
303
533
  const flags = {
304
534
  task: null,
305
535
  args: [],
306
536
  knownArgs: [],
307
- unknownArgs: null,
537
+ unknownArgs: [],
538
+ ...init,
308
539
  };
540
+ return flags;
541
+ };
542
+
543
+ /**
544
+ * Parse command line arguments into a structured `ConfigFlags` object
545
+ *
546
+ * @param args an array of config flags
547
+ * @param sys an optional compiler system
548
+ * @returns a structured ConfigFlags object
549
+ */
550
+ const parseFlags = (args, sys) => {
551
+ const flags = createConfigFlags();
309
552
  // cmd line has more priority over npm scripts cmd
310
- flags.args = args.slice();
553
+ flags.args = Array.isArray(args) ? args.slice() : [];
311
554
  if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
312
555
  flags.task = flags.args[0];
313
556
  }
314
- parseArgs(flags, flags.args, flags.knownArgs);
557
+ parseArgs(flags, flags.args);
315
558
  if (sys && sys.name === 'node') {
316
559
  const envArgs = getNpmConfigEnvArgs(sys);
317
- parseArgs(flags, envArgs, flags.knownArgs);
560
+ parseArgs(flags, envArgs);
318
561
  envArgs.forEach((envArg) => {
319
562
  if (!flags.args.includes(envArg)) {
320
563
  flags.args.push(envArg);
@@ -333,185 +576,230 @@ const parseFlags = (args, sys) => {
333
576
  return flags;
334
577
  };
335
578
  /**
336
- * Parse command line arguments that are whitelisted via the BOOLEAN_ARG_OPTS,
337
- * STRING_ARG_OPTS, and NUMBER_ARG_OPTS arrays in this file. Handles leading
338
- * dashes on arguments, aliases that are defined for a small number of argument
339
- * types, and parsing values for non-boolean arguments (e.g. port number).
579
+ * Parse command line arguments that are enumerated in the `config-flags`
580
+ * module. Handles leading dashes on arguments, aliases that are defined for a
581
+ * small number of arguments, and parsing values for non-boolean arguments
582
+ * (e.g. port number for the dev server).
583
+ *
584
+ * @param flags a ConfigFlags object to which parsed arguments will be added
585
+ * @param args an array of command-line arguments to parse
586
+ */
587
+ const parseArgs = (flags, args) => {
588
+ BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
589
+ STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
590
+ NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
591
+ STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
592
+ LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
593
+ };
594
+ /**
595
+ * Parse a boolean CLI argument. For these, we support the following formats:
340
596
  *
341
- * @param flags a ConfigFlags object
342
- * @param args an array of command-line arguments to parse
343
- * @param knownArgs an array to which all recognized, legal arguments are added
344
- */
345
- const parseArgs = (flags, args, knownArgs) => {
346
- BOOLEAN_ARG_OPTS.forEach((booleanName) => {
347
- const alias = ARG_OPTS_ALIASES[booleanName];
348
- const flagKey = configCase(booleanName);
349
- if (typeof flags[flagKey] !== 'boolean') {
350
- flags[flagKey] = null;
597
+ * - `--booleanArg`
598
+ * - `--boolean-arg`
599
+ * - `--noBooleanArg`
600
+ * - `--no-boolean-arg`
601
+ *
602
+ * The final two variants should be parsed to a value of `false` on the config
603
+ * object.
604
+ *
605
+ * @param flags the config flags object, while we'll modify
606
+ * @param args our CLI arguments
607
+ * @param configCaseName the argument we want to look at right now
608
+ */
609
+ const parseBooleanArg = (flags, args, configCaseName) => {
610
+ // we support both dash-case and PascalCase versions of the parameter
611
+ // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS
612
+ const alias = CLI_ARG_ALIASES[configCaseName];
613
+ const dashCaseName = toDashCase(configCaseName);
614
+ if (typeof flags[configCaseName] !== 'boolean') {
615
+ flags[configCaseName] = null;
616
+ }
617
+ args.forEach((cmdArg) => {
618
+ let value;
619
+ if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) {
620
+ value = true;
351
621
  }
352
- args.forEach((cmdArg) => {
353
- if (cmdArg === `--${booleanName}`) {
354
- flags[flagKey] = true;
355
- knownArgs.push(cmdArg);
356
- }
357
- else if (cmdArg === `--${flagKey}`) {
358
- flags[flagKey] = true;
359
- knownArgs.push(cmdArg);
360
- }
361
- else if (cmdArg === `--no-${booleanName}`) {
362
- flags[flagKey] = false;
363
- knownArgs.push(cmdArg);
364
- }
365
- else if (cmdArg === `--no${dashToPascalCase(booleanName)}`) {
366
- flags[flagKey] = false;
367
- knownArgs.push(cmdArg);
368
- }
369
- else if (alias && cmdArg === `-${alias}`) {
370
- flags[flagKey] = true;
371
- knownArgs.push(cmdArg);
372
- }
373
- });
374
- });
375
- STRING_ARG_OPTS.forEach((stringName) => {
376
- const alias = ARG_OPTS_ALIASES[stringName];
377
- const flagKey = configCase(stringName);
378
- if (typeof flags[flagKey] !== 'string') {
379
- flags[flagKey] = null;
622
+ else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) {
623
+ value = false;
380
624
  }
381
- for (let i = 0; i < args.length; i++) {
382
- const cmdArg = args[i];
383
- if (cmdArg.startsWith(`--${stringName}=`)) {
384
- const values = cmdArg.split('=');
385
- values.shift();
386
- flags[flagKey] = values.join('=');
387
- knownArgs.push(cmdArg);
388
- }
389
- else if (cmdArg === `--${stringName}`) {
390
- flags[flagKey] = args[i + 1];
391
- knownArgs.push(cmdArg);
392
- knownArgs.push(args[i + 1]);
393
- }
394
- else if (cmdArg === `--${flagKey}`) {
395
- flags[flagKey] = args[i + 1];
396
- knownArgs.push(cmdArg);
397
- knownArgs.push(args[i + 1]);
398
- }
399
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
400
- const values = cmdArg.split('=');
401
- values.shift();
402
- flags[flagKey] = values.join('=');
403
- knownArgs.push(cmdArg);
404
- }
405
- else if (alias) {
406
- if (cmdArg.startsWith(`-${alias}=`)) {
407
- const values = cmdArg.split('=');
408
- values.shift();
409
- flags[flagKey] = values.join('=');
410
- knownArgs.push(cmdArg);
411
- }
412
- else if (cmdArg === `-${alias}`) {
413
- flags[flagKey] = args[i + 1];
414
- knownArgs.push(args[i + 1]);
415
- }
416
- }
625
+ else if (alias && cmdArg === `-${alias}`) {
626
+ value = true;
627
+ }
628
+ if (value !== undefined && cmdArg !== undefined) {
629
+ flags[configCaseName] = value;
630
+ flags.knownArgs.push(cmdArg);
417
631
  }
418
632
  });
419
- NUMBER_ARG_OPTS.forEach((numberName) => {
420
- const alias = ARG_OPTS_ALIASES[numberName];
421
- const flagKey = configCase(numberName);
422
- if (typeof flags[flagKey] !== 'number') {
423
- flags[flagKey] = null;
633
+ };
634
+ /**
635
+ * Parse a string CLI argument
636
+ *
637
+ * @param flags the config flags object, while we'll modify
638
+ * @param args our CLI arguments
639
+ * @param configCaseName the argument we want to look at right now
640
+ */
641
+ const parseStringArg = (flags, args, configCaseName) => {
642
+ if (typeof flags[configCaseName] !== 'string') {
643
+ flags[configCaseName] = null;
644
+ }
645
+ const { value, matchingArg } = getValue(args, configCaseName);
646
+ if (value !== undefined && matchingArg !== undefined) {
647
+ flags[configCaseName] = value;
648
+ flags.knownArgs.push(matchingArg);
649
+ flags.knownArgs.push(value);
650
+ }
651
+ };
652
+ /**
653
+ * Parse a number CLI argument
654
+ *
655
+ * @param flags the config flags object, while we'll modify
656
+ * @param args our CLI arguments
657
+ * @param configCaseName the argument we want to look at right now
658
+ */
659
+ const parseNumberArg = (flags, args, configCaseName) => {
660
+ if (typeof flags[configCaseName] !== 'number') {
661
+ flags[configCaseName] = null;
662
+ }
663
+ const { value, matchingArg } = getValue(args, configCaseName);
664
+ if (value !== undefined && matchingArg !== undefined) {
665
+ flags[configCaseName] = parseInt(value, 10);
666
+ flags.knownArgs.push(matchingArg);
667
+ flags.knownArgs.push(value);
668
+ }
669
+ };
670
+ /**
671
+ * Parse a CLI argument which may be either a string or a number
672
+ *
673
+ * @param flags the config flags object, while we'll modify
674
+ * @param args our CLI arguments
675
+ * @param configCaseName the argument we want to look at right now
676
+ */
677
+ const parseStringNumberArg = (flags, args, configCaseName) => {
678
+ if (!['number', 'string'].includes(typeof flags[configCaseName])) {
679
+ flags[configCaseName] = null;
680
+ }
681
+ const { value, matchingArg } = getValue(args, configCaseName);
682
+ if (value !== undefined && matchingArg !== undefined) {
683
+ if (CLI_ARG_STRING_REGEX.test(value)) {
684
+ // if it matches the regex we treat it like a string
685
+ flags[configCaseName] = value;
424
686
  }
425
- for (let i = 0; i < args.length; i++) {
426
- const cmdArg = args[i];
427
- if (cmdArg.startsWith(`--${numberName}=`)) {
428
- const values = cmdArg.split('=');
429
- values.shift();
430
- flags[flagKey] = parseInt(values.join(''), 10);
431
- knownArgs.push(cmdArg);
432
- }
433
- else if (cmdArg === `--${numberName}`) {
434
- flags[flagKey] = parseInt(args[i + 1], 10);
435
- knownArgs.push(args[i + 1]);
436
- }
437
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
438
- const values = cmdArg.split('=');
439
- values.shift();
440
- flags[flagKey] = parseInt(values.join(''), 10);
441
- knownArgs.push(cmdArg);
442
- }
443
- else if (cmdArg === `--${flagKey}`) {
444
- flags[flagKey] = parseInt(args[i + 1], 10);
445
- knownArgs.push(args[i + 1]);
687
+ else {
688
+ // it was a number, great!
689
+ flags[configCaseName] = Number(value);
690
+ }
691
+ flags.knownArgs.push(matchingArg);
692
+ flags.knownArgs.push(value);
693
+ }
694
+ };
695
+ /**
696
+ * We use this regular expression to detect CLI parameters which
697
+ * should be parsed as string values (as opposed to numbers) for
698
+ * the argument types for which we support both a string and a
699
+ * number value.
700
+ *
701
+ * The regex tests for the presence of at least one character which is
702
+ * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
703
+ * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
704
+ * support the admittedly unlikely use of scientific notation, like `"4e+0"`
705
+ * for `4`).
706
+ *
707
+ * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
708
+ * `"5.0"`. If it matches a given string we conclude that the string should
709
+ * be parsed as a string literal, rather than using `Number` to convert it
710
+ * to a number.
711
+ */
712
+ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
713
+ /**
714
+ * Parse a LogLevel CLI argument. These can be only a specific
715
+ * set of strings, so this function takes care of validating that
716
+ * the value is correct.
717
+ *
718
+ * @param flags the config flags object, while we'll modify
719
+ * @param args our CLI arguments
720
+ * @param configCaseName the argument we want to look at right now
721
+ */
722
+ const parseLogLevelArg = (flags, args, configCaseName) => {
723
+ if (typeof flags[configCaseName] !== 'string') {
724
+ flags[configCaseName] = null;
725
+ }
726
+ const { value, matchingArg } = getValue(args, configCaseName);
727
+ if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) {
728
+ flags[configCaseName] = value;
729
+ flags.knownArgs.push(matchingArg);
730
+ flags.knownArgs.push(value);
731
+ }
732
+ };
733
+ /**
734
+ * Helper for pulling values out from the raw array of CLI arguments. This logic
735
+ * is shared between a few different types of CLI args.
736
+ *
737
+ * We look for arguments in the following formats:
738
+ *
739
+ * - `--my-cli-argument value`
740
+ * - `--my-cli-argument=value`
741
+ * - `--myCliArgument value`
742
+ * - `--myCliArgument=value`
743
+ *
744
+ * We also check for shortened aliases, which we define for a few arguments.
745
+ *
746
+ * @param args the CLI args we're dealing with
747
+ * @param configCaseName the ConfigFlag key which we're looking to pull out a value for
748
+ * @returns the value for the flag as well as the exact string which it matched from
749
+ * the user input.
750
+ */
751
+ const getValue = (args, configCaseName) => {
752
+ // for some CLI args we have a short alias, like 'c' for 'config'
753
+ const alias = CLI_ARG_ALIASES[configCaseName];
754
+ // we support supplying arguments in both dash-case and configCase
755
+ // for ease of use
756
+ const dashCaseName = toDashCase(configCaseName);
757
+ let value;
758
+ let matchingArg;
759
+ args.forEach((arg, i) => {
760
+ if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) {
761
+ value = getEqualsValue(arg);
762
+ matchingArg = arg;
763
+ }
764
+ else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) {
765
+ value = args[i + 1];
766
+ matchingArg = arg;
767
+ }
768
+ else if (alias) {
769
+ if (arg.startsWith(`-${alias}=`)) {
770
+ value = getEqualsValue(arg);
771
+ matchingArg = arg;
446
772
  }
447
- else if (alias) {
448
- if (cmdArg.startsWith(`-${alias}=`)) {
449
- const values = cmdArg.split('=');
450
- values.shift();
451
- flags[flagKey] = parseInt(values.join(''), 10);
452
- knownArgs.push(cmdArg);
453
- }
454
- else if (cmdArg === `-${alias}`) {
455
- flags[flagKey] = parseInt(args[i + 1], 10);
456
- knownArgs.push(args[i + 1]);
457
- }
773
+ else if (arg === `-${alias}`) {
774
+ value = args[i + 1];
775
+ matchingArg = arg;
458
776
  }
459
777
  }
460
778
  });
779
+ return { value, matchingArg };
461
780
  };
462
- const configCase = (prop) => {
463
- prop = dashToPascalCase(prop);
464
- return prop.charAt(0).toLowerCase() + prop.slice(1);
465
- };
466
- const BOOLEAN_ARG_OPTS = [
467
- 'build',
468
- 'cache',
469
- 'check-version',
470
- 'ci',
471
- 'compare',
472
- 'debug',
473
- 'dev',
474
- 'devtools',
475
- 'docs',
476
- 'e2e',
477
- 'es5',
478
- 'esm',
479
- 'headless',
480
- 'help',
481
- 'log',
482
- 'open',
483
- 'prerender',
484
- 'prerender-external',
485
- 'prod',
486
- 'profile',
487
- 'service-worker',
488
- 'screenshot',
489
- 'serve',
490
- 'skip-node-check',
491
- 'spec',
492
- 'ssr',
493
- 'stats',
494
- 'update-screenshot',
495
- 'verbose',
496
- 'version',
497
- 'watch',
498
- ];
499
- const NUMBER_ARG_OPTS = ['max-workers', 'port'];
500
- const STRING_ARG_OPTS = [
501
- 'address',
502
- 'config',
503
- 'docs-json',
504
- 'emulate',
505
- 'log-level',
506
- 'root',
507
- 'screenshot-connector',
508
- ];
509
- const ARG_OPTS_ALIASES = {
510
- config: 'c',
511
- help: 'h',
512
- port: 'p',
513
- version: 'v',
514
- };
781
+ /**
782
+ * When a parameter is set in the format `--foobar=12` at the CLI (as opposed to
783
+ * `--foobar 12`) we want to get the value after the `=` sign
784
+ *
785
+ * @param commandArgument the arg in question
786
+ * @returns the value after the `=`
787
+ */
788
+ const getEqualsValue = (commandArgument) => commandArgument.split('=').slice(1).join('=');
789
+ /**
790
+ * Small helper for getting type-system-level assurance that a `string` can be
791
+ * narrowed to a `LogLevel`
792
+ *
793
+ * @param maybeLogLevel the string to check
794
+ * @returns whether this is a `LogLevel`
795
+ */
796
+ const isLogLevel = (maybeLogLevel) =>
797
+ // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
798
+ // boolean` so a `string` cannot be passed to `includes` on a
799
+ // `ReadonlyArray` 😢 thus we `as any`
800
+ //
801
+ // see microsoft/TypeScript#31018 for some discussion of this
802
+ LOG_LEVELS.includes(maybeLogLevel);
515
803
  const getNpmConfigEnvArgs = (sys) => {
516
804
  // process.env.npm_config_argv
517
805
  // {"remain":["4444"],"cooked":["run","serve","--port","4444"],"original":["run","serve","--port","4444"]}
@@ -532,7 +820,7 @@ const getNpmConfigEnvArgs = (sys) => {
532
820
  const dependencies = [
533
821
  {
534
822
  name: "@stencil/core",
535
- version: "2.16.1",
823
+ version: "2.17.2-0",
536
824
  main: "compiler/stencil.js",
537
825
  resources: [
538
826
  "package.json",
@@ -857,12 +1145,11 @@ const tryFn = async (fn, ...args) => {
857
1145
  }
858
1146
  return null;
859
1147
  };
860
- const isInteractive = (sys, config, object) => {
861
- var _a;
1148
+ const isInteractive = (sys, flags, object) => {
862
1149
  const terminalInfo = object ||
863
1150
  Object.freeze({
864
1151
  tty: sys.isTTY() ? true : false,
865
- ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!((_a = config.flags) === null || _a === void 0 ? void 0 : _a.ci),
1152
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!flags.ci,
866
1153
  });
867
1154
  return terminalInfo.tty && !terminalInfo.ci;
868
1155
  };
@@ -887,19 +1174,19 @@ async function readJson(sys, path) {
887
1174
  }
888
1175
  /**
889
1176
  * Does the command have the debug flag?
890
- * @param config The config passed into the Stencil command
1177
+ * @param flags The configuration flags passed into the Stencil command
891
1178
  * @returns true if --debug has been passed, otherwise false
892
1179
  */
893
- function hasDebug(config) {
894
- return config.flags.debug;
1180
+ function hasDebug(flags) {
1181
+ return flags.debug;
895
1182
  }
896
1183
  /**
897
1184
  * Does the command have the verbose and debug flags?
898
- * @param config The config passed into the Stencil command
1185
+ * @param flags The configuration flags passed into the Stencil command
899
1186
  * @returns true if both --debug and --verbose have been passed, otherwise false
900
1187
  */
901
- function hasVerbose(config) {
902
- return config.flags.verbose && hasDebug(config);
1188
+ function hasVerbose(flags) {
1189
+ return flags.verbose && hasDebug(flags);
903
1190
  }
904
1191
 
905
1192
  /**
@@ -910,7 +1197,7 @@ function hasVerbose(config) {
910
1197
  * @returns true if telemetry should be sent, false otherwise
911
1198
  */
912
1199
  async function shouldTrack(config, sys, ci) {
913
- return !ci && isInteractive(sys, config) && (await checkTelemetry(sys));
1200
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
914
1201
  }
915
1202
 
916
1203
  const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
@@ -967,7 +1254,9 @@ async function updateConfig(sys, newOptions) {
967
1254
  return await writeConfig(sys, Object.assign(config, newOptions));
968
1255
  }
969
1256
 
1257
+ const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
970
1258
  const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1259
+ const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
971
1260
  const DOCS_CUSTOM = 'docs-custom';
972
1261
  const DOCS_JSON = 'docs-json';
973
1262
  const DOCS_README = 'docs-readme';
@@ -979,11 +1268,10 @@ const WWW = 'www';
979
1268
  *
980
1269
  * @param sys The system where the command is invoked
981
1270
  * @param config The config passed into the Stencil command
982
- * @param logger The tool used to do logging
983
1271
  * @param coreCompiler The compiler used to do builds
984
1272
  * @param result The results of a compiler build.
985
1273
  */
986
- async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) {
1274
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
987
1275
  const tracking = await shouldTrack(config, sys, config.flags.ci);
988
1276
  if (!tracking) {
989
1277
  return;
@@ -991,20 +1279,19 @@ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, r
991
1279
  const component_count = Object.keys(result.componentGraph).length;
992
1280
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
993
1281
  await sendMetric(sys, config, 'stencil_cli_command', data);
994
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1282
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
995
1283
  }
996
1284
  /**
997
1285
  * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
1286
+ *
998
1287
  * @param sys The system where the command is invoked
999
1288
  * @param config The config passed into the Stencil command
1000
- * @param logger The tool used to do logging
1001
1289
  * @param coreCompiler The compiler used to do builds
1002
1290
  * @param action A Promise-based function to call in order to get the duration of any given command.
1003
1291
  * @returns void
1004
1292
  */
1005
- async function telemetryAction(sys, config, logger, coreCompiler, action) {
1006
- var _a;
1007
- const tracking = await shouldTrack(config, sys, !!((_a = config === null || config === void 0 ? void 0 : config.flags) === null || _a === void 0 ? void 0 : _a.ci));
1293
+ async function telemetryAction(sys, config, coreCompiler, action) {
1294
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1008
1295
  let duration = undefined;
1009
1296
  let error;
1010
1297
  if (action) {
@@ -1024,7 +1311,7 @@ async function telemetryAction(sys, config, logger, coreCompiler, action) {
1024
1311
  }
1025
1312
  const data = await prepareData(coreCompiler, config, sys, duration);
1026
1313
  await sendMetric(sys, config, 'stencil_cli_command', data);
1027
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1314
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1028
1315
  if (error) {
1029
1316
  throw error;
1030
1317
  }
@@ -1040,6 +1327,16 @@ async function getActiveTargets(config) {
1040
1327
  const result = config.outputTargets.map((t) => t.type);
1041
1328
  return Array.from(new Set(result));
1042
1329
  }
1330
+ /**
1331
+ * Prepare data for telemetry
1332
+ *
1333
+ * @param coreCompiler the core compiler
1334
+ * @param config the current Stencil config
1335
+ * @param sys the compiler system instance in use
1336
+ * @param duration_ms the duration of the action being tracked
1337
+ * @param component_count the number of components being built (optional)
1338
+ * @returns a Promise wrapping data for the telemetry endpoint
1339
+ */
1043
1340
  const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1044
1341
  const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1045
1342
  const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
@@ -1052,6 +1349,7 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1052
1349
  const cpu_model = sys.details.cpuModel;
1053
1350
  const build = coreCompiler.buildId || 'unknown';
1054
1351
  const has_app_pwa_config = hasAppTarget(config);
1352
+ const anonymizedConfig = anonymizeConfigForTelemetry(config);
1055
1353
  return {
1056
1354
  yarn,
1057
1355
  duration_ms,
@@ -1071,12 +1369,83 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1071
1369
  typescript,
1072
1370
  rollup,
1073
1371
  has_app_pwa_config,
1372
+ config: anonymizedConfig,
1074
1373
  };
1075
1374
  };
1375
+ // props in output targets for which we retain their original values when
1376
+ // preparing a config for telemetry
1377
+ //
1378
+ // we omit the values of all other fields on output targets.
1379
+ const OUTPUT_TARGET_KEYS_TO_KEEP = ['type'];
1380
+ // top-level config props that we anonymize for telemetry
1381
+ const CONFIG_PROPS_TO_ANONYMIZE = [
1382
+ 'rootDir',
1383
+ 'fsNamespace',
1384
+ 'packageJsonFilePath',
1385
+ 'namespace',
1386
+ 'srcDir',
1387
+ 'srcIndexHtml',
1388
+ 'buildLogFilePath',
1389
+ 'cacheDir',
1390
+ 'configPath',
1391
+ 'tsconfig',
1392
+ ];
1393
+ // Props we delete entirely from the config for telemetry
1394
+ //
1395
+ // TODO(STENCIL-469): Investigate improving anonymization for tsCompilerOptions and devServer
1396
+ const CONFIG_PROPS_TO_DELETE = ['sys', 'logger', 'tsCompilerOptions', 'devServer'];
1397
+ /**
1398
+ * Anonymize the config for telemetry, replacing potentially revealing config props
1399
+ * with a placeholder string if they are present (this lets us still track how frequently
1400
+ * these config options are being used)
1401
+ *
1402
+ * @param config the config to anonymize
1403
+ * @returns an anonymized copy of the same config
1404
+ */
1405
+ const anonymizeConfigForTelemetry = (config) => {
1406
+ var _a;
1407
+ const anonymizedConfig = { ...config };
1408
+ for (const prop of CONFIG_PROPS_TO_ANONYMIZE) {
1409
+ if (anonymizedConfig[prop] !== undefined) {
1410
+ anonymizedConfig[prop] = 'omitted';
1411
+ }
1412
+ }
1413
+ anonymizedConfig.outputTargets = ((_a = config.outputTargets) !== null && _a !== void 0 ? _a : []).map((target) => {
1414
+ // Anonymize the outputTargets on our configuration, taking advantage of the
1415
+ // optional 2nd argument to `JSON.stringify`. If anything is not a string
1416
+ // we retain it so that any nested properties are handled, else we check
1417
+ // whether it's in our 'keep' list to decide whether to keep it or replace it
1418
+ // with `"omitted"`.
1419
+ const anonymizedOT = JSON.parse(JSON.stringify(target, (key, value) => {
1420
+ if (!(typeof value === 'string')) {
1421
+ return value;
1422
+ }
1423
+ if (OUTPUT_TARGET_KEYS_TO_KEEP.includes(key)) {
1424
+ return value;
1425
+ }
1426
+ return 'omitted';
1427
+ }));
1428
+ // this prop has to be handled separately because it is an array
1429
+ // so the replace function above will be called with all of its
1430
+ // members, giving us `["omitted", "omitted", ...]`.
1431
+ //
1432
+ // Instead, we check for its presence and manually copy over.
1433
+ if (isOutputTargetHydrate(target) && target.external) {
1434
+ anonymizedOT['external'] = target.external.concat();
1435
+ }
1436
+ return anonymizedOT;
1437
+ });
1438
+ // TODO(STENCIL-469): Investigate improving anonymization for tsCompilerOptions and devServer
1439
+ for (const prop of CONFIG_PROPS_TO_DELETE) {
1440
+ delete anonymizedConfig[prop];
1441
+ }
1442
+ return anonymizedConfig;
1443
+ };
1076
1444
  /**
1077
1445
  * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1078
1446
  * the dependencies and devDependencies properties. Pulls up the current installed version
1079
1447
  * of each package under the @stencil, @ionic, and @capacitor scopes.
1448
+ *
1080
1449
  * @param sys the system instance where telemetry is invoked
1081
1450
  * @param config the Stencil configuration associated with the current task that triggered telemetry
1082
1451
  * @returns an object listing all dev and production dependencies under the aforementioned scopes
@@ -1110,7 +1479,7 @@ async function getInstalledPackages(sys, config) {
1110
1479
  return { packages, packagesNoVersions };
1111
1480
  }
1112
1481
  catch (err) {
1113
- hasDebug(config) && console.error(err);
1482
+ hasDebug(config.flags) && console.error(err);
1114
1483
  return { packages, packagesNoVersions };
1115
1484
  }
1116
1485
  }
@@ -1159,6 +1528,7 @@ function sanitizeDeclaredVersion(version) {
1159
1528
  }
1160
1529
  /**
1161
1530
  * If telemetry is enabled, send a metric to an external data store
1531
+ *
1162
1532
  * @param sys the system instance where telemetry is invoked
1163
1533
  * @param config the Stencil configuration associated with the current task that triggered telemetry
1164
1534
  * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
@@ -1174,10 +1544,11 @@ async function sendMetric(sys, config, name, value) {
1174
1544
  value,
1175
1545
  session_id,
1176
1546
  };
1177
- await sendTelemetry(sys, config, { type: 'telemetry', message });
1547
+ await sendTelemetry(sys, config, message);
1178
1548
  }
1179
1549
  /**
1180
1550
  * Used to read the config file's tokens.telemetry property.
1551
+ *
1181
1552
  * @param sys The system where the command is invoked
1182
1553
  * @returns string
1183
1554
  */
@@ -1199,7 +1570,7 @@ async function sendTelemetry(sys, config, data) {
1199
1570
  try {
1200
1571
  const now = new Date().toISOString();
1201
1572
  const body = {
1202
- metrics: [data.message],
1573
+ metrics: [data],
1203
1574
  sent_at: now,
1204
1575
  };
1205
1576
  // This request is only made if telemetry is on.
@@ -1210,15 +1581,15 @@ async function sendTelemetry(sys, config, data) {
1210
1581
  },
1211
1582
  body: JSON.stringify(body),
1212
1583
  });
1213
- hasVerbose(config) &&
1214
- console.debug('\nSent %O metric to events service (status: %O)', data.message.name, response.status, '\n');
1584
+ hasVerbose(config.flags) &&
1585
+ console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1215
1586
  if (response.status !== 204) {
1216
- hasVerbose(config) &&
1587
+ hasVerbose(config.flags) &&
1217
1588
  console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1218
1589
  }
1219
1590
  }
1220
1591
  catch (e) {
1221
- hasVerbose(config) && console.debug('Telemetry request failed:', e);
1592
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1222
1593
  }
1223
1594
  }
1224
1595
  /**
@@ -1275,7 +1646,7 @@ const taskBuild = async (coreCompiler, config, sys) => {
1275
1646
  const results = await compiler.build();
1276
1647
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1277
1648
  if (sys) {
1278
- await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results);
1649
+ await telemetryBuildFinishedAction(sys, config, coreCompiler, results);
1279
1650
  }
1280
1651
  await compiler.destroy();
1281
1652
  if (results.hasError) {
@@ -1314,6 +1685,7 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1314
1685
  !!global.process &&
1315
1686
  typeof __filename === 'string' &&
1316
1687
  (!global.origin || typeof global.origin !== 'string');
1688
+ const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1317
1689
 
1318
1690
  /**
1319
1691
  * Task to generate component boilerplate and write it to disk. This task can
@@ -1362,7 +1734,10 @@ const taskGenerate = async (coreCompiler, config) => {
1362
1734
  if (!writtenFiles) {
1363
1735
  return config.sys.exit(1);
1364
1736
  }
1365
- // TODO(STENCIL-424): Investigate moving these console.log calls to config.logger.info
1737
+ // We use `console.log` here rather than our `config.logger` because we don't want
1738
+ // our TUI messages to be prefixed with timestamps and so on.
1739
+ //
1740
+ // See STENCIL-424 for details.
1366
1741
  console.log();
1367
1742
  console.log(`${config.logger.gray('$')} stencil generate ${input}`);
1368
1743
  console.log();
@@ -1506,7 +1881,8 @@ export class ${toPascalCase(tagName)} {
1506
1881
  `;
1507
1882
  };
1508
1883
  /**
1509
- * Get the boilerplate for style.
1884
+ * Get the boilerplate for style for a generated component
1885
+ * @returns a boilerplate CSS block
1510
1886
  */
1511
1887
  const getStyleUrlBoilerplate = () => `:host {
1512
1888
  display: block;
@@ -1560,10 +1936,17 @@ describe('${tagName}', () => {
1560
1936
  */
1561
1937
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1562
1938
 
1563
- const taskTelemetry = async (config, sys, logger) => {
1939
+ /**
1940
+ * Entrypoint for the Telemetry task
1941
+ * @param flags configuration flags provided to Stencil when a task was called (either this task or a task that invokes
1942
+ * telemetry)
1943
+ * @param sys the abstraction for interfacing with the operating system
1944
+ * @param logger a logging implementation to log the results out to the user
1945
+ */
1946
+ const taskTelemetry = async (flags, sys, logger) => {
1564
1947
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1565
- const isEnabling = config.flags.args.includes('on');
1566
- const isDisabling = config.flags.args.includes('off');
1948
+ const isEnabling = flags.args.includes('on');
1949
+ const isDisabling = flags.args.includes('off');
1567
1950
  const INFORMATION = `Opt in or our of telemetry. Information about the data we collect is available on our website: ${logger.bold('https://stenciljs.com/telemetry')}`;
1568
1951
  const THANK_YOU = `Thank you for helping to make Stencil better! 💖`;
1569
1952
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
@@ -1592,7 +1975,14 @@ const taskTelemetry = async (config, sys, logger) => {
1592
1975
  `);
1593
1976
  };
1594
1977
 
1595
- const taskHelp = async (config, logger, sys) => {
1978
+ /**
1979
+ * Entrypoint for the Help task, providing Stencil usage context to the user
1980
+ * @param flags configuration flags provided to Stencil when a task was call (either this task or a task that invokes
1981
+ * telemetry)
1982
+ * @param logger a logging implementation to log the results out to the user
1983
+ * @param sys the abstraction for interfacing with the operating system
1984
+ */
1985
+ const taskHelp = async (flags, logger, sys) => {
1596
1986
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1597
1987
  console.log(`
1598
1988
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
@@ -1625,7 +2015,7 @@ const taskHelp = async (config, logger, sys) => {
1625
2015
  `);
1626
2016
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1627
2017
  if (sys) {
1628
- await taskTelemetry(config, sys, logger);
2018
+ await taskTelemetry(flags, sys, logger);
1629
2019
  }
1630
2020
  console.log(`
1631
2021
  ${logger.bold('Examples:')}
@@ -1682,6 +2072,10 @@ const taskServe = async (config) => {
1682
2072
  });
1683
2073
  };
1684
2074
 
2075
+ /**
2076
+ * Entrypoint for any Stencil tests
2077
+ * @param config a validated Stencil configuration entity
2078
+ */
1685
2079
  const taskTest = async (config) => {
1686
2080
  if (!IS_NODE_ENV) {
1687
2081
  config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
@@ -1728,6 +2122,95 @@ const taskTest = async (config) => {
1728
2122
  }
1729
2123
  };
1730
2124
 
2125
+ /**
2126
+ * Creates an instance of a logger
2127
+ * @returns the new logger instance
2128
+ */
2129
+ const createLogger = () => {
2130
+ let useColors = IS_BROWSER_ENV;
2131
+ let level = 'info';
2132
+ return {
2133
+ enableColors: (uc) => (useColors = uc),
2134
+ getLevel: () => level,
2135
+ setLevel: (l) => (level = l),
2136
+ emoji: (e) => e,
2137
+ info: console.log.bind(console),
2138
+ warn: console.warn.bind(console),
2139
+ error: console.error.bind(console),
2140
+ debug: console.debug.bind(console),
2141
+ red: (msg) => msg,
2142
+ green: (msg) => msg,
2143
+ yellow: (msg) => msg,
2144
+ blue: (msg) => msg,
2145
+ magenta: (msg) => msg,
2146
+ cyan: (msg) => msg,
2147
+ gray: (msg) => msg,
2148
+ bold: (msg) => msg,
2149
+ dim: (msg) => msg,
2150
+ bgRed: (msg) => msg,
2151
+ createTimeSpan: (_startMsg, _debug = false) => ({
2152
+ duration: () => 0,
2153
+ finish: () => 0,
2154
+ }),
2155
+ printDiagnostics(diagnostics) {
2156
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
2157
+ },
2158
+ };
2159
+ };
2160
+ const logDiagnostic = (diagnostic, useColors) => {
2161
+ let color = BLUE;
2162
+ let prefix = 'Build';
2163
+ let msg = '';
2164
+ if (diagnostic.level === 'error') {
2165
+ color = RED;
2166
+ prefix = 'Error';
2167
+ }
2168
+ else if (diagnostic.level === 'warn') {
2169
+ color = YELLOW;
2170
+ prefix = 'Warning';
2171
+ }
2172
+ if (diagnostic.header) {
2173
+ prefix = diagnostic.header;
2174
+ }
2175
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
2176
+ if (filePath) {
2177
+ msg += filePath;
2178
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
2179
+ msg += ', line ' + diagnostic.lineNumber;
2180
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
2181
+ msg += ', column ' + diagnostic.columnNumber;
2182
+ }
2183
+ }
2184
+ msg += '\n';
2185
+ }
2186
+ msg += diagnostic.messageText;
2187
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
2188
+ diagnostic.lines.forEach((l) => {
2189
+ msg += '\n' + l.lineNumber + ': ' + l.text;
2190
+ });
2191
+ msg += '\n';
2192
+ }
2193
+ if (useColors) {
2194
+ const styledPrefix = [
2195
+ '%c' + prefix,
2196
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
2197
+ ];
2198
+ console.log(...styledPrefix, msg);
2199
+ }
2200
+ else if (diagnostic.level === 'error') {
2201
+ console.error(msg);
2202
+ }
2203
+ else if (diagnostic.level === 'warn') {
2204
+ console.warn(msg);
2205
+ }
2206
+ else {
2207
+ console.log(msg);
2208
+ }
2209
+ };
2210
+ const YELLOW = `#f39c12`;
2211
+ const RED = `#c0392b`;
2212
+ const BLUE = `#3498db`;
2213
+
1731
2214
  const run = async (init) => {
1732
2215
  const { args, logger, sys } = init;
1733
2216
  try {
@@ -1743,7 +2226,7 @@ const run = async (init) => {
1743
2226
  sys.applyGlobalPatch(sys.getCurrentDirectory());
1744
2227
  }
1745
2228
  if (task === 'help' || flags.help) {
1746
- await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys);
2229
+ await taskHelp(createConfigFlags({ task: 'help', args }), logger, sys);
1747
2230
  return;
1748
2231
  }
1749
2232
  startupLog(logger, task);
@@ -1769,7 +2252,7 @@ const run = async (init) => {
1769
2252
  startupLogVersion(logger, task, coreCompiler);
1770
2253
  loadedCompilerLog(sys, logger, flags, coreCompiler);
1771
2254
  if (task === 'info') {
1772
- await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => {
2255
+ await telemetryAction(sys, { flags: createConfigFlags({ task: 'info' }), logger }, coreCompiler, async () => {
1773
2256
  await taskInfo(coreCompiler, sys, logger);
1774
2257
  });
1775
2258
  return;
@@ -1792,7 +2275,7 @@ const run = async (init) => {
1792
2275
  sys.applyGlobalPatch(validated.config.rootDir);
1793
2276
  }
1794
2277
  await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
1795
- await telemetryAction(sys, validated.config, logger, coreCompiler, async () => {
2278
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
1796
2279
  await runTask(coreCompiler, validated.config, task, sys);
1797
2280
  });
1798
2281
  }
@@ -1805,43 +2288,45 @@ const run = async (init) => {
1805
2288
  }
1806
2289
  };
1807
2290
  const runTask = async (coreCompiler, config, task, sys) => {
1808
- config.flags = config.flags || { task };
1809
- config.outputTargets = config.outputTargets || [];
2291
+ var _a, _b;
2292
+ const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2293
+ const strictConfig = { ...config, flags: createConfigFlags((_b = config.flags) !== null && _b !== void 0 ? _b : { task }), logger };
2294
+ strictConfig.outputTargets = strictConfig.outputTargets || [];
1810
2295
  switch (task) {
1811
2296
  case 'build':
1812
- await taskBuild(coreCompiler, config, sys);
2297
+ await taskBuild(coreCompiler, strictConfig, sys);
1813
2298
  break;
1814
2299
  case 'docs':
1815
- await taskDocs(coreCompiler, config);
2300
+ await taskDocs(coreCompiler, strictConfig);
1816
2301
  break;
1817
2302
  case 'generate':
1818
2303
  case 'g':
1819
- await taskGenerate(coreCompiler, config);
2304
+ await taskGenerate(coreCompiler, strictConfig);
1820
2305
  break;
1821
2306
  case 'help':
1822
- await taskHelp(config, config.logger, sys);
2307
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1823
2308
  break;
1824
2309
  case 'prerender':
1825
- await taskPrerender(coreCompiler, config);
2310
+ await taskPrerender(coreCompiler, strictConfig);
1826
2311
  break;
1827
2312
  case 'serve':
1828
- await taskServe(config);
2313
+ await taskServe(strictConfig);
1829
2314
  break;
1830
2315
  case 'telemetry':
1831
2316
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1832
2317
  if (sys) {
1833
- await taskTelemetry(config, sys, config.logger);
2318
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
1834
2319
  }
1835
2320
  break;
1836
2321
  case 'test':
1837
- await taskTest(config);
2322
+ await taskTest(strictConfig);
1838
2323
  break;
1839
2324
  case 'version':
1840
2325
  console.log(coreCompiler.version);
1841
2326
  break;
1842
2327
  default:
1843
- config.logger.error(`${config.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
1844
- await taskHelp(config, config.logger, sys);
2328
+ strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
2329
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1845
2330
  return config.sys.exit(1);
1846
2331
  }
1847
2332
  };