@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.js CHANGED
@@ -1,8 +1,50 @@
1
1
  /*!
2
- Stencil CLI v2.16.1-0 | MIT Licensed | https://stenciljs.com
2
+ Stencil CLI v2.17.1 | MIT Licensed | https://stenciljs.com
3
3
  */
4
- const toLowerCase = (str) => str.toLowerCase();
5
- const dashToPascalCase = (str) => toLowerCase(str)
4
+ /**
5
+ * This sets the log level hierarchy for our terminal logger, ranging from
6
+ * most to least verbose.
7
+ *
8
+ * Ordering the levels like this lets us easily check whether we should log a
9
+ * message at a given time. For instance, if the log level is set to `'warn'`,
10
+ * then anything passed to the logger with level `'warn'` or `'error'` should
11
+ * be logged, but we should _not_ log anything with level `'info'` or `'debug'`.
12
+ *
13
+ * If we have a current log level `currentLevel` and a message with level
14
+ * `msgLevel` is passed to the logger, we can determine whether or not we should
15
+ * log it by checking if the log level on the message is further up or at the
16
+ * same level in the hierarchy than `currentLevel`, like so:
17
+ *
18
+ * ```ts
19
+ * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel)
20
+ * ```
21
+ *
22
+ * NOTE: for the reasons described above, do not change the order of the entries
23
+ * in this array without good reason!
24
+ */
25
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
26
+
27
+ /**
28
+ * Convert a string from PascalCase to dash-case
29
+ *
30
+ * @param str the string to convert
31
+ * @returns a converted string
32
+ */
33
+ const toDashCase = (str) => str
34
+ .replace(/([A-Z0-9])/g, (match) => ` ${match[0]}`)
35
+ .trim()
36
+ .split(' ')
37
+ .join('-')
38
+ .toLowerCase();
39
+ /**
40
+ * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase,
41
+ * or whatever you call it!)
42
+ *
43
+ * @param str a string to convert
44
+ * @returns a converted string
45
+ */
46
+ const dashToPascalCase = (str) => str
47
+ .toLowerCase()
6
48
  .split('-')
7
49
  .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
8
50
  .join('');
@@ -275,22 +317,211 @@ const validateComponentTag = (tag) => {
275
317
  return undefined;
276
318
  };
277
319
 
320
+ /**
321
+ * All the Boolean options supported by the Stencil CLI
322
+ */
323
+ const BOOLEAN_CLI_ARGS = [
324
+ 'build',
325
+ 'cache',
326
+ 'checkVersion',
327
+ 'ci',
328
+ 'compare',
329
+ 'debug',
330
+ 'dev',
331
+ 'devtools',
332
+ 'docs',
333
+ 'e2e',
334
+ 'es5',
335
+ 'esm',
336
+ 'headless',
337
+ 'help',
338
+ 'log',
339
+ 'open',
340
+ 'prerender',
341
+ 'prerenderExternal',
342
+ 'prod',
343
+ 'profile',
344
+ 'serviceWorker',
345
+ 'screenshot',
346
+ 'serve',
347
+ 'skipNodeCheck',
348
+ 'spec',
349
+ 'ssr',
350
+ 'stats',
351
+ 'updateScreenshot',
352
+ 'verbose',
353
+ 'version',
354
+ 'watch',
355
+ // JEST CLI OPTIONS
356
+ 'all',
357
+ 'automock',
358
+ 'bail',
359
+ // 'cache', Stencil already supports this argument
360
+ 'changedFilesWithAncestor',
361
+ // 'ci', Stencil already supports this argument
362
+ 'clearCache',
363
+ 'clearMocks',
364
+ 'collectCoverage',
365
+ 'color',
366
+ 'colors',
367
+ 'coverage',
368
+ // 'debug', Stencil already supports this argument
369
+ 'detectLeaks',
370
+ 'detectOpenHandles',
371
+ 'errorOnDeprecated',
372
+ 'expand',
373
+ 'findRelatedTests',
374
+ 'forceExit',
375
+ 'init',
376
+ 'injectGlobals',
377
+ 'json',
378
+ 'lastCommit',
379
+ 'listTests',
380
+ 'logHeapUsage',
381
+ 'noStackTrace',
382
+ 'notify',
383
+ 'onlyChanged',
384
+ 'onlyFailures',
385
+ 'passWithNoTests',
386
+ 'resetMocks',
387
+ 'resetModules',
388
+ 'restoreMocks',
389
+ 'runInBand',
390
+ 'runTestsByPath',
391
+ 'showConfig',
392
+ 'silent',
393
+ 'skipFilter',
394
+ 'testLocationInResults',
395
+ 'updateSnapshot',
396
+ 'useStderr',
397
+ // 'verbose', Stencil already supports this argument
398
+ // 'version', Stencil already supports this argument
399
+ // 'watch', Stencil already supports this argument
400
+ 'watchAll',
401
+ 'watchman',
402
+ ];
403
+ /**
404
+ * All the Number options supported by the Stencil CLI
405
+ */
406
+ const NUMBER_CLI_ARGS = [
407
+ 'port',
408
+ // JEST CLI ARGS
409
+ 'maxConcurrency',
410
+ 'testTimeout',
411
+ ];
412
+ /**
413
+ * All the String options supported by the Stencil CLI
414
+ */
415
+ const STRING_CLI_ARGS = [
416
+ 'address',
417
+ 'config',
418
+ 'docsApi',
419
+ 'docsJson',
420
+ 'emulate',
421
+ 'root',
422
+ 'screenshotConnector',
423
+ // JEST CLI ARGS
424
+ 'cacheDirectory',
425
+ 'changedSince',
426
+ 'collectCoverageFrom',
427
+ // 'config', Stencil already supports this argument
428
+ 'coverageDirectory',
429
+ 'coverageThreshold',
430
+ 'env',
431
+ 'filter',
432
+ 'globalSetup',
433
+ 'globalTeardown',
434
+ 'globals',
435
+ 'haste',
436
+ 'moduleNameMapper',
437
+ 'notifyMode',
438
+ 'outputFile',
439
+ 'preset',
440
+ 'prettierPath',
441
+ 'resolver',
442
+ 'rootDir',
443
+ 'runner',
444
+ 'testEnvironment',
445
+ 'testEnvironmentOptions',
446
+ 'testFailureExitCode',
447
+ 'testNamePattern',
448
+ 'testResultsProcessor',
449
+ 'testRunner',
450
+ 'testSequencer',
451
+ 'testURL',
452
+ 'timers',
453
+ 'transform',
454
+ // ARRAY ARGS
455
+ 'collectCoverageOnlyFrom',
456
+ 'coveragePathIgnorePatterns',
457
+ 'coverageReporters',
458
+ 'moduleDirectories',
459
+ 'moduleFileExtensions',
460
+ 'modulePathIgnorePatterns',
461
+ 'modulePaths',
462
+ 'projects',
463
+ 'reporters',
464
+ 'roots',
465
+ 'selectProjects',
466
+ 'setupFiles',
467
+ 'setupFilesAfterEnv',
468
+ 'snapshotSerializers',
469
+ 'testMatch',
470
+ 'testPathIgnorePatterns',
471
+ 'testPathPattern',
472
+ 'testRegex',
473
+ 'transformIgnorePatterns',
474
+ 'unmockedModulePathPatterns',
475
+ 'watchPathIgnorePatterns',
476
+ ];
477
+ /**
478
+ * All the CLI arguments which may have string or number values
479
+ *
480
+ * `maxWorkers` is an argument which is used both by Stencil _and_ by Jest,
481
+ * which means that we need to support parsing both string and number values.
482
+ */
483
+ const STRING_NUMBER_CLI_ARGS = ['maxWorkers'];
484
+ /**
485
+ * All the LogLevel-type options supported by the Stencil CLI
486
+ *
487
+ * This is a bit silly since there's only one such argument atm,
488
+ * but this approach lets us make sure that we're handling all
489
+ * our arguments in a type-safe way.
490
+ */
491
+ const LOG_LEVEL_CLI_ARGS = ['logLevel'];
492
+ /**
493
+ * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'`
494
+ */
495
+ const CLI_ARG_ALIASES = {
496
+ config: 'c',
497
+ help: 'h',
498
+ port: 'p',
499
+ version: 'v',
500
+ };
501
+
502
+ /**
503
+ * Parse command line arguments into a structured `ConfigFlags` object
504
+ *
505
+ * @param args an array of config flags
506
+ * @param sys an optional compiler system
507
+ * @returns a structured ConfigFlags object
508
+ */
278
509
  const parseFlags = (args, sys) => {
279
510
  const flags = {
280
511
  task: null,
281
512
  args: [],
282
513
  knownArgs: [],
283
- unknownArgs: null,
514
+ unknownArgs: [],
284
515
  };
285
516
  // cmd line has more priority over npm scripts cmd
286
- flags.args = args.slice();
517
+ flags.args = Array.isArray(args) ? args.slice() : [];
287
518
  if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
288
519
  flags.task = flags.args[0];
289
520
  }
290
- parseArgs(flags, flags.args, flags.knownArgs);
521
+ parseArgs(flags, flags.args);
291
522
  if (sys && sys.name === 'node') {
292
523
  const envArgs = getNpmConfigEnvArgs(sys);
293
- parseArgs(flags, envArgs, flags.knownArgs);
524
+ parseArgs(flags, envArgs);
294
525
  envArgs.forEach((envArg) => {
295
526
  if (!flags.args.includes(envArg)) {
296
527
  flags.args.push(envArg);
@@ -309,185 +540,230 @@ const parseFlags = (args, sys) => {
309
540
  return flags;
310
541
  };
311
542
  /**
312
- * Parse command line arguments that are whitelisted via the BOOLEAN_ARG_OPTS,
313
- * STRING_ARG_OPTS, and NUMBER_ARG_OPTS arrays in this file. Handles leading
314
- * dashes on arguments, aliases that are defined for a small number of argument
315
- * types, and parsing values for non-boolean arguments (e.g. port number).
543
+ * Parse command line arguments that are enumerated in the `config-flags`
544
+ * module. Handles leading dashes on arguments, aliases that are defined for a
545
+ * small number of arguments, and parsing values for non-boolean arguments
546
+ * (e.g. port number for the dev server).
547
+ *
548
+ * @param flags a ConfigFlags object to which parsed arguments will be added
549
+ * @param args an array of command-line arguments to parse
550
+ */
551
+ const parseArgs = (flags, args) => {
552
+ BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
553
+ STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
554
+ NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
555
+ STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
556
+ LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
557
+ };
558
+ /**
559
+ * Parse a boolean CLI argument. For these, we support the following formats:
560
+ *
561
+ * - `--booleanArg`
562
+ * - `--boolean-arg`
563
+ * - `--noBooleanArg`
564
+ * - `--no-boolean-arg`
316
565
  *
317
- * @param flags a ConfigFlags object
318
- * @param args an array of command-line arguments to parse
319
- * @param knownArgs an array to which all recognized, legal arguments are added
320
- */
321
- const parseArgs = (flags, args, knownArgs) => {
322
- BOOLEAN_ARG_OPTS.forEach((booleanName) => {
323
- const alias = ARG_OPTS_ALIASES[booleanName];
324
- const flagKey = configCase(booleanName);
325
- if (typeof flags[flagKey] !== 'boolean') {
326
- flags[flagKey] = null;
566
+ * The final two variants should be parsed to a value of `false` on the config
567
+ * object.
568
+ *
569
+ * @param flags the config flags object, while we'll modify
570
+ * @param args our CLI arguments
571
+ * @param configCaseName the argument we want to look at right now
572
+ */
573
+ const parseBooleanArg = (flags, args, configCaseName) => {
574
+ // we support both dash-case and PascalCase versions of the parameter
575
+ // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS
576
+ const alias = CLI_ARG_ALIASES[configCaseName];
577
+ const dashCaseName = toDashCase(configCaseName);
578
+ if (typeof flags[configCaseName] !== 'boolean') {
579
+ flags[configCaseName] = null;
580
+ }
581
+ args.forEach((cmdArg) => {
582
+ let value;
583
+ if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) {
584
+ value = true;
327
585
  }
328
- args.forEach((cmdArg) => {
329
- if (cmdArg === `--${booleanName}`) {
330
- flags[flagKey] = true;
331
- knownArgs.push(cmdArg);
332
- }
333
- else if (cmdArg === `--${flagKey}`) {
334
- flags[flagKey] = true;
335
- knownArgs.push(cmdArg);
336
- }
337
- else if (cmdArg === `--no-${booleanName}`) {
338
- flags[flagKey] = false;
339
- knownArgs.push(cmdArg);
340
- }
341
- else if (cmdArg === `--no${dashToPascalCase(booleanName)}`) {
342
- flags[flagKey] = false;
343
- knownArgs.push(cmdArg);
344
- }
345
- else if (alias && cmdArg === `-${alias}`) {
346
- flags[flagKey] = true;
347
- knownArgs.push(cmdArg);
348
- }
349
- });
350
- });
351
- STRING_ARG_OPTS.forEach((stringName) => {
352
- const alias = ARG_OPTS_ALIASES[stringName];
353
- const flagKey = configCase(stringName);
354
- if (typeof flags[flagKey] !== 'string') {
355
- flags[flagKey] = null;
586
+ else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) {
587
+ value = false;
356
588
  }
357
- for (let i = 0; i < args.length; i++) {
358
- const cmdArg = args[i];
359
- if (cmdArg.startsWith(`--${stringName}=`)) {
360
- const values = cmdArg.split('=');
361
- values.shift();
362
- flags[flagKey] = values.join('=');
363
- knownArgs.push(cmdArg);
364
- }
365
- else if (cmdArg === `--${stringName}`) {
366
- flags[flagKey] = args[i + 1];
367
- knownArgs.push(cmdArg);
368
- knownArgs.push(args[i + 1]);
369
- }
370
- else if (cmdArg === `--${flagKey}`) {
371
- flags[flagKey] = args[i + 1];
372
- knownArgs.push(cmdArg);
373
- knownArgs.push(args[i + 1]);
374
- }
375
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
376
- const values = cmdArg.split('=');
377
- values.shift();
378
- flags[flagKey] = values.join('=');
379
- knownArgs.push(cmdArg);
380
- }
381
- else if (alias) {
382
- if (cmdArg.startsWith(`-${alias}=`)) {
383
- const values = cmdArg.split('=');
384
- values.shift();
385
- flags[flagKey] = values.join('=');
386
- knownArgs.push(cmdArg);
387
- }
388
- else if (cmdArg === `-${alias}`) {
389
- flags[flagKey] = args[i + 1];
390
- knownArgs.push(args[i + 1]);
391
- }
392
- }
589
+ else if (alias && cmdArg === `-${alias}`) {
590
+ value = true;
591
+ }
592
+ if (value !== undefined && cmdArg !== undefined) {
593
+ flags[configCaseName] = value;
594
+ flags.knownArgs.push(cmdArg);
393
595
  }
394
596
  });
395
- NUMBER_ARG_OPTS.forEach((numberName) => {
396
- const alias = ARG_OPTS_ALIASES[numberName];
397
- const flagKey = configCase(numberName);
398
- if (typeof flags[flagKey] !== 'number') {
399
- flags[flagKey] = null;
597
+ };
598
+ /**
599
+ * Parse a string CLI argument
600
+ *
601
+ * @param flags the config flags object, while we'll modify
602
+ * @param args our CLI arguments
603
+ * @param configCaseName the argument we want to look at right now
604
+ */
605
+ const parseStringArg = (flags, args, configCaseName) => {
606
+ if (typeof flags[configCaseName] !== 'string') {
607
+ flags[configCaseName] = null;
608
+ }
609
+ const { value, matchingArg } = getValue(args, configCaseName);
610
+ if (value !== undefined && matchingArg !== undefined) {
611
+ flags[configCaseName] = value;
612
+ flags.knownArgs.push(matchingArg);
613
+ flags.knownArgs.push(value);
614
+ }
615
+ };
616
+ /**
617
+ * Parse a number CLI argument
618
+ *
619
+ * @param flags the config flags object, while we'll modify
620
+ * @param args our CLI arguments
621
+ * @param configCaseName the argument we want to look at right now
622
+ */
623
+ const parseNumberArg = (flags, args, configCaseName) => {
624
+ if (typeof flags[configCaseName] !== 'number') {
625
+ flags[configCaseName] = null;
626
+ }
627
+ const { value, matchingArg } = getValue(args, configCaseName);
628
+ if (value !== undefined && matchingArg !== undefined) {
629
+ flags[configCaseName] = parseInt(value, 10);
630
+ flags.knownArgs.push(matchingArg);
631
+ flags.knownArgs.push(value);
632
+ }
633
+ };
634
+ /**
635
+ * Parse a CLI argument which may be either a string or a number
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 parseStringNumberArg = (flags, args, configCaseName) => {
642
+ if (!['number', 'string'].includes(typeof flags[configCaseName])) {
643
+ flags[configCaseName] = null;
644
+ }
645
+ const { value, matchingArg } = getValue(args, configCaseName);
646
+ if (value !== undefined && matchingArg !== undefined) {
647
+ if (CLI_ARG_STRING_REGEX.test(value)) {
648
+ // if it matches the regex we treat it like a string
649
+ flags[configCaseName] = value;
400
650
  }
401
- for (let i = 0; i < args.length; i++) {
402
- const cmdArg = args[i];
403
- if (cmdArg.startsWith(`--${numberName}=`)) {
404
- const values = cmdArg.split('=');
405
- values.shift();
406
- flags[flagKey] = parseInt(values.join(''), 10);
407
- knownArgs.push(cmdArg);
408
- }
409
- else if (cmdArg === `--${numberName}`) {
410
- flags[flagKey] = parseInt(args[i + 1], 10);
411
- knownArgs.push(args[i + 1]);
412
- }
413
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
414
- const values = cmdArg.split('=');
415
- values.shift();
416
- flags[flagKey] = parseInt(values.join(''), 10);
417
- knownArgs.push(cmdArg);
418
- }
419
- else if (cmdArg === `--${flagKey}`) {
420
- flags[flagKey] = parseInt(args[i + 1], 10);
421
- knownArgs.push(args[i + 1]);
651
+ else {
652
+ // it was a number, great!
653
+ flags[configCaseName] = Number(value);
654
+ }
655
+ flags.knownArgs.push(matchingArg);
656
+ flags.knownArgs.push(value);
657
+ }
658
+ };
659
+ /**
660
+ * We use this regular expression to detect CLI parameters which
661
+ * should be parsed as string values (as opposed to numbers) for
662
+ * the argument types for which we support both a string and a
663
+ * number value.
664
+ *
665
+ * The regex tests for the presence of at least one character which is
666
+ * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
667
+ * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
668
+ * support the admittedly unlikely use of scientific notation, like `"4e+0"`
669
+ * for `4`).
670
+ *
671
+ * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
672
+ * `"5.0"`. If it matches a given string we conclude that the string should
673
+ * be parsed as a string literal, rather than using `Number` to convert it
674
+ * to a number.
675
+ */
676
+ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
677
+ /**
678
+ * Parse a LogLevel CLI argument. These can be only a specific
679
+ * set of strings, so this function takes care of validating that
680
+ * the value is correct.
681
+ *
682
+ * @param flags the config flags object, while we'll modify
683
+ * @param args our CLI arguments
684
+ * @param configCaseName the argument we want to look at right now
685
+ */
686
+ const parseLogLevelArg = (flags, args, configCaseName) => {
687
+ if (typeof flags[configCaseName] !== 'string') {
688
+ flags[configCaseName] = null;
689
+ }
690
+ const { value, matchingArg } = getValue(args, configCaseName);
691
+ if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) {
692
+ flags[configCaseName] = value;
693
+ flags.knownArgs.push(matchingArg);
694
+ flags.knownArgs.push(value);
695
+ }
696
+ };
697
+ /**
698
+ * Helper for pulling values out from the raw array of CLI arguments. This logic
699
+ * is shared between a few different types of CLI args.
700
+ *
701
+ * We look for arguments in the following formats:
702
+ *
703
+ * - `--my-cli-argument value`
704
+ * - `--my-cli-argument=value`
705
+ * - `--myCliArgument value`
706
+ * - `--myCliArgument=value`
707
+ *
708
+ * We also check for shortened aliases, which we define for a few arguments.
709
+ *
710
+ * @param args the CLI args we're dealing with
711
+ * @param configCaseName the ConfigFlag key which we're looking to pull out a value for
712
+ * @returns the value for the flag as well as the exact string which it matched from
713
+ * the user input.
714
+ */
715
+ const getValue = (args, configCaseName) => {
716
+ // for some CLI args we have a short alias, like 'c' for 'config'
717
+ const alias = CLI_ARG_ALIASES[configCaseName];
718
+ // we support supplying arguments in both dash-case and configCase
719
+ // for ease of use
720
+ const dashCaseName = toDashCase(configCaseName);
721
+ let value;
722
+ let matchingArg;
723
+ args.forEach((arg, i) => {
724
+ if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) {
725
+ value = getEqualsValue(arg);
726
+ matchingArg = arg;
727
+ }
728
+ else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) {
729
+ value = args[i + 1];
730
+ matchingArg = arg;
731
+ }
732
+ else if (alias) {
733
+ if (arg.startsWith(`-${alias}=`)) {
734
+ value = getEqualsValue(arg);
735
+ matchingArg = arg;
422
736
  }
423
- else if (alias) {
424
- if (cmdArg.startsWith(`-${alias}=`)) {
425
- const values = cmdArg.split('=');
426
- values.shift();
427
- flags[flagKey] = parseInt(values.join(''), 10);
428
- knownArgs.push(cmdArg);
429
- }
430
- else if (cmdArg === `-${alias}`) {
431
- flags[flagKey] = parseInt(args[i + 1], 10);
432
- knownArgs.push(args[i + 1]);
433
- }
737
+ else if (arg === `-${alias}`) {
738
+ value = args[i + 1];
739
+ matchingArg = arg;
434
740
  }
435
741
  }
436
742
  });
743
+ return { value, matchingArg };
437
744
  };
438
- const configCase = (prop) => {
439
- prop = dashToPascalCase(prop);
440
- return prop.charAt(0).toLowerCase() + prop.slice(1);
441
- };
442
- const BOOLEAN_ARG_OPTS = [
443
- 'build',
444
- 'cache',
445
- 'check-version',
446
- 'ci',
447
- 'compare',
448
- 'debug',
449
- 'dev',
450
- 'devtools',
451
- 'docs',
452
- 'e2e',
453
- 'es5',
454
- 'esm',
455
- 'headless',
456
- 'help',
457
- 'log',
458
- 'open',
459
- 'prerender',
460
- 'prerender-external',
461
- 'prod',
462
- 'profile',
463
- 'service-worker',
464
- 'screenshot',
465
- 'serve',
466
- 'skip-node-check',
467
- 'spec',
468
- 'ssr',
469
- 'stats',
470
- 'update-screenshot',
471
- 'verbose',
472
- 'version',
473
- 'watch',
474
- ];
475
- const NUMBER_ARG_OPTS = ['max-workers', 'port'];
476
- const STRING_ARG_OPTS = [
477
- 'address',
478
- 'config',
479
- 'docs-json',
480
- 'emulate',
481
- 'log-level',
482
- 'root',
483
- 'screenshot-connector',
484
- ];
485
- const ARG_OPTS_ALIASES = {
486
- config: 'c',
487
- help: 'h',
488
- port: 'p',
489
- version: 'v',
490
- };
745
+ /**
746
+ * When a parameter is set in the format `--foobar=12` at the CLI (as opposed to
747
+ * `--foobar 12`) we want to get the value after the `=` sign
748
+ *
749
+ * @param commandArgument the arg in question
750
+ * @returns the value after the `=`
751
+ */
752
+ const getEqualsValue = (commandArgument) => commandArgument.split('=').slice(1).join('=');
753
+ /**
754
+ * Small helper for getting type-system-level assurance that a `string` can be
755
+ * narrowed to a `LogLevel`
756
+ *
757
+ * @param maybeLogLevel the string to check
758
+ * @returns whether this is a `LogLevel`
759
+ */
760
+ const isLogLevel = (maybeLogLevel) =>
761
+ // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
762
+ // boolean` so a `string` cannot be passed to `includes` on a
763
+ // `ReadonlyArray` 😢 thus we `as any`
764
+ //
765
+ // see microsoft/TypeScript#31018 for some discussion of this
766
+ LOG_LEVELS.includes(maybeLogLevel);
491
767
  const getNpmConfigEnvArgs = (sys) => {
492
768
  // process.env.npm_config_argv
493
769
  // {"remain":["4444"],"cooked":["run","serve","--port","4444"],"original":["run","serve","--port","4444"]}
@@ -508,7 +784,7 @@ const getNpmConfigEnvArgs = (sys) => {
508
784
  const dependencies = [
509
785
  {
510
786
  name: "@stencil/core",
511
- version: "2.16.1-0",
787
+ version: "2.17.1",
512
788
  main: "compiler/stencil.js",
513
789
  resources: [
514
790
  "package.json",
@@ -833,12 +1109,11 @@ const tryFn = async (fn, ...args) => {
833
1109
  }
834
1110
  return null;
835
1111
  };
836
- const isInteractive = (sys, config, object) => {
837
- var _a;
1112
+ const isInteractive = (sys, flags, object) => {
838
1113
  const terminalInfo = object ||
839
1114
  Object.freeze({
840
1115
  tty: sys.isTTY() ? true : false,
841
- 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),
1116
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!flags.ci,
842
1117
  });
843
1118
  return terminalInfo.tty && !terminalInfo.ci;
844
1119
  };
@@ -863,19 +1138,19 @@ async function readJson(sys, path) {
863
1138
  }
864
1139
  /**
865
1140
  * Does the command have the debug flag?
866
- * @param config The config passed into the Stencil command
1141
+ * @param flags The configuration flags passed into the Stencil command
867
1142
  * @returns true if --debug has been passed, otherwise false
868
1143
  */
869
- function hasDebug(config) {
870
- return config.flags.debug;
1144
+ function hasDebug(flags) {
1145
+ return flags.debug;
871
1146
  }
872
1147
  /**
873
1148
  * Does the command have the verbose and debug flags?
874
- * @param config The config passed into the Stencil command
1149
+ * @param flags The configuration flags passed into the Stencil command
875
1150
  * @returns true if both --debug and --verbose have been passed, otherwise false
876
1151
  */
877
- function hasVerbose(config) {
878
- return config.flags.verbose && hasDebug(config);
1152
+ function hasVerbose(flags) {
1153
+ return flags.verbose && hasDebug(flags);
879
1154
  }
880
1155
 
881
1156
  /**
@@ -886,7 +1161,7 @@ function hasVerbose(config) {
886
1161
  * @returns true if telemetry should be sent, false otherwise
887
1162
  */
888
1163
  async function shouldTrack(config, sys, ci) {
889
- return !ci && isInteractive(sys, config) && (await checkTelemetry(sys));
1164
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
890
1165
  }
891
1166
 
892
1167
  const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
@@ -943,7 +1218,9 @@ async function updateConfig(sys, newOptions) {
943
1218
  return await writeConfig(sys, Object.assign(config, newOptions));
944
1219
  }
945
1220
 
1221
+ const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
946
1222
  const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1223
+ const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
947
1224
  const DOCS_CUSTOM = 'docs-custom';
948
1225
  const DOCS_JSON = 'docs-json';
949
1226
  const DOCS_README = 'docs-readme';
@@ -955,11 +1232,10 @@ const WWW = 'www';
955
1232
  *
956
1233
  * @param sys The system where the command is invoked
957
1234
  * @param config The config passed into the Stencil command
958
- * @param logger The tool used to do logging
959
1235
  * @param coreCompiler The compiler used to do builds
960
1236
  * @param result The results of a compiler build.
961
1237
  */
962
- async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) {
1238
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
963
1239
  const tracking = await shouldTrack(config, sys, config.flags.ci);
964
1240
  if (!tracking) {
965
1241
  return;
@@ -967,20 +1243,19 @@ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, r
967
1243
  const component_count = Object.keys(result.componentGraph).length;
968
1244
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
969
1245
  await sendMetric(sys, config, 'stencil_cli_command', data);
970
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1246
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
971
1247
  }
972
1248
  /**
973
1249
  * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
1250
+ *
974
1251
  * @param sys The system where the command is invoked
975
1252
  * @param config The config passed into the Stencil command
976
- * @param logger The tool used to do logging
977
1253
  * @param coreCompiler The compiler used to do builds
978
1254
  * @param action A Promise-based function to call in order to get the duration of any given command.
979
1255
  * @returns void
980
1256
  */
981
- async function telemetryAction(sys, config, logger, coreCompiler, action) {
982
- var _a;
983
- const tracking = await shouldTrack(config, sys, !!((_a = config === null || config === void 0 ? void 0 : config.flags) === null || _a === void 0 ? void 0 : _a.ci));
1257
+ async function telemetryAction(sys, config, coreCompiler, action) {
1258
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
984
1259
  let duration = undefined;
985
1260
  let error;
986
1261
  if (action) {
@@ -1000,7 +1275,7 @@ async function telemetryAction(sys, config, logger, coreCompiler, action) {
1000
1275
  }
1001
1276
  const data = await prepareData(coreCompiler, config, sys, duration);
1002
1277
  await sendMetric(sys, config, 'stencil_cli_command', data);
1003
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1278
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1004
1279
  if (error) {
1005
1280
  throw error;
1006
1281
  }
@@ -1016,6 +1291,16 @@ async function getActiveTargets(config) {
1016
1291
  const result = config.outputTargets.map((t) => t.type);
1017
1292
  return Array.from(new Set(result));
1018
1293
  }
1294
+ /**
1295
+ * Prepare data for telemetry
1296
+ *
1297
+ * @param coreCompiler the core compiler
1298
+ * @param config the current Stencil config
1299
+ * @param sys the compiler system instance in use
1300
+ * @param duration_ms the duration of the action being tracked
1301
+ * @param component_count the number of components being built (optional)
1302
+ * @returns a Promise wrapping data for the telemetry endpoint
1303
+ */
1019
1304
  const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1020
1305
  const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1021
1306
  const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
@@ -1028,6 +1313,7 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1028
1313
  const cpu_model = sys.details.cpuModel;
1029
1314
  const build = coreCompiler.buildId || 'unknown';
1030
1315
  const has_app_pwa_config = hasAppTarget(config);
1316
+ const anonymizedConfig = anonymizeConfigForTelemetry(config);
1031
1317
  return {
1032
1318
  yarn,
1033
1319
  duration_ms,
@@ -1047,12 +1333,83 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1047
1333
  typescript,
1048
1334
  rollup,
1049
1335
  has_app_pwa_config,
1336
+ config: anonymizedConfig,
1050
1337
  };
1051
1338
  };
1339
+ // props in output targets for which we retain their original values when
1340
+ // preparing a config for telemetry
1341
+ //
1342
+ // we omit the values of all other fields on output targets.
1343
+ const OUTPUT_TARGET_KEYS_TO_KEEP = ['type'];
1344
+ // top-level config props that we anonymize for telemetry
1345
+ const CONFIG_PROPS_TO_ANONYMIZE = [
1346
+ 'rootDir',
1347
+ 'fsNamespace',
1348
+ 'packageJsonFilePath',
1349
+ 'namespace',
1350
+ 'srcDir',
1351
+ 'srcIndexHtml',
1352
+ 'buildLogFilePath',
1353
+ 'cacheDir',
1354
+ 'configPath',
1355
+ 'tsconfig',
1356
+ ];
1357
+ // Props we delete entirely from the config for telemetry
1358
+ //
1359
+ // TODO(STENCIL-469): Investigate improving anonymization for tsCompilerOptions and devServer
1360
+ const CONFIG_PROPS_TO_DELETE = ['sys', 'logger', 'tsCompilerOptions', 'devServer'];
1361
+ /**
1362
+ * Anonymize the config for telemetry, replacing potentially revealing config props
1363
+ * with a placeholder string if they are present (this lets us still track how frequently
1364
+ * these config options are being used)
1365
+ *
1366
+ * @param config the config to anonymize
1367
+ * @returns an anonymized copy of the same config
1368
+ */
1369
+ const anonymizeConfigForTelemetry = (config) => {
1370
+ var _a;
1371
+ const anonymizedConfig = { ...config };
1372
+ for (const prop of CONFIG_PROPS_TO_ANONYMIZE) {
1373
+ if (anonymizedConfig[prop] !== undefined) {
1374
+ anonymizedConfig[prop] = 'omitted';
1375
+ }
1376
+ }
1377
+ anonymizedConfig.outputTargets = ((_a = config.outputTargets) !== null && _a !== void 0 ? _a : []).map((target) => {
1378
+ // Anonymize the outputTargets on our configuration, taking advantage of the
1379
+ // optional 2nd argument to `JSON.stringify`. If anything is not a string
1380
+ // we retain it so that any nested properties are handled, else we check
1381
+ // whether it's in our 'keep' list to decide whether to keep it or replace it
1382
+ // with `"omitted"`.
1383
+ const anonymizedOT = JSON.parse(JSON.stringify(target, (key, value) => {
1384
+ if (!(typeof value === 'string')) {
1385
+ return value;
1386
+ }
1387
+ if (OUTPUT_TARGET_KEYS_TO_KEEP.includes(key)) {
1388
+ return value;
1389
+ }
1390
+ return 'omitted';
1391
+ }));
1392
+ // this prop has to be handled separately because it is an array
1393
+ // so the replace function above will be called with all of its
1394
+ // members, giving us `["omitted", "omitted", ...]`.
1395
+ //
1396
+ // Instead, we check for its presence and manually copy over.
1397
+ if (isOutputTargetHydrate(target) && target.external) {
1398
+ anonymizedOT['external'] = target.external.concat();
1399
+ }
1400
+ return anonymizedOT;
1401
+ });
1402
+ // TODO(STENCIL-469): Investigate improving anonymization for tsCompilerOptions and devServer
1403
+ for (const prop of CONFIG_PROPS_TO_DELETE) {
1404
+ delete anonymizedConfig[prop];
1405
+ }
1406
+ return anonymizedConfig;
1407
+ };
1052
1408
  /**
1053
1409
  * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1054
1410
  * the dependencies and devDependencies properties. Pulls up the current installed version
1055
1411
  * of each package under the @stencil, @ionic, and @capacitor scopes.
1412
+ *
1056
1413
  * @param sys the system instance where telemetry is invoked
1057
1414
  * @param config the Stencil configuration associated with the current task that triggered telemetry
1058
1415
  * @returns an object listing all dev and production dependencies under the aforementioned scopes
@@ -1086,7 +1443,7 @@ async function getInstalledPackages(sys, config) {
1086
1443
  return { packages, packagesNoVersions };
1087
1444
  }
1088
1445
  catch (err) {
1089
- hasDebug(config) && console.error(err);
1446
+ hasDebug(config.flags) && console.error(err);
1090
1447
  return { packages, packagesNoVersions };
1091
1448
  }
1092
1449
  }
@@ -1135,6 +1492,7 @@ function sanitizeDeclaredVersion(version) {
1135
1492
  }
1136
1493
  /**
1137
1494
  * If telemetry is enabled, send a metric to an external data store
1495
+ *
1138
1496
  * @param sys the system instance where telemetry is invoked
1139
1497
  * @param config the Stencil configuration associated with the current task that triggered telemetry
1140
1498
  * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
@@ -1150,10 +1508,11 @@ async function sendMetric(sys, config, name, value) {
1150
1508
  value,
1151
1509
  session_id,
1152
1510
  };
1153
- await sendTelemetry(sys, config, { type: 'telemetry', message });
1511
+ await sendTelemetry(sys, config, message);
1154
1512
  }
1155
1513
  /**
1156
1514
  * Used to read the config file's tokens.telemetry property.
1515
+ *
1157
1516
  * @param sys The system where the command is invoked
1158
1517
  * @returns string
1159
1518
  */
@@ -1175,7 +1534,7 @@ async function sendTelemetry(sys, config, data) {
1175
1534
  try {
1176
1535
  const now = new Date().toISOString();
1177
1536
  const body = {
1178
- metrics: [data.message],
1537
+ metrics: [data],
1179
1538
  sent_at: now,
1180
1539
  };
1181
1540
  // This request is only made if telemetry is on.
@@ -1186,15 +1545,15 @@ async function sendTelemetry(sys, config, data) {
1186
1545
  },
1187
1546
  body: JSON.stringify(body),
1188
1547
  });
1189
- hasVerbose(config) &&
1190
- console.debug('\nSent %O metric to events service (status: %O)', data.message.name, response.status, '\n');
1548
+ hasVerbose(config.flags) &&
1549
+ console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1191
1550
  if (response.status !== 204) {
1192
- hasVerbose(config) &&
1551
+ hasVerbose(config.flags) &&
1193
1552
  console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1194
1553
  }
1195
1554
  }
1196
1555
  catch (e) {
1197
- hasVerbose(config) && console.debug('Telemetry request failed:', e);
1556
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1198
1557
  }
1199
1558
  }
1200
1559
  /**
@@ -1251,7 +1610,7 @@ const taskBuild = async (coreCompiler, config, sys) => {
1251
1610
  const results = await compiler.build();
1252
1611
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1253
1612
  if (sys) {
1254
- await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results);
1613
+ await telemetryBuildFinishedAction(sys, config, coreCompiler, results);
1255
1614
  }
1256
1615
  await compiler.destroy();
1257
1616
  if (results.hasError) {
@@ -1290,6 +1649,7 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1290
1649
  !!global.process &&
1291
1650
  typeof __filename === 'string' &&
1292
1651
  (!global.origin || typeof global.origin !== 'string');
1652
+ const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1293
1653
 
1294
1654
  /**
1295
1655
  * Task to generate component boilerplate and write it to disk. This task can
@@ -1338,7 +1698,10 @@ const taskGenerate = async (coreCompiler, config) => {
1338
1698
  if (!writtenFiles) {
1339
1699
  return config.sys.exit(1);
1340
1700
  }
1341
- // TODO(STENCIL-424): Investigate moving these console.log calls to config.logger.info
1701
+ // We use `console.log` here rather than our `config.logger` because we don't want
1702
+ // our TUI messages to be prefixed with timestamps and so on.
1703
+ //
1704
+ // See STENCIL-424 for details.
1342
1705
  console.log();
1343
1706
  console.log(`${config.logger.gray('$')} stencil generate ${input}`);
1344
1707
  console.log();
@@ -1482,7 +1845,8 @@ export class ${toPascalCase(tagName)} {
1482
1845
  `;
1483
1846
  };
1484
1847
  /**
1485
- * Get the boilerplate for style.
1848
+ * Get the boilerplate for style for a generated component
1849
+ * @returns a boilerplate CSS block
1486
1850
  */
1487
1851
  const getStyleUrlBoilerplate = () => `:host {
1488
1852
  display: block;
@@ -1536,10 +1900,17 @@ describe('${tagName}', () => {
1536
1900
  */
1537
1901
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1538
1902
 
1539
- const taskTelemetry = async (config, sys, logger) => {
1903
+ /**
1904
+ * Entrypoint for the Telemetry task
1905
+ * @param flags configuration flags provided to Stencil when a task was called (either this task or a task that invokes
1906
+ * telemetry)
1907
+ * @param sys the abstraction for interfacing with the operating system
1908
+ * @param logger a logging implementation to log the results out to the user
1909
+ */
1910
+ const taskTelemetry = async (flags, sys, logger) => {
1540
1911
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1541
- const isEnabling = config.flags.args.includes('on');
1542
- const isDisabling = config.flags.args.includes('off');
1912
+ const isEnabling = flags.args.includes('on');
1913
+ const isDisabling = flags.args.includes('off');
1543
1914
  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')}`;
1544
1915
  const THANK_YOU = `Thank you for helping to make Stencil better! 💖`;
1545
1916
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
@@ -1568,7 +1939,14 @@ const taskTelemetry = async (config, sys, logger) => {
1568
1939
  `);
1569
1940
  };
1570
1941
 
1571
- const taskHelp = async (config, logger, sys) => {
1942
+ /**
1943
+ * Entrypoint for the Help task, providing Stencil usage context to the user
1944
+ * @param flags configuration flags provided to Stencil when a task was call (either this task or a task that invokes
1945
+ * telemetry)
1946
+ * @param logger a logging implementation to log the results out to the user
1947
+ * @param sys the abstraction for interfacing with the operating system
1948
+ */
1949
+ const taskHelp = async (flags, logger, sys) => {
1572
1950
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1573
1951
  console.log(`
1574
1952
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
@@ -1601,7 +1979,7 @@ const taskHelp = async (config, logger, sys) => {
1601
1979
  `);
1602
1980
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1603
1981
  if (sys) {
1604
- await taskTelemetry(config, sys, logger);
1982
+ await taskTelemetry(flags, sys, logger);
1605
1983
  }
1606
1984
  console.log(`
1607
1985
  ${logger.bold('Examples:')}
@@ -1658,6 +2036,10 @@ const taskServe = async (config) => {
1658
2036
  });
1659
2037
  };
1660
2038
 
2039
+ /**
2040
+ * Entrypoint for any Stencil tests
2041
+ * @param config a validated Stencil configuration entity
2042
+ */
1661
2043
  const taskTest = async (config) => {
1662
2044
  if (!IS_NODE_ENV) {
1663
2045
  config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
@@ -1704,6 +2086,95 @@ const taskTest = async (config) => {
1704
2086
  }
1705
2087
  };
1706
2088
 
2089
+ /**
2090
+ * Creates an instance of a logger
2091
+ * @returns the new logger instance
2092
+ */
2093
+ const createLogger = () => {
2094
+ let useColors = IS_BROWSER_ENV;
2095
+ let level = 'info';
2096
+ return {
2097
+ enableColors: (uc) => (useColors = uc),
2098
+ getLevel: () => level,
2099
+ setLevel: (l) => (level = l),
2100
+ emoji: (e) => e,
2101
+ info: console.log.bind(console),
2102
+ warn: console.warn.bind(console),
2103
+ error: console.error.bind(console),
2104
+ debug: console.debug.bind(console),
2105
+ red: (msg) => msg,
2106
+ green: (msg) => msg,
2107
+ yellow: (msg) => msg,
2108
+ blue: (msg) => msg,
2109
+ magenta: (msg) => msg,
2110
+ cyan: (msg) => msg,
2111
+ gray: (msg) => msg,
2112
+ bold: (msg) => msg,
2113
+ dim: (msg) => msg,
2114
+ bgRed: (msg) => msg,
2115
+ createTimeSpan: (_startMsg, _debug = false) => ({
2116
+ duration: () => 0,
2117
+ finish: () => 0,
2118
+ }),
2119
+ printDiagnostics(diagnostics) {
2120
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
2121
+ },
2122
+ };
2123
+ };
2124
+ const logDiagnostic = (diagnostic, useColors) => {
2125
+ let color = BLUE;
2126
+ let prefix = 'Build';
2127
+ let msg = '';
2128
+ if (diagnostic.level === 'error') {
2129
+ color = RED;
2130
+ prefix = 'Error';
2131
+ }
2132
+ else if (diagnostic.level === 'warn') {
2133
+ color = YELLOW;
2134
+ prefix = 'Warning';
2135
+ }
2136
+ if (diagnostic.header) {
2137
+ prefix = diagnostic.header;
2138
+ }
2139
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
2140
+ if (filePath) {
2141
+ msg += filePath;
2142
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
2143
+ msg += ', line ' + diagnostic.lineNumber;
2144
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
2145
+ msg += ', column ' + diagnostic.columnNumber;
2146
+ }
2147
+ }
2148
+ msg += '\n';
2149
+ }
2150
+ msg += diagnostic.messageText;
2151
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
2152
+ diagnostic.lines.forEach((l) => {
2153
+ msg += '\n' + l.lineNumber + ': ' + l.text;
2154
+ });
2155
+ msg += '\n';
2156
+ }
2157
+ if (useColors) {
2158
+ const styledPrefix = [
2159
+ '%c' + prefix,
2160
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
2161
+ ];
2162
+ console.log(...styledPrefix, msg);
2163
+ }
2164
+ else if (diagnostic.level === 'error') {
2165
+ console.error(msg);
2166
+ }
2167
+ else if (diagnostic.level === 'warn') {
2168
+ console.warn(msg);
2169
+ }
2170
+ else {
2171
+ console.log(msg);
2172
+ }
2173
+ };
2174
+ const YELLOW = `#f39c12`;
2175
+ const RED = `#c0392b`;
2176
+ const BLUE = `#3498db`;
2177
+
1707
2178
  const run = async (init) => {
1708
2179
  const { args, logger, sys } = init;
1709
2180
  try {
@@ -1719,7 +2190,7 @@ const run = async (init) => {
1719
2190
  sys.applyGlobalPatch(sys.getCurrentDirectory());
1720
2191
  }
1721
2192
  if (task === 'help' || flags.help) {
1722
- await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys);
2193
+ await taskHelp({ task: 'help', args }, logger, sys);
1723
2194
  return;
1724
2195
  }
1725
2196
  startupLog(logger, task);
@@ -1745,7 +2216,7 @@ const run = async (init) => {
1745
2216
  startupLogVersion(logger, task, coreCompiler);
1746
2217
  loadedCompilerLog(sys, logger, flags, coreCompiler);
1747
2218
  if (task === 'info') {
1748
- await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => {
2219
+ await telemetryAction(sys, { flags: { task: 'info' }, logger }, coreCompiler, async () => {
1749
2220
  await taskInfo(coreCompiler, sys, logger);
1750
2221
  });
1751
2222
  return;
@@ -1768,7 +2239,7 @@ const run = async (init) => {
1768
2239
  sys.applyGlobalPatch(validated.config.rootDir);
1769
2240
  }
1770
2241
  await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
1771
- await telemetryAction(sys, validated.config, logger, coreCompiler, async () => {
2242
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
1772
2243
  await runTask(coreCompiler, validated.config, task, sys);
1773
2244
  });
1774
2245
  }
@@ -1781,43 +2252,45 @@ const run = async (init) => {
1781
2252
  }
1782
2253
  };
1783
2254
  const runTask = async (coreCompiler, config, task, sys) => {
1784
- config.flags = config.flags || { task };
1785
- config.outputTargets = config.outputTargets || [];
2255
+ var _a, _b;
2256
+ const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2257
+ const strictConfig = { ...config, flags: (_b = { ...config.flags }) !== null && _b !== void 0 ? _b : { task }, logger };
2258
+ strictConfig.outputTargets = strictConfig.outputTargets || [];
1786
2259
  switch (task) {
1787
2260
  case 'build':
1788
- await taskBuild(coreCompiler, config, sys);
2261
+ await taskBuild(coreCompiler, strictConfig, sys);
1789
2262
  break;
1790
2263
  case 'docs':
1791
- await taskDocs(coreCompiler, config);
2264
+ await taskDocs(coreCompiler, strictConfig);
1792
2265
  break;
1793
2266
  case 'generate':
1794
2267
  case 'g':
1795
- await taskGenerate(coreCompiler, config);
2268
+ await taskGenerate(coreCompiler, strictConfig);
1796
2269
  break;
1797
2270
  case 'help':
1798
- await taskHelp(config, config.logger, sys);
2271
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1799
2272
  break;
1800
2273
  case 'prerender':
1801
- await taskPrerender(coreCompiler, config);
2274
+ await taskPrerender(coreCompiler, strictConfig);
1802
2275
  break;
1803
2276
  case 'serve':
1804
- await taskServe(config);
2277
+ await taskServe(strictConfig);
1805
2278
  break;
1806
2279
  case 'telemetry':
1807
2280
  // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1808
2281
  if (sys) {
1809
- await taskTelemetry(config, sys, config.logger);
2282
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
1810
2283
  }
1811
2284
  break;
1812
2285
  case 'test':
1813
- await taskTest(config);
2286
+ await taskTest(strictConfig);
1814
2287
  break;
1815
2288
  case 'version':
1816
2289
  console.log(coreCompiler.version);
1817
2290
  break;
1818
2291
  default:
1819
- config.logger.error(`${config.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
1820
- await taskHelp(config, config.logger, sys);
2292
+ strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
2293
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1821
2294
  return config.sys.exit(1);
1822
2295
  }
1823
2296
  };