@stencil/core 2.16.1-0 → 2.17.1

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 +102 -0
  2. package/cli/index.cjs +697 -224
  3. package/cli/index.d.ts +2 -1
  4. package/cli/index.js +697 -224
  5. package/cli/package.json +1 -1
  6. package/compiler/package.json +1 -1
  7. package/compiler/stencil.js +856 -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 +140 -5
  32. package/mock-doc/index.d.ts +76 -1
  33. package/mock-doc/index.js +140 -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 +436 -381
  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-0 | MIT Licensed | https://stenciljs.com
2
+ Stencil CLI (CommonJS) v2.17.1 | 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,211 @@ const validateComponentTag = (tag) => {
299
341
  return undefined;
300
342
  };
301
343
 
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
+ /**
527
+ * Parse command line arguments into a structured `ConfigFlags` object
528
+ *
529
+ * @param args an array of config flags
530
+ * @param sys an optional compiler system
531
+ * @returns a structured ConfigFlags object
532
+ */
302
533
  const parseFlags = (args, sys) => {
303
534
  const flags = {
304
535
  task: null,
305
536
  args: [],
306
537
  knownArgs: [],
307
- unknownArgs: null,
538
+ unknownArgs: [],
308
539
  };
309
540
  // cmd line has more priority over npm scripts cmd
310
- flags.args = args.slice();
541
+ flags.args = Array.isArray(args) ? args.slice() : [];
311
542
  if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
312
543
  flags.task = flags.args[0];
313
544
  }
314
- parseArgs(flags, flags.args, flags.knownArgs);
545
+ parseArgs(flags, flags.args);
315
546
  if (sys && sys.name === 'node') {
316
547
  const envArgs = getNpmConfigEnvArgs(sys);
317
- parseArgs(flags, envArgs, flags.knownArgs);
548
+ parseArgs(flags, envArgs);
318
549
  envArgs.forEach((envArg) => {
319
550
  if (!flags.args.includes(envArg)) {
320
551
  flags.args.push(envArg);
@@ -333,185 +564,230 @@ const parseFlags = (args, sys) => {
333
564
  return flags;
334
565
  };
335
566
  /**
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).
567
+ * Parse command line arguments that are enumerated in the `config-flags`
568
+ * module. Handles leading dashes on arguments, aliases that are defined for a
569
+ * small number of arguments, and parsing values for non-boolean arguments
570
+ * (e.g. port number for the dev server).
571
+ *
572
+ * @param flags a ConfigFlags object to which parsed arguments will be added
573
+ * @param args an array of command-line arguments to parse
574
+ */
575
+ const parseArgs = (flags, args) => {
576
+ BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
577
+ STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
578
+ NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
579
+ STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
580
+ LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
581
+ };
582
+ /**
583
+ * Parse a boolean CLI argument. For these, we support the following formats:
584
+ *
585
+ * - `--booleanArg`
586
+ * - `--boolean-arg`
587
+ * - `--noBooleanArg`
588
+ * - `--no-boolean-arg`
340
589
  *
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;
590
+ * The final two variants should be parsed to a value of `false` on the config
591
+ * object.
592
+ *
593
+ * @param flags the config flags object, while we'll modify
594
+ * @param args our CLI arguments
595
+ * @param configCaseName the argument we want to look at right now
596
+ */
597
+ const parseBooleanArg = (flags, args, configCaseName) => {
598
+ // we support both dash-case and PascalCase versions of the parameter
599
+ // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS
600
+ const alias = CLI_ARG_ALIASES[configCaseName];
601
+ const dashCaseName = toDashCase(configCaseName);
602
+ if (typeof flags[configCaseName] !== 'boolean') {
603
+ flags[configCaseName] = null;
604
+ }
605
+ args.forEach((cmdArg) => {
606
+ let value;
607
+ if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) {
608
+ value = true;
351
609
  }
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;
610
+ else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) {
611
+ value = false;
380
612
  }
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
- }
613
+ else if (alias && cmdArg === `-${alias}`) {
614
+ value = true;
615
+ }
616
+ if (value !== undefined && cmdArg !== undefined) {
617
+ flags[configCaseName] = value;
618
+ flags.knownArgs.push(cmdArg);
417
619
  }
418
620
  });
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;
621
+ };
622
+ /**
623
+ * Parse a string CLI argument
624
+ *
625
+ * @param flags the config flags object, while we'll modify
626
+ * @param args our CLI arguments
627
+ * @param configCaseName the argument we want to look at right now
628
+ */
629
+ const parseStringArg = (flags, args, configCaseName) => {
630
+ if (typeof flags[configCaseName] !== 'string') {
631
+ flags[configCaseName] = null;
632
+ }
633
+ const { value, matchingArg } = getValue(args, configCaseName);
634
+ if (value !== undefined && matchingArg !== undefined) {
635
+ flags[configCaseName] = value;
636
+ flags.knownArgs.push(matchingArg);
637
+ flags.knownArgs.push(value);
638
+ }
639
+ };
640
+ /**
641
+ * Parse a number CLI argument
642
+ *
643
+ * @param flags the config flags object, while we'll modify
644
+ * @param args our CLI arguments
645
+ * @param configCaseName the argument we want to look at right now
646
+ */
647
+ const parseNumberArg = (flags, args, configCaseName) => {
648
+ if (typeof flags[configCaseName] !== 'number') {
649
+ flags[configCaseName] = null;
650
+ }
651
+ const { value, matchingArg } = getValue(args, configCaseName);
652
+ if (value !== undefined && matchingArg !== undefined) {
653
+ flags[configCaseName] = parseInt(value, 10);
654
+ flags.knownArgs.push(matchingArg);
655
+ flags.knownArgs.push(value);
656
+ }
657
+ };
658
+ /**
659
+ * Parse a CLI argument which may be either a string or a number
660
+ *
661
+ * @param flags the config flags object, while we'll modify
662
+ * @param args our CLI arguments
663
+ * @param configCaseName the argument we want to look at right now
664
+ */
665
+ const parseStringNumberArg = (flags, args, configCaseName) => {
666
+ if (!['number', 'string'].includes(typeof flags[configCaseName])) {
667
+ flags[configCaseName] = null;
668
+ }
669
+ const { value, matchingArg } = getValue(args, configCaseName);
670
+ if (value !== undefined && matchingArg !== undefined) {
671
+ if (CLI_ARG_STRING_REGEX.test(value)) {
672
+ // if it matches the regex we treat it like a string
673
+ flags[configCaseName] = value;
424
674
  }
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]);
675
+ else {
676
+ // it was a number, great!
677
+ flags[configCaseName] = Number(value);
678
+ }
679
+ flags.knownArgs.push(matchingArg);
680
+ flags.knownArgs.push(value);
681
+ }
682
+ };
683
+ /**
684
+ * We use this regular expression to detect CLI parameters which
685
+ * should be parsed as string values (as opposed to numbers) for
686
+ * the argument types for which we support both a string and a
687
+ * number value.
688
+ *
689
+ * The regex tests for the presence of at least one character which is
690
+ * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
691
+ * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
692
+ * support the admittedly unlikely use of scientific notation, like `"4e+0"`
693
+ * for `4`).
694
+ *
695
+ * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
696
+ * `"5.0"`. If it matches a given string we conclude that the string should
697
+ * be parsed as a string literal, rather than using `Number` to convert it
698
+ * to a number.
699
+ */
700
+ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
701
+ /**
702
+ * Parse a LogLevel CLI argument. These can be only a specific
703
+ * set of strings, so this function takes care of validating that
704
+ * the value is correct.
705
+ *
706
+ * @param flags the config flags object, while we'll modify
707
+ * @param args our CLI arguments
708
+ * @param configCaseName the argument we want to look at right now
709
+ */
710
+ const parseLogLevelArg = (flags, args, configCaseName) => {
711
+ if (typeof flags[configCaseName] !== 'string') {
712
+ flags[configCaseName] = null;
713
+ }
714
+ const { value, matchingArg } = getValue(args, configCaseName);
715
+ if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) {
716
+ flags[configCaseName] = value;
717
+ flags.knownArgs.push(matchingArg);
718
+ flags.knownArgs.push(value);
719
+ }
720
+ };
721
+ /**
722
+ * Helper for pulling values out from the raw array of CLI arguments. This logic
723
+ * is shared between a few different types of CLI args.
724
+ *
725
+ * We look for arguments in the following formats:
726
+ *
727
+ * - `--my-cli-argument value`
728
+ * - `--my-cli-argument=value`
729
+ * - `--myCliArgument value`
730
+ * - `--myCliArgument=value`
731
+ *
732
+ * We also check for shortened aliases, which we define for a few arguments.
733
+ *
734
+ * @param args the CLI args we're dealing with
735
+ * @param configCaseName the ConfigFlag key which we're looking to pull out a value for
736
+ * @returns the value for the flag as well as the exact string which it matched from
737
+ * the user input.
738
+ */
739
+ const getValue = (args, configCaseName) => {
740
+ // for some CLI args we have a short alias, like 'c' for 'config'
741
+ const alias = CLI_ARG_ALIASES[configCaseName];
742
+ // we support supplying arguments in both dash-case and configCase
743
+ // for ease of use
744
+ const dashCaseName = toDashCase(configCaseName);
745
+ let value;
746
+ let matchingArg;
747
+ args.forEach((arg, i) => {
748
+ if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) {
749
+ value = getEqualsValue(arg);
750
+ matchingArg = arg;
751
+ }
752
+ else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) {
753
+ value = args[i + 1];
754
+ matchingArg = arg;
755
+ }
756
+ else if (alias) {
757
+ if (arg.startsWith(`-${alias}=`)) {
758
+ value = getEqualsValue(arg);
759
+ matchingArg = arg;
446
760
  }
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
- }
761
+ else if (arg === `-${alias}`) {
762
+ value = args[i + 1];
763
+ matchingArg = arg;
458
764
  }
459
765
  }
460
766
  });
767
+ return { value, matchingArg };
461
768
  };
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
- };
769
+ /**
770
+ * When a parameter is set in the format `--foobar=12` at the CLI (as opposed to
771
+ * `--foobar 12`) we want to get the value after the `=` sign
772
+ *
773
+ * @param commandArgument the arg in question
774
+ * @returns the value after the `=`
775
+ */
776
+ const getEqualsValue = (commandArgument) => commandArgument.split('=').slice(1).join('=');
777
+ /**
778
+ * Small helper for getting type-system-level assurance that a `string` can be
779
+ * narrowed to a `LogLevel`
780
+ *
781
+ * @param maybeLogLevel the string to check
782
+ * @returns whether this is a `LogLevel`
783
+ */
784
+ const isLogLevel = (maybeLogLevel) =>
785
+ // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
786
+ // boolean` so a `string` cannot be passed to `includes` on a
787
+ // `ReadonlyArray` 😢 thus we `as any`
788
+ //
789
+ // see microsoft/TypeScript#31018 for some discussion of this
790
+ LOG_LEVELS.includes(maybeLogLevel);
515
791
  const getNpmConfigEnvArgs = (sys) => {
516
792
  // process.env.npm_config_argv
517
793
  // {"remain":["4444"],"cooked":["run","serve","--port","4444"],"original":["run","serve","--port","4444"]}
@@ -532,7 +808,7 @@ const getNpmConfigEnvArgs = (sys) => {
532
808
  const dependencies = [
533
809
  {
534
810
  name: "@stencil/core",
535
- version: "2.16.1-0",
811
+ version: "2.17.1",
536
812
  main: "compiler/stencil.js",
537
813
  resources: [
538
814
  "package.json",
@@ -857,12 +1133,11 @@ const tryFn = async (fn, ...args) => {
857
1133
  }
858
1134
  return null;
859
1135
  };
860
- const isInteractive = (sys, config, object) => {
861
- var _a;
1136
+ const isInteractive = (sys, flags, object) => {
862
1137
  const terminalInfo = object ||
863
1138
  Object.freeze({
864
1139
  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),
1140
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!flags.ci,
866
1141
  });
867
1142
  return terminalInfo.tty && !terminalInfo.ci;
868
1143
  };
@@ -887,19 +1162,19 @@ async function readJson(sys, path) {
887
1162
  }
888
1163
  /**
889
1164
  * Does the command have the debug flag?
890
- * @param config The config passed into the Stencil command
1165
+ * @param flags The configuration flags passed into the Stencil command
891
1166
  * @returns true if --debug has been passed, otherwise false
892
1167
  */
893
- function hasDebug(config) {
894
- return config.flags.debug;
1168
+ function hasDebug(flags) {
1169
+ return flags.debug;
895
1170
  }
896
1171
  /**
897
1172
  * Does the command have the verbose and debug flags?
898
- * @param config The config passed into the Stencil command
1173
+ * @param flags The configuration flags passed into the Stencil command
899
1174
  * @returns true if both --debug and --verbose have been passed, otherwise false
900
1175
  */
901
- function hasVerbose(config) {
902
- return config.flags.verbose && hasDebug(config);
1176
+ function hasVerbose(flags) {
1177
+ return flags.verbose && hasDebug(flags);
903
1178
  }
904
1179
 
905
1180
  /**
@@ -910,7 +1185,7 @@ function hasVerbose(config) {
910
1185
  * @returns true if telemetry should be sent, false otherwise
911
1186
  */
912
1187
  async function shouldTrack(config, sys, ci) {
913
- return !ci && isInteractive(sys, config) && (await checkTelemetry(sys));
1188
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
914
1189
  }
915
1190
 
916
1191
  const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
@@ -967,7 +1242,9 @@ async function updateConfig(sys, newOptions) {
967
1242
  return await writeConfig(sys, Object.assign(config, newOptions));
968
1243
  }
969
1244
 
1245
+ const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
970
1246
  const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1247
+ const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
971
1248
  const DOCS_CUSTOM = 'docs-custom';
972
1249
  const DOCS_JSON = 'docs-json';
973
1250
  const DOCS_README = 'docs-readme';
@@ -979,11 +1256,10 @@ const WWW = 'www';
979
1256
  *
980
1257
  * @param sys The system where the command is invoked
981
1258
  * @param config The config passed into the Stencil command
982
- * @param logger The tool used to do logging
983
1259
  * @param coreCompiler The compiler used to do builds
984
1260
  * @param result The results of a compiler build.
985
1261
  */
986
- async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) {
1262
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
987
1263
  const tracking = await shouldTrack(config, sys, config.flags.ci);
988
1264
  if (!tracking) {
989
1265
  return;
@@ -991,20 +1267,19 @@ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, r
991
1267
  const component_count = Object.keys(result.componentGraph).length;
992
1268
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
993
1269
  await sendMetric(sys, config, 'stencil_cli_command', data);
994
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1270
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
995
1271
  }
996
1272
  /**
997
1273
  * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
1274
+ *
998
1275
  * @param sys The system where the command is invoked
999
1276
  * @param config The config passed into the Stencil command
1000
- * @param logger The tool used to do logging
1001
1277
  * @param coreCompiler The compiler used to do builds
1002
1278
  * @param action A Promise-based function to call in order to get the duration of any given command.
1003
1279
  * @returns void
1004
1280
  */
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));
1281
+ async function telemetryAction(sys, config, coreCompiler, action) {
1282
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1008
1283
  let duration = undefined;
1009
1284
  let error;
1010
1285
  if (action) {
@@ -1024,7 +1299,7 @@ async function telemetryAction(sys, config, logger, coreCompiler, action) {
1024
1299
  }
1025
1300
  const data = await prepareData(coreCompiler, config, sys, duration);
1026
1301
  await sendMetric(sys, config, 'stencil_cli_command', data);
1027
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1302
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1028
1303
  if (error) {
1029
1304
  throw error;
1030
1305
  }
@@ -1040,6 +1315,16 @@ async function getActiveTargets(config) {
1040
1315
  const result = config.outputTargets.map((t) => t.type);
1041
1316
  return Array.from(new Set(result));
1042
1317
  }
1318
+ /**
1319
+ * Prepare data for telemetry
1320
+ *
1321
+ * @param coreCompiler the core compiler
1322
+ * @param config the current Stencil config
1323
+ * @param sys the compiler system instance in use
1324
+ * @param duration_ms the duration of the action being tracked
1325
+ * @param component_count the number of components being built (optional)
1326
+ * @returns a Promise wrapping data for the telemetry endpoint
1327
+ */
1043
1328
  const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1044
1329
  const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1045
1330
  const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
@@ -1052,6 +1337,7 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1052
1337
  const cpu_model = sys.details.cpuModel;
1053
1338
  const build = coreCompiler.buildId || 'unknown';
1054
1339
  const has_app_pwa_config = hasAppTarget(config);
1340
+ const anonymizedConfig = anonymizeConfigForTelemetry(config);
1055
1341
  return {
1056
1342
  yarn,
1057
1343
  duration_ms,
@@ -1071,12 +1357,83 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1071
1357
  typescript,
1072
1358
  rollup,
1073
1359
  has_app_pwa_config,
1360
+ config: anonymizedConfig,
1074
1361
  };
1075
1362
  };
1363
+ // props in output targets for which we retain their original values when
1364
+ // preparing a config for telemetry
1365
+ //
1366
+ // we omit the values of all other fields on output targets.
1367
+ const OUTPUT_TARGET_KEYS_TO_KEEP = ['type'];
1368
+ // top-level config props that we anonymize for telemetry
1369
+ const CONFIG_PROPS_TO_ANONYMIZE = [
1370
+ 'rootDir',
1371
+ 'fsNamespace',
1372
+ 'packageJsonFilePath',
1373
+ 'namespace',
1374
+ 'srcDir',
1375
+ 'srcIndexHtml',
1376
+ 'buildLogFilePath',
1377
+ 'cacheDir',
1378
+ 'configPath',
1379
+ 'tsconfig',
1380
+ ];
1381
+ // Props we delete entirely from the config for telemetry
1382
+ //
1383
+ // TODO(STENCIL-469): Investigate improving anonymization for tsCompilerOptions and devServer
1384
+ const CONFIG_PROPS_TO_DELETE = ['sys', 'logger', 'tsCompilerOptions', 'devServer'];
1385
+ /**
1386
+ * Anonymize the config for telemetry, replacing potentially revealing config props
1387
+ * with a placeholder string if they are present (this lets us still track how frequently
1388
+ * these config options are being used)
1389
+ *
1390
+ * @param config the config to anonymize
1391
+ * @returns an anonymized copy of the same config
1392
+ */
1393
+ const anonymizeConfigForTelemetry = (config) => {
1394
+ var _a;
1395
+ const anonymizedConfig = { ...config };
1396
+ for (const prop of CONFIG_PROPS_TO_ANONYMIZE) {
1397
+ if (anonymizedConfig[prop] !== undefined) {
1398
+ anonymizedConfig[prop] = 'omitted';
1399
+ }
1400
+ }
1401
+ anonymizedConfig.outputTargets = ((_a = config.outputTargets) !== null && _a !== void 0 ? _a : []).map((target) => {
1402
+ // Anonymize the outputTargets on our configuration, taking advantage of the
1403
+ // optional 2nd argument to `JSON.stringify`. If anything is not a string
1404
+ // we retain it so that any nested properties are handled, else we check
1405
+ // whether it's in our 'keep' list to decide whether to keep it or replace it
1406
+ // with `"omitted"`.
1407
+ const anonymizedOT = JSON.parse(JSON.stringify(target, (key, value) => {
1408
+ if (!(typeof value === 'string')) {
1409
+ return value;
1410
+ }
1411
+ if (OUTPUT_TARGET_KEYS_TO_KEEP.includes(key)) {
1412
+ return value;
1413
+ }
1414
+ return 'omitted';
1415
+ }));
1416
+ // this prop has to be handled separately because it is an array
1417
+ // so the replace function above will be called with all of its
1418
+ // members, giving us `["omitted", "omitted", ...]`.
1419
+ //
1420
+ // Instead, we check for its presence and manually copy over.
1421
+ if (isOutputTargetHydrate(target) && target.external) {
1422
+ anonymizedOT['external'] = target.external.concat();
1423
+ }
1424
+ return anonymizedOT;
1425
+ });
1426
+ // TODO(STENCIL-469): Investigate improving anonymization for tsCompilerOptions and devServer
1427
+ for (const prop of CONFIG_PROPS_TO_DELETE) {
1428
+ delete anonymizedConfig[prop];
1429
+ }
1430
+ return anonymizedConfig;
1431
+ };
1076
1432
  /**
1077
1433
  * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1078
1434
  * the dependencies and devDependencies properties. Pulls up the current installed version
1079
1435
  * of each package under the @stencil, @ionic, and @capacitor scopes.
1436
+ *
1080
1437
  * @param sys the system instance where telemetry is invoked
1081
1438
  * @param config the Stencil configuration associated with the current task that triggered telemetry
1082
1439
  * @returns an object listing all dev and production dependencies under the aforementioned scopes
@@ -1110,7 +1467,7 @@ async function getInstalledPackages(sys, config) {
1110
1467
  return { packages, packagesNoVersions };
1111
1468
  }
1112
1469
  catch (err) {
1113
- hasDebug(config) && console.error(err);
1470
+ hasDebug(config.flags) && console.error(err);
1114
1471
  return { packages, packagesNoVersions };
1115
1472
  }
1116
1473
  }
@@ -1159,6 +1516,7 @@ function sanitizeDeclaredVersion(version) {
1159
1516
  }
1160
1517
  /**
1161
1518
  * If telemetry is enabled, send a metric to an external data store
1519
+ *
1162
1520
  * @param sys the system instance where telemetry is invoked
1163
1521
  * @param config the Stencil configuration associated with the current task that triggered telemetry
1164
1522
  * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
@@ -1174,10 +1532,11 @@ async function sendMetric(sys, config, name, value) {
1174
1532
  value,
1175
1533
  session_id,
1176
1534
  };
1177
- await sendTelemetry(sys, config, { type: 'telemetry', message });
1535
+ await sendTelemetry(sys, config, message);
1178
1536
  }
1179
1537
  /**
1180
1538
  * Used to read the config file's tokens.telemetry property.
1539
+ *
1181
1540
  * @param sys The system where the command is invoked
1182
1541
  * @returns string
1183
1542
  */
@@ -1199,7 +1558,7 @@ async function sendTelemetry(sys, config, data) {
1199
1558
  try {
1200
1559
  const now = new Date().toISOString();
1201
1560
  const body = {
1202
- metrics: [data.message],
1561
+ metrics: [data],
1203
1562
  sent_at: now,
1204
1563
  };
1205
1564
  // This request is only made if telemetry is on.
@@ -1210,15 +1569,15 @@ async function sendTelemetry(sys, config, data) {
1210
1569
  },
1211
1570
  body: JSON.stringify(body),
1212
1571
  });
1213
- hasVerbose(config) &&
1214
- console.debug('\nSent %O metric to events service (status: %O)', data.message.name, response.status, '\n');
1572
+ hasVerbose(config.flags) &&
1573
+ console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1215
1574
  if (response.status !== 204) {
1216
- hasVerbose(config) &&
1575
+ hasVerbose(config.flags) &&
1217
1576
  console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1218
1577
  }
1219
1578
  }
1220
1579
  catch (e) {
1221
- hasVerbose(config) && console.debug('Telemetry request failed:', e);
1580
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1222
1581
  }
1223
1582
  }
1224
1583
  /**
@@ -1275,7 +1634,7 @@ const taskBuild = async (coreCompiler, config, sys) => {
1275
1634
  const results = await compiler.build();
1276
1635
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1277
1636
  if (sys) {
1278
- await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results);
1637
+ await telemetryBuildFinishedAction(sys, config, coreCompiler, results);
1279
1638
  }
1280
1639
  await compiler.destroy();
1281
1640
  if (results.hasError) {
@@ -1314,6 +1673,7 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1314
1673
  !!global.process &&
1315
1674
  typeof __filename === 'string' &&
1316
1675
  (!global.origin || typeof global.origin !== 'string');
1676
+ const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1317
1677
 
1318
1678
  /**
1319
1679
  * Task to generate component boilerplate and write it to disk. This task can
@@ -1362,7 +1722,10 @@ const taskGenerate = async (coreCompiler, config) => {
1362
1722
  if (!writtenFiles) {
1363
1723
  return config.sys.exit(1);
1364
1724
  }
1365
- // TODO(STENCIL-424): Investigate moving these console.log calls to config.logger.info
1725
+ // We use `console.log` here rather than our `config.logger` because we don't want
1726
+ // our TUI messages to be prefixed with timestamps and so on.
1727
+ //
1728
+ // See STENCIL-424 for details.
1366
1729
  console.log();
1367
1730
  console.log(`${config.logger.gray('$')} stencil generate ${input}`);
1368
1731
  console.log();
@@ -1506,7 +1869,8 @@ export class ${toPascalCase(tagName)} {
1506
1869
  `;
1507
1870
  };
1508
1871
  /**
1509
- * Get the boilerplate for style.
1872
+ * Get the boilerplate for style for a generated component
1873
+ * @returns a boilerplate CSS block
1510
1874
  */
1511
1875
  const getStyleUrlBoilerplate = () => `:host {
1512
1876
  display: block;
@@ -1560,10 +1924,17 @@ describe('${tagName}', () => {
1560
1924
  */
1561
1925
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1562
1926
 
1563
- const taskTelemetry = async (config, sys, logger) => {
1927
+ /**
1928
+ * Entrypoint for the Telemetry task
1929
+ * @param flags configuration flags provided to Stencil when a task was called (either this task or a task that invokes
1930
+ * telemetry)
1931
+ * @param sys the abstraction for interfacing with the operating system
1932
+ * @param logger a logging implementation to log the results out to the user
1933
+ */
1934
+ const taskTelemetry = async (flags, sys, logger) => {
1564
1935
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1565
- const isEnabling = config.flags.args.includes('on');
1566
- const isDisabling = config.flags.args.includes('off');
1936
+ const isEnabling = flags.args.includes('on');
1937
+ const isDisabling = flags.args.includes('off');
1567
1938
  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
1939
  const THANK_YOU = `Thank you for helping to make Stencil better! 💖`;
1569
1940
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
@@ -1592,7 +1963,14 @@ const taskTelemetry = async (config, sys, logger) => {
1592
1963
  `);
1593
1964
  };
1594
1965
 
1595
- const taskHelp = async (config, logger, sys) => {
1966
+ /**
1967
+ * Entrypoint for the Help task, providing Stencil usage context to the user
1968
+ * @param flags configuration flags provided to Stencil when a task was call (either this task or a task that invokes
1969
+ * telemetry)
1970
+ * @param logger a logging implementation to log the results out to the user
1971
+ * @param sys the abstraction for interfacing with the operating system
1972
+ */
1973
+ const taskHelp = async (flags, logger, sys) => {
1596
1974
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1597
1975
  console.log(`
1598
1976
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
@@ -1625,7 +2003,7 @@ const taskHelp = async (config, logger, sys) => {
1625
2003
  `);
1626
2004
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1627
2005
  if (sys) {
1628
- await taskTelemetry(config, sys, logger);
2006
+ await taskTelemetry(flags, sys, logger);
1629
2007
  }
1630
2008
  console.log(`
1631
2009
  ${logger.bold('Examples:')}
@@ -1682,6 +2060,10 @@ const taskServe = async (config) => {
1682
2060
  });
1683
2061
  };
1684
2062
 
2063
+ /**
2064
+ * Entrypoint for any Stencil tests
2065
+ * @param config a validated Stencil configuration entity
2066
+ */
1685
2067
  const taskTest = async (config) => {
1686
2068
  if (!IS_NODE_ENV) {
1687
2069
  config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
@@ -1728,6 +2110,95 @@ const taskTest = async (config) => {
1728
2110
  }
1729
2111
  };
1730
2112
 
2113
+ /**
2114
+ * Creates an instance of a logger
2115
+ * @returns the new logger instance
2116
+ */
2117
+ const createLogger = () => {
2118
+ let useColors = IS_BROWSER_ENV;
2119
+ let level = 'info';
2120
+ return {
2121
+ enableColors: (uc) => (useColors = uc),
2122
+ getLevel: () => level,
2123
+ setLevel: (l) => (level = l),
2124
+ emoji: (e) => e,
2125
+ info: console.log.bind(console),
2126
+ warn: console.warn.bind(console),
2127
+ error: console.error.bind(console),
2128
+ debug: console.debug.bind(console),
2129
+ red: (msg) => msg,
2130
+ green: (msg) => msg,
2131
+ yellow: (msg) => msg,
2132
+ blue: (msg) => msg,
2133
+ magenta: (msg) => msg,
2134
+ cyan: (msg) => msg,
2135
+ gray: (msg) => msg,
2136
+ bold: (msg) => msg,
2137
+ dim: (msg) => msg,
2138
+ bgRed: (msg) => msg,
2139
+ createTimeSpan: (_startMsg, _debug = false) => ({
2140
+ duration: () => 0,
2141
+ finish: () => 0,
2142
+ }),
2143
+ printDiagnostics(diagnostics) {
2144
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
2145
+ },
2146
+ };
2147
+ };
2148
+ const logDiagnostic = (diagnostic, useColors) => {
2149
+ let color = BLUE;
2150
+ let prefix = 'Build';
2151
+ let msg = '';
2152
+ if (diagnostic.level === 'error') {
2153
+ color = RED;
2154
+ prefix = 'Error';
2155
+ }
2156
+ else if (diagnostic.level === 'warn') {
2157
+ color = YELLOW;
2158
+ prefix = 'Warning';
2159
+ }
2160
+ if (diagnostic.header) {
2161
+ prefix = diagnostic.header;
2162
+ }
2163
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
2164
+ if (filePath) {
2165
+ msg += filePath;
2166
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
2167
+ msg += ', line ' + diagnostic.lineNumber;
2168
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
2169
+ msg += ', column ' + diagnostic.columnNumber;
2170
+ }
2171
+ }
2172
+ msg += '\n';
2173
+ }
2174
+ msg += diagnostic.messageText;
2175
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
2176
+ diagnostic.lines.forEach((l) => {
2177
+ msg += '\n' + l.lineNumber + ': ' + l.text;
2178
+ });
2179
+ msg += '\n';
2180
+ }
2181
+ if (useColors) {
2182
+ const styledPrefix = [
2183
+ '%c' + prefix,
2184
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
2185
+ ];
2186
+ console.log(...styledPrefix, msg);
2187
+ }
2188
+ else if (diagnostic.level === 'error') {
2189
+ console.error(msg);
2190
+ }
2191
+ else if (diagnostic.level === 'warn') {
2192
+ console.warn(msg);
2193
+ }
2194
+ else {
2195
+ console.log(msg);
2196
+ }
2197
+ };
2198
+ const YELLOW = `#f39c12`;
2199
+ const RED = `#c0392b`;
2200
+ const BLUE = `#3498db`;
2201
+
1731
2202
  const run = async (init) => {
1732
2203
  const { args, logger, sys } = init;
1733
2204
  try {
@@ -1743,7 +2214,7 @@ const run = async (init) => {
1743
2214
  sys.applyGlobalPatch(sys.getCurrentDirectory());
1744
2215
  }
1745
2216
  if (task === 'help' || flags.help) {
1746
- await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys);
2217
+ await taskHelp({ task: 'help', args }, logger, sys);
1747
2218
  return;
1748
2219
  }
1749
2220
  startupLog(logger, task);
@@ -1769,7 +2240,7 @@ const run = async (init) => {
1769
2240
  startupLogVersion(logger, task, coreCompiler);
1770
2241
  loadedCompilerLog(sys, logger, flags, coreCompiler);
1771
2242
  if (task === 'info') {
1772
- await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => {
2243
+ await telemetryAction(sys, { flags: { task: 'info' }, logger }, coreCompiler, async () => {
1773
2244
  await taskInfo(coreCompiler, sys, logger);
1774
2245
  });
1775
2246
  return;
@@ -1792,7 +2263,7 @@ const run = async (init) => {
1792
2263
  sys.applyGlobalPatch(validated.config.rootDir);
1793
2264
  }
1794
2265
  await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
1795
- await telemetryAction(sys, validated.config, logger, coreCompiler, async () => {
2266
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
1796
2267
  await runTask(coreCompiler, validated.config, task, sys);
1797
2268
  });
1798
2269
  }
@@ -1805,43 +2276,45 @@ const run = async (init) => {
1805
2276
  }
1806
2277
  };
1807
2278
  const runTask = async (coreCompiler, config, task, sys) => {
1808
- config.flags = config.flags || { task };
1809
- config.outputTargets = config.outputTargets || [];
2279
+ var _a, _b;
2280
+ const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2281
+ const strictConfig = { ...config, flags: (_b = { ...config.flags }) !== null && _b !== void 0 ? _b : { task }, logger };
2282
+ strictConfig.outputTargets = strictConfig.outputTargets || [];
1810
2283
  switch (task) {
1811
2284
  case 'build':
1812
- await taskBuild(coreCompiler, config, sys);
2285
+ await taskBuild(coreCompiler, strictConfig, sys);
1813
2286
  break;
1814
2287
  case 'docs':
1815
- await taskDocs(coreCompiler, config);
2288
+ await taskDocs(coreCompiler, strictConfig);
1816
2289
  break;
1817
2290
  case 'generate':
1818
2291
  case 'g':
1819
- await taskGenerate(coreCompiler, config);
2292
+ await taskGenerate(coreCompiler, strictConfig);
1820
2293
  break;
1821
2294
  case 'help':
1822
- await taskHelp(config, config.logger, sys);
2295
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1823
2296
  break;
1824
2297
  case 'prerender':
1825
- await taskPrerender(coreCompiler, config);
2298
+ await taskPrerender(coreCompiler, strictConfig);
1826
2299
  break;
1827
2300
  case 'serve':
1828
- await taskServe(config);
2301
+ await taskServe(strictConfig);
1829
2302
  break;
1830
2303
  case 'telemetry':
1831
2304
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1832
2305
  if (sys) {
1833
- await taskTelemetry(config, sys, config.logger);
2306
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
1834
2307
  }
1835
2308
  break;
1836
2309
  case 'test':
1837
- await taskTest(config);
2310
+ await taskTest(strictConfig);
1838
2311
  break;
1839
2312
  case 'version':
1840
2313
  console.log(coreCompiler.version);
1841
2314
  break;
1842
2315
  default:
1843
- config.logger.error(`${config.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
1844
- await taskHelp(config, config.logger, sys);
2316
+ strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
2317
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1845
2318
  return config.sys.exit(1);
1846
2319
  }
1847
2320
  };