@stencil/core 2.17.0 → 2.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/cli/config-flags.d.ts +110 -0
  2. package/cli/index.cjs +675 -253
  3. package/cli/index.d.ts +3 -2
  4. package/cli/index.js +675 -253
  5. package/cli/package.json +1 -1
  6. package/compiler/package.json +1 -1
  7. package/compiler/stencil.js +676 -226
  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 +14 -4
  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 +41 -3
  32. package/mock-doc/index.d.ts +15 -0
  33. package/mock-doc/index.js +41 -3
  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 +4 -4
  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 +71 -40
  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 +28 -2
  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.17.0 | MIT Licensed | https://stenciljs.com
2
+ Stencil CLI v2.17.2 | 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,174 +317,13 @@ const validateComponentTag = (tag) => {
275
317
  return undefined;
276
318
  };
277
319
 
278
- const parseFlags = (args, sys) => {
279
- const flags = {
280
- task: null,
281
- args: [],
282
- knownArgs: [],
283
- unknownArgs: null,
284
- };
285
- // cmd line has more priority over npm scripts cmd
286
- flags.args = args.slice();
287
- if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
288
- flags.task = flags.args[0];
289
- }
290
- parseArgs(flags, flags.args, flags.knownArgs);
291
- if (sys && sys.name === 'node') {
292
- const envArgs = getNpmConfigEnvArgs(sys);
293
- parseArgs(flags, envArgs, flags.knownArgs);
294
- envArgs.forEach((envArg) => {
295
- if (!flags.args.includes(envArg)) {
296
- flags.args.push(envArg);
297
- }
298
- });
299
- }
300
- if (flags.task != null) {
301
- const i = flags.args.indexOf(flags.task);
302
- if (i > -1) {
303
- flags.args.splice(i, 1);
304
- }
305
- }
306
- flags.unknownArgs = flags.args.filter((arg) => {
307
- return !flags.knownArgs.includes(arg);
308
- });
309
- return flags;
310
- };
311
320
  /**
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).
316
- *
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;
327
- }
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;
356
- }
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
- }
393
- }
394
- });
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;
400
- }
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]);
422
- }
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
- }
434
- }
435
- }
436
- });
437
- };
438
- const configCase = (prop) => {
439
- prop = dashToPascalCase(prop);
440
- return prop.charAt(0).toLowerCase() + prop.slice(1);
441
- };
442
- const BOOLEAN_ARG_OPTS = [
321
+ * All the Boolean options supported by the Stencil CLI
322
+ */
323
+ const BOOLEAN_CLI_ARGS = [
443
324
  'build',
444
325
  'cache',
445
- 'check-version',
326
+ 'checkVersion',
446
327
  'ci',
447
328
  'compare',
448
329
  'debug',
@@ -457,58 +338,474 @@ const BOOLEAN_ARG_OPTS = [
457
338
  'log',
458
339
  'open',
459
340
  'prerender',
460
- 'prerender-external',
341
+ 'prerenderExternal',
461
342
  'prod',
462
343
  'profile',
463
- 'service-worker',
344
+ 'serviceWorker',
464
345
  'screenshot',
465
346
  'serve',
466
- 'skip-node-check',
347
+ 'skipNodeCheck',
467
348
  'spec',
468
349
  'ssr',
469
350
  'stats',
470
- 'update-screenshot',
351
+ 'updateScreenshot',
471
352
  'verbose',
472
353
  'version',
473
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',
474
411
  ];
475
- const NUMBER_ARG_OPTS = ['max-workers', 'port'];
476
- const STRING_ARG_OPTS = [
412
+ /**
413
+ * All the String options supported by the Stencil CLI
414
+ */
415
+ const STRING_CLI_ARGS = [
477
416
  'address',
478
417
  'config',
479
- 'docs-json',
418
+ 'docsApi',
419
+ 'docsJson',
480
420
  'emulate',
481
- 'log-level',
482
421
  'root',
483
- 'screenshot-connector',
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',
484
476
  ];
485
- const ARG_OPTS_ALIASES = {
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 = {
486
496
  config: 'c',
487
497
  help: 'h',
488
498
  port: 'p',
489
499
  version: 'v',
490
500
  };
491
- const getNpmConfigEnvArgs = (sys) => {
492
- // process.env.npm_config_argv
493
- // {"remain":["4444"],"cooked":["run","serve","--port","4444"],"original":["run","serve","--port","4444"]}
494
- let args = [];
495
- try {
496
- const npmConfigArgs = sys.getEnvironmentVar('npm_config_argv');
497
- if (npmConfigArgs) {
498
- args = JSON.parse(npmConfigArgs).original;
499
- if (args[0] === 'run') {
500
- args = args.slice(2);
501
- }
501
+ /**
502
+ * Helper function for initializing a `ConfigFlags` object. Provide any overrides
503
+ * for default values and off you go!
504
+ *
505
+ * @param init an object with any overrides for default values
506
+ * @returns a complete CLI flag object
507
+ */
508
+ const createConfigFlags = (init = {}) => {
509
+ const flags = {
510
+ task: null,
511
+ args: [],
512
+ knownArgs: [],
513
+ unknownArgs: [],
514
+ ...init,
515
+ };
516
+ return flags;
517
+ };
518
+
519
+ /**
520
+ * Parse command line arguments into a structured `ConfigFlags` object
521
+ *
522
+ * @param args an array of CLI flags
523
+ * @param _sys an optional compiler system
524
+ * @returns a structured ConfigFlags object
525
+ */
526
+ const parseFlags = (args, _sys) => {
527
+ // TODO(STENCIL-509): remove the _sys parameter here ^^ (for v3)
528
+ const flags = createConfigFlags();
529
+ // cmd line has more priority over npm scripts cmd
530
+ flags.args = Array.isArray(args) ? args.slice() : [];
531
+ if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
532
+ flags.task = flags.args[0];
533
+ }
534
+ parseArgs(flags, flags.args);
535
+ if (flags.task != null) {
536
+ const i = flags.args.indexOf(flags.task);
537
+ if (i > -1) {
538
+ flags.args.splice(i, 1);
502
539
  }
503
540
  }
504
- catch (e) { }
505
- return args;
541
+ // to find unknown / unrecognized arguments we filter `args`, including only
542
+ // arguments whose normalized form is not found in `knownArgs`. `knownArgs`
543
+ // is populated during the call to `parseArgs` above. For arguments like
544
+ // `--foobar` the string `"--foobar"` will be added, while for more
545
+ // complicated arguments like `--bizBoz=bop` or `--bizBoz bop` just the
546
+ // string `"--bizBoz"` will be added.
547
+ flags.unknownArgs = flags.args.filter((arg) => !flags.knownArgs.includes(parseEqualsArg(arg)[0]));
548
+ return flags;
506
549
  };
550
+ /**
551
+ * Parse command line arguments that are enumerated in the `config-flags`
552
+ * module. Handles leading dashes on arguments, aliases that are defined for a
553
+ * small number of arguments, and parsing values for non-boolean arguments
554
+ * (e.g. port number for the dev server).
555
+ *
556
+ * @param flags a ConfigFlags object to which parsed arguments will be added
557
+ * @param args an array of command-line arguments to parse
558
+ */
559
+ const parseArgs = (flags, args) => {
560
+ BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
561
+ STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
562
+ NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
563
+ STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
564
+ LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
565
+ };
566
+ /**
567
+ * Parse a boolean CLI argument. For these, we support the following formats:
568
+ *
569
+ * - `--booleanArg`
570
+ * - `--boolean-arg`
571
+ * - `--noBooleanArg`
572
+ * - `--no-boolean-arg`
573
+ *
574
+ * The final two variants should be parsed to a value of `false` on the config
575
+ * object.
576
+ *
577
+ * @param flags the config flags object, while we'll modify
578
+ * @param args our CLI arguments
579
+ * @param configCaseName the argument we want to look at right now
580
+ */
581
+ const parseBooleanArg = (flags, args, configCaseName) => {
582
+ // we support both dash-case and PascalCase versions of the parameter
583
+ // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS
584
+ const alias = CLI_ARG_ALIASES[configCaseName];
585
+ const dashCaseName = toDashCase(configCaseName);
586
+ if (typeof flags[configCaseName] !== 'boolean') {
587
+ flags[configCaseName] = null;
588
+ }
589
+ args.forEach((cmdArg) => {
590
+ let value;
591
+ if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) {
592
+ value = true;
593
+ }
594
+ else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) {
595
+ value = false;
596
+ }
597
+ else if (alias && cmdArg === `-${alias}`) {
598
+ value = true;
599
+ }
600
+ if (value !== undefined && cmdArg !== undefined) {
601
+ flags[configCaseName] = value;
602
+ flags.knownArgs.push(cmdArg);
603
+ }
604
+ });
605
+ };
606
+ /**
607
+ * Parse a string CLI argument
608
+ *
609
+ * @param flags the config flags object, while we'll modify
610
+ * @param args our CLI arguments
611
+ * @param configCaseName the argument we want to look at right now
612
+ */
613
+ const parseStringArg = (flags, args, configCaseName) => {
614
+ if (typeof flags[configCaseName] !== 'string') {
615
+ flags[configCaseName] = null;
616
+ }
617
+ const { value, matchingArg } = getValue(args, configCaseName);
618
+ if (value !== undefined && matchingArg !== undefined) {
619
+ flags[configCaseName] = value;
620
+ flags.knownArgs.push(matchingArg);
621
+ flags.knownArgs.push(value);
622
+ }
623
+ };
624
+ /**
625
+ * Parse a number CLI argument
626
+ *
627
+ * @param flags the config flags object, while we'll modify
628
+ * @param args our CLI arguments
629
+ * @param configCaseName the argument we want to look at right now
630
+ */
631
+ const parseNumberArg = (flags, args, configCaseName) => {
632
+ if (typeof flags[configCaseName] !== 'number') {
633
+ flags[configCaseName] = null;
634
+ }
635
+ const { value, matchingArg } = getValue(args, configCaseName);
636
+ if (value !== undefined && matchingArg !== undefined) {
637
+ flags[configCaseName] = parseInt(value, 10);
638
+ flags.knownArgs.push(matchingArg);
639
+ flags.knownArgs.push(value);
640
+ }
641
+ };
642
+ /**
643
+ * Parse a CLI argument which may be either a string or a number
644
+ *
645
+ * @param flags the config flags object, while we'll modify
646
+ * @param args our CLI arguments
647
+ * @param configCaseName the argument we want to look at right now
648
+ */
649
+ const parseStringNumberArg = (flags, args, configCaseName) => {
650
+ if (!['number', 'string'].includes(typeof flags[configCaseName])) {
651
+ flags[configCaseName] = null;
652
+ }
653
+ const { value, matchingArg } = getValue(args, configCaseName);
654
+ if (value !== undefined && matchingArg !== undefined) {
655
+ if (CLI_ARG_STRING_REGEX.test(value)) {
656
+ // if it matches the regex we treat it like a string
657
+ flags[configCaseName] = value;
658
+ }
659
+ else {
660
+ // it was a number, great!
661
+ flags[configCaseName] = Number(value);
662
+ }
663
+ flags.knownArgs.push(matchingArg);
664
+ flags.knownArgs.push(value);
665
+ }
666
+ };
667
+ /**
668
+ * We use this regular expression to detect CLI parameters which
669
+ * should be parsed as string values (as opposed to numbers) for
670
+ * the argument types for which we support both a string and a
671
+ * number value.
672
+ *
673
+ * The regex tests for the presence of at least one character which is
674
+ * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
675
+ * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
676
+ * support the admittedly unlikely use of scientific notation, like `"4e+0"`
677
+ * for `4`).
678
+ *
679
+ * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
680
+ * `"5.0"`. If it matches a given string we conclude that the string should
681
+ * be parsed as a string literal, rather than using `Number` to convert it
682
+ * to a number.
683
+ */
684
+ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
685
+ /**
686
+ * Parse a LogLevel CLI argument. These can be only a specific
687
+ * set of strings, so this function takes care of validating that
688
+ * the value is correct.
689
+ *
690
+ * @param flags the config flags object, while we'll modify
691
+ * @param args our CLI arguments
692
+ * @param configCaseName the argument we want to look at right now
693
+ */
694
+ const parseLogLevelArg = (flags, args, configCaseName) => {
695
+ if (typeof flags[configCaseName] !== 'string') {
696
+ flags[configCaseName] = null;
697
+ }
698
+ const { value, matchingArg } = getValue(args, configCaseName);
699
+ if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) {
700
+ flags[configCaseName] = value;
701
+ flags.knownArgs.push(matchingArg);
702
+ flags.knownArgs.push(value);
703
+ }
704
+ };
705
+ /**
706
+ * Helper for pulling values out from the raw array of CLI arguments. This logic
707
+ * is shared between a few different types of CLI args.
708
+ *
709
+ * We look for arguments in the following formats:
710
+ *
711
+ * - `--my-cli-argument value`
712
+ * - `--my-cli-argument=value`
713
+ * - `--myCliArgument value`
714
+ * - `--myCliArgument=value`
715
+ *
716
+ * We also check for shortened aliases, which we define for a few arguments.
717
+ *
718
+ * @param args the CLI args we're dealing with
719
+ * @param configCaseName the ConfigFlag key which we're looking to pull out a value for
720
+ * @returns the value for the flag as well as the exact string which it matched from
721
+ * the user input.
722
+ */
723
+ const getValue = (args, configCaseName) => {
724
+ // for some CLI args we have a short alias, like 'c' for 'config'
725
+ const alias = CLI_ARG_ALIASES[configCaseName];
726
+ // we support supplying arguments in both dash-case and configCase
727
+ // for ease of use
728
+ const dashCaseName = toDashCase(configCaseName);
729
+ let value;
730
+ let matchingArg;
731
+ args.forEach((arg, i) => {
732
+ if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) {
733
+ // our argument was passed at the command-line in the format --argName=arg-value
734
+ [matchingArg, value] = parseEqualsArg(arg);
735
+ }
736
+ else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) {
737
+ // the next value in the array is assumed to be a value for this argument
738
+ value = args[i + 1];
739
+ matchingArg = arg;
740
+ }
741
+ else if (alias) {
742
+ if (arg.startsWith(`-${alias}=`)) {
743
+ [matchingArg, value] = parseEqualsArg(arg);
744
+ }
745
+ else if (arg === `-${alias}`) {
746
+ value = args[i + 1];
747
+ matchingArg = arg;
748
+ }
749
+ }
750
+ });
751
+ return { value, matchingArg };
752
+ };
753
+ /**
754
+ * Parse an 'equals' argument, which is a CLI argument-value pair in the
755
+ * format `--foobar=12` (as opposed to a space-separated format like
756
+ * `--foobar 12`).
757
+ *
758
+ * To parse this we split on the `=`, returning the first part as the argument
759
+ * name and the second part as the value. We join the value on `"="` in case
760
+ * there is another `"="` in the argument.
761
+ *
762
+ * This function is safe to call with any arg, and can therefore be used as
763
+ * an argument 'normalizer'. If CLI argument is not an 'equals' argument then
764
+ * the return value will be a tuple of the original argument and an empty
765
+ * string `""` for the value.
766
+ *
767
+ * In code terms, if you do:
768
+ *
769
+ * ```ts
770
+ * const [arg, value] = parseEqualsArg("--myArgument")
771
+ * ```
772
+ *
773
+ * Then `arg` will be `"--myArgument"` and `value` will be `""`, whereas if
774
+ * you do:
775
+ *
776
+ *
777
+ * ```ts
778
+ * const [arg, value] = parseEqualsArg("--myArgument=myValue")
779
+ * ```
780
+ *
781
+ * Then `arg` will be `"--myArgument"` and `value` will be `"myValue"`.
782
+ *
783
+ * @param arg the arg in question
784
+ * @returns a tuple containing the arg name and the value (if present)
785
+ */
786
+ const parseEqualsArg = (arg) => {
787
+ const [originalArg, ...value] = arg.split('=');
788
+ return [originalArg, value.join('=')];
789
+ };
790
+ /**
791
+ * Small helper for getting type-system-level assurance that a `string` can be
792
+ * narrowed to a `LogLevel`
793
+ *
794
+ * @param maybeLogLevel the string to check
795
+ * @returns whether this is a `LogLevel`
796
+ */
797
+ const isLogLevel = (maybeLogLevel) =>
798
+ // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
799
+ // boolean` so a `string` cannot be passed to `includes` on a
800
+ // `ReadonlyArray` 😢 thus we `as any`
801
+ //
802
+ // see microsoft/TypeScript#31018 for some discussion of this
803
+ LOG_LEVELS.includes(maybeLogLevel);
507
804
 
508
805
  const dependencies = [
509
806
  {
510
807
  name: "@stencil/core",
511
- version: "2.17.0",
808
+ version: "2.17.2",
512
809
  main: "compiler/stencil.js",
513
810
  resources: [
514
811
  "package.json",
@@ -833,12 +1130,11 @@ const tryFn = async (fn, ...args) => {
833
1130
  }
834
1131
  return null;
835
1132
  };
836
- const isInteractive = (sys, config, object) => {
837
- var _a;
1133
+ const isInteractive = (sys, flags, object) => {
838
1134
  const terminalInfo = object ||
839
1135
  Object.freeze({
840
1136
  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),
1137
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!flags.ci,
842
1138
  });
843
1139
  return terminalInfo.tty && !terminalInfo.ci;
844
1140
  };
@@ -863,19 +1159,19 @@ async function readJson(sys, path) {
863
1159
  }
864
1160
  /**
865
1161
  * Does the command have the debug flag?
866
- * @param config The config passed into the Stencil command
1162
+ * @param flags The configuration flags passed into the Stencil command
867
1163
  * @returns true if --debug has been passed, otherwise false
868
1164
  */
869
- function hasDebug(config) {
870
- return config.flags.debug;
1165
+ function hasDebug(flags) {
1166
+ return flags.debug;
871
1167
  }
872
1168
  /**
873
1169
  * Does the command have the verbose and debug flags?
874
- * @param config The config passed into the Stencil command
1170
+ * @param flags The configuration flags passed into the Stencil command
875
1171
  * @returns true if both --debug and --verbose have been passed, otherwise false
876
1172
  */
877
- function hasVerbose(config) {
878
- return config.flags.verbose && hasDebug(config);
1173
+ function hasVerbose(flags) {
1174
+ return flags.verbose && hasDebug(flags);
879
1175
  }
880
1176
 
881
1177
  /**
@@ -886,7 +1182,7 @@ function hasVerbose(config) {
886
1182
  * @returns true if telemetry should be sent, false otherwise
887
1183
  */
888
1184
  async function shouldTrack(config, sys, ci) {
889
- return !ci && isInteractive(sys, config) && (await checkTelemetry(sys));
1185
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
890
1186
  }
891
1187
 
892
1188
  const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
@@ -957,11 +1253,10 @@ const WWW = 'www';
957
1253
  *
958
1254
  * @param sys The system where the command is invoked
959
1255
  * @param config The config passed into the Stencil command
960
- * @param logger The tool used to do logging
961
1256
  * @param coreCompiler The compiler used to do builds
962
1257
  * @param result The results of a compiler build.
963
1258
  */
964
- async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) {
1259
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
965
1260
  const tracking = await shouldTrack(config, sys, config.flags.ci);
966
1261
  if (!tracking) {
967
1262
  return;
@@ -969,21 +1264,19 @@ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, r
969
1264
  const component_count = Object.keys(result.componentGraph).length;
970
1265
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
971
1266
  await sendMetric(sys, config, 'stencil_cli_command', data);
972
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1267
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
973
1268
  }
974
1269
  /**
975
1270
  * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
976
1271
  *
977
1272
  * @param sys The system where the command is invoked
978
1273
  * @param config The config passed into the Stencil command
979
- * @param logger The tool used to do logging
980
1274
  * @param coreCompiler The compiler used to do builds
981
1275
  * @param action A Promise-based function to call in order to get the duration of any given command.
982
1276
  * @returns void
983
1277
  */
984
- async function telemetryAction(sys, config, logger, coreCompiler, action) {
985
- var _a;
986
- const tracking = await shouldTrack(config, sys, !!((_a = config === null || config === void 0 ? void 0 : config.flags) === null || _a === void 0 ? void 0 : _a.ci));
1278
+ async function telemetryAction(sys, config, coreCompiler, action) {
1279
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
987
1280
  let duration = undefined;
988
1281
  let error;
989
1282
  if (action) {
@@ -1003,11 +1296,20 @@ async function telemetryAction(sys, config, logger, coreCompiler, action) {
1003
1296
  }
1004
1297
  const data = await prepareData(coreCompiler, config, sys, duration);
1005
1298
  await sendMetric(sys, config, 'stencil_cli_command', data);
1006
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1299
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1007
1300
  if (error) {
1008
1301
  throw error;
1009
1302
  }
1010
1303
  }
1304
+ /**
1305
+ * Helper function to determine if a Stencil configuration builds an application.
1306
+ *
1307
+ * This function is a rough approximation whether an application is generated as a part of a Stencil build, based on
1308
+ * contents of the project's `stencil.config.ts` file.
1309
+ *
1310
+ * @param config the configuration used by the Stencil project
1311
+ * @returns true if we believe the project generates an application, false otherwise
1312
+ */
1011
1313
  function hasAppTarget(config) {
1012
1314
  return config.outputTargets.some((target) => target.type === WWW && (!!target.serviceWorker || (!!target.baseUrl && target.baseUrl !== '/')));
1013
1315
  }
@@ -1015,7 +1317,15 @@ function isUsingYarn(sys) {
1015
1317
  var _a;
1016
1318
  return ((_a = sys.getEnvironmentVar('npm_execpath')) === null || _a === void 0 ? void 0 : _a.includes('yarn')) || false;
1017
1319
  }
1018
- async function getActiveTargets(config) {
1320
+ /**
1321
+ * Build a list of the different types of output targets used in a Stencil configuration.
1322
+ *
1323
+ * Duplicate entries will not be returned from the list
1324
+ *
1325
+ * @param config the configuration used by the Stencil project
1326
+ * @returns a unique list of output target types found in the Stencil configuration
1327
+ */
1328
+ function getActiveTargets(config) {
1019
1329
  const result = config.outputTargets.map((t) => t.type);
1020
1330
  return Array.from(new Set(result));
1021
1331
  }
@@ -1032,7 +1342,7 @@ async function getActiveTargets(config) {
1032
1342
  const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1033
1343
  const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1034
1344
  const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
1035
- const targets = await getActiveTargets(config);
1345
+ const targets = getActiveTargets(config);
1036
1346
  const yarn = isUsingYarn(sys);
1037
1347
  const stencil = coreCompiler.version || 'unknown';
1038
1348
  const system = `${sys.name} ${sys.version}`;
@@ -1095,14 +1405,13 @@ const CONFIG_PROPS_TO_DELETE = ['sys', 'logger', 'tsCompilerOptions', 'devServer
1095
1405
  * @returns an anonymized copy of the same config
1096
1406
  */
1097
1407
  const anonymizeConfigForTelemetry = (config) => {
1098
- var _a;
1099
1408
  const anonymizedConfig = { ...config };
1100
1409
  for (const prop of CONFIG_PROPS_TO_ANONYMIZE) {
1101
1410
  if (anonymizedConfig[prop] !== undefined) {
1102
1411
  anonymizedConfig[prop] = 'omitted';
1103
1412
  }
1104
1413
  }
1105
- anonymizedConfig.outputTargets = ((_a = config.outputTargets) !== null && _a !== void 0 ? _a : []).map((target) => {
1414
+ anonymizedConfig.outputTargets = config.outputTargets.map((target) => {
1106
1415
  // Anonymize the outputTargets on our configuration, taking advantage of the
1107
1416
  // optional 2nd argument to `JSON.stringify`. If anything is not a string
1108
1417
  // we retain it so that any nested properties are handled, else we check
@@ -1171,7 +1480,7 @@ async function getInstalledPackages(sys, config) {
1171
1480
  return { packages, packagesNoVersions };
1172
1481
  }
1173
1482
  catch (err) {
1174
- hasDebug(config) && console.error(err);
1483
+ hasDebug(config.flags) && console.error(err);
1175
1484
  return { packages, packagesNoVersions };
1176
1485
  }
1177
1486
  }
@@ -1273,15 +1582,15 @@ async function sendTelemetry(sys, config, data) {
1273
1582
  },
1274
1583
  body: JSON.stringify(body),
1275
1584
  });
1276
- hasVerbose(config) &&
1585
+ hasVerbose(config.flags) &&
1277
1586
  console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1278
1587
  if (response.status !== 204) {
1279
- hasVerbose(config) &&
1588
+ hasVerbose(config.flags) &&
1280
1589
  console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1281
1590
  }
1282
1591
  }
1283
1592
  catch (e) {
1284
- hasVerbose(config) && console.debug('Telemetry request failed:', e);
1593
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1285
1594
  }
1286
1595
  }
1287
1596
  /**
@@ -1323,7 +1632,7 @@ function getMajorVersion(version) {
1323
1632
  return parts[0];
1324
1633
  }
1325
1634
 
1326
- const taskBuild = async (coreCompiler, config, sys) => {
1635
+ const taskBuild = async (coreCompiler, config) => {
1327
1636
  if (config.flags.watch) {
1328
1637
  // watch build
1329
1638
  await taskWatch(coreCompiler, config);
@@ -1336,10 +1645,7 @@ const taskBuild = async (coreCompiler, config, sys) => {
1336
1645
  const versionChecker = startCheckVersion(config, coreCompiler.version);
1337
1646
  const compiler = await coreCompiler.createCompiler(config);
1338
1647
  const results = await compiler.build();
1339
- // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1340
- if (sys) {
1341
- await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results);
1342
- }
1648
+ await telemetryBuildFinishedAction(config.sys, config, coreCompiler, results);
1343
1649
  await compiler.destroy();
1344
1650
  if (results.hasError) {
1345
1651
  exitCode = 1;
@@ -1377,6 +1683,7 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1377
1683
  !!global.process &&
1378
1684
  typeof __filename === 'string' &&
1379
1685
  (!global.origin || typeof global.origin !== 'string');
1686
+ const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1380
1687
 
1381
1688
  /**
1382
1689
  * Task to generate component boilerplate and write it to disk. This task can
@@ -1572,7 +1879,8 @@ export class ${toPascalCase(tagName)} {
1572
1879
  `;
1573
1880
  };
1574
1881
  /**
1575
- * Get the boilerplate for style.
1882
+ * Get the boilerplate for style for a generated component
1883
+ * @returns a boilerplate CSS block
1576
1884
  */
1577
1885
  const getStyleUrlBoilerplate = () => `:host {
1578
1886
  display: block;
@@ -1626,10 +1934,17 @@ describe('${tagName}', () => {
1626
1934
  */
1627
1935
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1628
1936
 
1629
- const taskTelemetry = async (config, sys, logger) => {
1937
+ /**
1938
+ * Entrypoint for the Telemetry task
1939
+ * @param flags configuration flags provided to Stencil when a task was called (either this task or a task that invokes
1940
+ * telemetry)
1941
+ * @param sys the abstraction for interfacing with the operating system
1942
+ * @param logger a logging implementation to log the results out to the user
1943
+ */
1944
+ const taskTelemetry = async (flags, sys, logger) => {
1630
1945
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1631
- const isEnabling = config.flags.args.includes('on');
1632
- const isDisabling = config.flags.args.includes('off');
1946
+ const isEnabling = flags.args.includes('on');
1947
+ const isDisabling = flags.args.includes('off');
1633
1948
  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')}`;
1634
1949
  const THANK_YOU = `Thank you for helping to make Stencil better! 💖`;
1635
1950
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
@@ -1658,7 +1973,14 @@ const taskTelemetry = async (config, sys, logger) => {
1658
1973
  `);
1659
1974
  };
1660
1975
 
1661
- const taskHelp = async (config, logger, sys) => {
1976
+ /**
1977
+ * Entrypoint for the Help task, providing Stencil usage context to the user
1978
+ * @param flags configuration flags provided to Stencil when a task was call (either this task or a task that invokes
1979
+ * telemetry)
1980
+ * @param logger a logging implementation to log the results out to the user
1981
+ * @param sys the abstraction for interfacing with the operating system
1982
+ */
1983
+ const taskHelp = async (flags, logger, sys) => {
1662
1984
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1663
1985
  console.log(`
1664
1986
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
@@ -1689,10 +2011,7 @@ const taskHelp = async (config, logger, sys) => {
1689
2011
  ${prompt} ${logger.green('stencil generate')} or ${logger.green('stencil g')}
1690
2012
 
1691
2013
  `);
1692
- // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1693
- if (sys) {
1694
- await taskTelemetry(config, sys, logger);
1695
- }
2014
+ await taskTelemetry(flags, sys, logger);
1696
2015
  console.log(`
1697
2016
  ${logger.bold('Examples:')}
1698
2017
 
@@ -1748,6 +2067,10 @@ const taskServe = async (config) => {
1748
2067
  });
1749
2068
  };
1750
2069
 
2070
+ /**
2071
+ * Entrypoint for any Stencil tests
2072
+ * @param config a validated Stencil configuration entity
2073
+ */
1751
2074
  const taskTest = async (config) => {
1752
2075
  if (!IS_NODE_ENV) {
1753
2076
  config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
@@ -1794,10 +2117,99 @@ const taskTest = async (config) => {
1794
2117
  }
1795
2118
  };
1796
2119
 
2120
+ /**
2121
+ * Creates an instance of a logger
2122
+ * @returns the new logger instance
2123
+ */
2124
+ const createLogger = () => {
2125
+ let useColors = IS_BROWSER_ENV;
2126
+ let level = 'info';
2127
+ return {
2128
+ enableColors: (uc) => (useColors = uc),
2129
+ getLevel: () => level,
2130
+ setLevel: (l) => (level = l),
2131
+ emoji: (e) => e,
2132
+ info: console.log.bind(console),
2133
+ warn: console.warn.bind(console),
2134
+ error: console.error.bind(console),
2135
+ debug: console.debug.bind(console),
2136
+ red: (msg) => msg,
2137
+ green: (msg) => msg,
2138
+ yellow: (msg) => msg,
2139
+ blue: (msg) => msg,
2140
+ magenta: (msg) => msg,
2141
+ cyan: (msg) => msg,
2142
+ gray: (msg) => msg,
2143
+ bold: (msg) => msg,
2144
+ dim: (msg) => msg,
2145
+ bgRed: (msg) => msg,
2146
+ createTimeSpan: (_startMsg, _debug = false) => ({
2147
+ duration: () => 0,
2148
+ finish: () => 0,
2149
+ }),
2150
+ printDiagnostics(diagnostics) {
2151
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
2152
+ },
2153
+ };
2154
+ };
2155
+ const logDiagnostic = (diagnostic, useColors) => {
2156
+ let color = BLUE;
2157
+ let prefix = 'Build';
2158
+ let msg = '';
2159
+ if (diagnostic.level === 'error') {
2160
+ color = RED;
2161
+ prefix = 'Error';
2162
+ }
2163
+ else if (diagnostic.level === 'warn') {
2164
+ color = YELLOW;
2165
+ prefix = 'Warning';
2166
+ }
2167
+ if (diagnostic.header) {
2168
+ prefix = diagnostic.header;
2169
+ }
2170
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
2171
+ if (filePath) {
2172
+ msg += filePath;
2173
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
2174
+ msg += ', line ' + diagnostic.lineNumber;
2175
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
2176
+ msg += ', column ' + diagnostic.columnNumber;
2177
+ }
2178
+ }
2179
+ msg += '\n';
2180
+ }
2181
+ msg += diagnostic.messageText;
2182
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
2183
+ diagnostic.lines.forEach((l) => {
2184
+ msg += '\n' + l.lineNumber + ': ' + l.text;
2185
+ });
2186
+ msg += '\n';
2187
+ }
2188
+ if (useColors) {
2189
+ const styledPrefix = [
2190
+ '%c' + prefix,
2191
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
2192
+ ];
2193
+ console.log(...styledPrefix, msg);
2194
+ }
2195
+ else if (diagnostic.level === 'error') {
2196
+ console.error(msg);
2197
+ }
2198
+ else if (diagnostic.level === 'warn') {
2199
+ console.warn(msg);
2200
+ }
2201
+ else {
2202
+ console.log(msg);
2203
+ }
2204
+ };
2205
+ const YELLOW = `#f39c12`;
2206
+ const RED = `#c0392b`;
2207
+ const BLUE = `#3498db`;
2208
+
1797
2209
  const run = async (init) => {
1798
2210
  const { args, logger, sys } = init;
1799
2211
  try {
1800
- const flags = parseFlags(args, sys);
2212
+ const flags = parseFlags(args);
1801
2213
  const task = flags.task;
1802
2214
  if (flags.debug || flags.verbose) {
1803
2215
  logger.setLevel('debug');
@@ -1809,7 +2221,7 @@ const run = async (init) => {
1809
2221
  sys.applyGlobalPatch(sys.getCurrentDirectory());
1810
2222
  }
1811
2223
  if (task === 'help' || flags.help) {
1812
- await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys);
2224
+ await taskHelp(createConfigFlags({ task: 'help', args }), logger, sys);
1813
2225
  return;
1814
2226
  }
1815
2227
  startupLog(logger, task);
@@ -1835,9 +2247,7 @@ const run = async (init) => {
1835
2247
  startupLogVersion(logger, task, coreCompiler);
1836
2248
  loadedCompilerLog(sys, logger, flags, coreCompiler);
1837
2249
  if (task === 'info') {
1838
- await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => {
1839
- await taskInfo(coreCompiler, sys, logger);
1840
- });
2250
+ taskInfo(coreCompiler, sys, logger);
1841
2251
  return;
1842
2252
  }
1843
2253
  const validated = await coreCompiler.loadConfig({
@@ -1858,7 +2268,7 @@ const run = async (init) => {
1858
2268
  sys.applyGlobalPatch(validated.config.rootDir);
1859
2269
  }
1860
2270
  await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
1861
- await telemetryAction(sys, validated.config, logger, coreCompiler, async () => {
2271
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
1862
2272
  await runTask(coreCompiler, validated.config, task, sys);
1863
2273
  });
1864
2274
  }
@@ -1870,44 +2280,56 @@ const run = async (init) => {
1870
2280
  }
1871
2281
  }
1872
2282
  };
2283
+ /**
2284
+ * Run a specified task
2285
+ * @param coreCompiler an instance of a minimal, bootstrap compiler for running the specified task
2286
+ * @param config a configuration for the Stencil project to apply to the task run
2287
+ * @param task the task to run
2288
+ * @param sys the {@link CompilerSystem} for interacting with the operating system
2289
+ * @public
2290
+ */
1873
2291
  const runTask = async (coreCompiler, config, task, sys) => {
1874
- config.flags = config.flags || { task };
1875
- config.outputTargets = config.outputTargets || [];
2292
+ var _a, _b, _c;
2293
+ const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2294
+ const strictConfig = {
2295
+ ...config,
2296
+ flags: createConfigFlags((_b = config.flags) !== null && _b !== void 0 ? _b : { task }),
2297
+ logger,
2298
+ outputTargets: (_c = config.outputTargets) !== null && _c !== void 0 ? _c : [],
2299
+ sys: sys !== null && sys !== void 0 ? sys : coreCompiler.createSystem({ logger }),
2300
+ };
1876
2301
  switch (task) {
1877
2302
  case 'build':
1878
- await taskBuild(coreCompiler, config, sys);
2303
+ await taskBuild(coreCompiler, strictConfig);
1879
2304
  break;
1880
2305
  case 'docs':
1881
- await taskDocs(coreCompiler, config);
2306
+ await taskDocs(coreCompiler, strictConfig);
1882
2307
  break;
1883
2308
  case 'generate':
1884
2309
  case 'g':
1885
- await taskGenerate(coreCompiler, config);
2310
+ await taskGenerate(coreCompiler, strictConfig);
1886
2311
  break;
1887
2312
  case 'help':
1888
- await taskHelp(config, config.logger, sys);
2313
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1889
2314
  break;
1890
2315
  case 'prerender':
1891
- await taskPrerender(coreCompiler, config);
2316
+ await taskPrerender(coreCompiler, strictConfig);
1892
2317
  break;
1893
2318
  case 'serve':
1894
- await taskServe(config);
2319
+ await taskServe(strictConfig);
1895
2320
  break;
1896
2321
  case 'telemetry':
1897
- // TODO(STENCIL-148) make this parameter no longer optional, remove the surrounding if statement
1898
- if (sys) {
1899
- await taskTelemetry(config, sys, config.logger);
1900
- }
2322
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
1901
2323
  break;
1902
2324
  case 'test':
1903
- await taskTest(config);
2325
+ await taskTest(strictConfig);
1904
2326
  break;
1905
2327
  case 'version':
1906
2328
  console.log(coreCompiler.version);
1907
2329
  break;
1908
2330
  default:
1909
- config.logger.error(`${config.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
1910
- await taskHelp(config, config.logger, sys);
2331
+ strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid stencil command, please see the options below:`);
2332
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1911
2333
  return config.sys.exit(1);
1912
2334
  }
1913
2335
  };