@rindo/core 2.16.1 → 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 (49) hide show
  1. package/cli/config-flags.d.ts +102 -0
  2. package/cli/index.cjs +690 -223
  3. package/cli/index.d.ts +2 -1
  4. package/cli/index.js +690 -223
  5. package/cli/package.json +1 -1
  6. package/compiler/package.json +1 -1
  7. package/compiler/rindo.js +856 -289
  8. package/compiler/rindo.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/rindo-private.d.ts +6 -2
  28. package/internal/rindo-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 +5 -3
  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 +124 -69
  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/testing-utils.d.ts +74 -2
  49. package/testing/testing.d.ts +2 -2
package/cli/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Rindo CLI (CommonJS) v2.16.1 | MIT Licensed | https://rindojs.web.app
2
+ Rindo CLI (CommonJS) v2.17.1 | MIT Licensed | https://rindojs.web.app
3
3
  */
4
4
  'use strict';
5
5
 
@@ -25,8 +25,50 @@ function _interopNamespace(e) {
25
25
  return Object.freeze(n);
26
26
  }
27
27
 
28
- const toLowerCase = (str) => str.toLowerCase();
29
- const dashToPascalCase = (str) => toLowerCase(str)
28
+ /**
29
+ * This sets the log level hierarchy for our terminal logger, ranging from
30
+ * most to least verbose.
31
+ *
32
+ * Ordering the levels like this lets us easily check whether we should log a
33
+ * message at a given time. For instance, if the log level is set to `'warn'`,
34
+ * then anything passed to the logger with level `'warn'` or `'error'` should
35
+ * be logged, but we should _not_ log anything with level `'info'` or `'debug'`.
36
+ *
37
+ * If we have a current log level `currentLevel` and a message with level
38
+ * `msgLevel` is passed to the logger, we can determine whether or not we should
39
+ * log it by checking if the log level on the message is further up or at the
40
+ * same level in the hierarchy than `currentLevel`, like so:
41
+ *
42
+ * ```ts
43
+ * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel)
44
+ * ```
45
+ *
46
+ * NOTE: for the reasons described above, do not change the order of the entries
47
+ * in this array without good reason!
48
+ */
49
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
50
+
51
+ /**
52
+ * Convert a string from PascalCase to dash-case
53
+ *
54
+ * @param str the string to convert
55
+ * @returns a converted string
56
+ */
57
+ const toDashCase = (str) => str
58
+ .replace(/([A-Z0-9])/g, (match) => ` ${match[0]}`)
59
+ .trim()
60
+ .split(' ')
61
+ .join('-')
62
+ .toLowerCase();
63
+ /**
64
+ * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase,
65
+ * or whatever you call it!)
66
+ *
67
+ * @param str a string to convert
68
+ * @returns a converted string
69
+ */
70
+ const dashToPascalCase = (str) => str
71
+ .toLowerCase()
30
72
  .split('-')
31
73
  .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
32
74
  .join('');
@@ -299,22 +341,211 @@ const validateComponentTag = (tag) => {
299
341
  return undefined;
300
342
  };
301
343
 
344
+ /**
345
+ * All the Boolean options supported by the Rindo CLI
346
+ */
347
+ const BOOLEAN_CLI_ARGS = [
348
+ 'build',
349
+ 'cache',
350
+ 'checkVersion',
351
+ 'ci',
352
+ 'compare',
353
+ 'debug',
354
+ 'dev',
355
+ 'devtools',
356
+ 'docs',
357
+ 'e2e',
358
+ 'es5',
359
+ 'esm',
360
+ 'headless',
361
+ 'help',
362
+ 'log',
363
+ 'open',
364
+ 'prerender',
365
+ 'prerenderExternal',
366
+ 'prod',
367
+ 'profile',
368
+ 'serviceWorker',
369
+ 'screenshot',
370
+ 'serve',
371
+ 'skipNodeCheck',
372
+ 'spec',
373
+ 'ssr',
374
+ 'stats',
375
+ 'updateScreenshot',
376
+ 'verbose',
377
+ 'version',
378
+ 'watch',
379
+ // JEST CLI OPTIONS
380
+ 'all',
381
+ 'automock',
382
+ 'bail',
383
+ // 'cache', Rindo already supports this argument
384
+ 'changedFilesWithAncestor',
385
+ // 'ci', Rindo already supports this argument
386
+ 'clearCache',
387
+ 'clearMocks',
388
+ 'collectCoverage',
389
+ 'color',
390
+ 'colors',
391
+ 'coverage',
392
+ // 'debug', Rindo already supports this argument
393
+ 'detectLeaks',
394
+ 'detectOpenHandles',
395
+ 'errorOnDeprecated',
396
+ 'expand',
397
+ 'findRelatedTests',
398
+ 'forceExit',
399
+ 'init',
400
+ 'injectGlobals',
401
+ 'json',
402
+ 'lastCommit',
403
+ 'listTests',
404
+ 'logHeapUsage',
405
+ 'noStackTrace',
406
+ 'notify',
407
+ 'onlyChanged',
408
+ 'onlyFailures',
409
+ 'passWithNoTests',
410
+ 'resetMocks',
411
+ 'resetModules',
412
+ 'restoreMocks',
413
+ 'runInBand',
414
+ 'runTestsByPath',
415
+ 'showConfig',
416
+ 'silent',
417
+ 'skipFilter',
418
+ 'testLocationInResults',
419
+ 'updateSnapshot',
420
+ 'useStderr',
421
+ // 'verbose', Rindo already supports this argument
422
+ // 'version', Rindo already supports this argument
423
+ // 'watch', Rindo already supports this argument
424
+ 'watchAll',
425
+ 'watchman',
426
+ ];
427
+ /**
428
+ * All the Number options supported by the Rindo CLI
429
+ */
430
+ const NUMBER_CLI_ARGS = [
431
+ 'port',
432
+ // JEST CLI ARGS
433
+ 'maxConcurrency',
434
+ 'testTimeout',
435
+ ];
436
+ /**
437
+ * All the String options supported by the Rindo CLI
438
+ */
439
+ const STRING_CLI_ARGS = [
440
+ 'address',
441
+ 'config',
442
+ 'docsApi',
443
+ 'docsJson',
444
+ 'emulate',
445
+ 'root',
446
+ 'screenshotConnector',
447
+ // JEST CLI ARGS
448
+ 'cacheDirectory',
449
+ 'changedSince',
450
+ 'collectCoverageFrom',
451
+ // 'config', Rindo already supports this argument
452
+ 'coverageDirectory',
453
+ 'coverageThreshold',
454
+ 'env',
455
+ 'filter',
456
+ 'globalSetup',
457
+ 'globalTeardown',
458
+ 'globals',
459
+ 'haste',
460
+ 'moduleNameMapper',
461
+ 'notifyMode',
462
+ 'outputFile',
463
+ 'preset',
464
+ 'prettierPath',
465
+ 'resolver',
466
+ 'rootDir',
467
+ 'runner',
468
+ 'testEnvironment',
469
+ 'testEnvironmentOptions',
470
+ 'testFailureExitCode',
471
+ 'testNamePattern',
472
+ 'testResultsProcessor',
473
+ 'testRunner',
474
+ 'testSequencer',
475
+ 'testURL',
476
+ 'timers',
477
+ 'transform',
478
+ // ARRAY ARGS
479
+ 'collectCoverageOnlyFrom',
480
+ 'coveragePathIgnorePatterns',
481
+ 'coverageReporters',
482
+ 'moduleDirectories',
483
+ 'moduleFileExtensions',
484
+ 'modulePathIgnorePatterns',
485
+ 'modulePaths',
486
+ 'projects',
487
+ 'reporters',
488
+ 'roots',
489
+ 'selectProjects',
490
+ 'setupFiles',
491
+ 'setupFilesAfterEnv',
492
+ 'snapshotSerializers',
493
+ 'testMatch',
494
+ 'testPathIgnorePatterns',
495
+ 'testPathPattern',
496
+ 'testRegex',
497
+ 'transformIgnorePatterns',
498
+ 'unmockedModulePathPatterns',
499
+ 'watchPathIgnorePatterns',
500
+ ];
501
+ /**
502
+ * All the CLI arguments which may have string or number values
503
+ *
504
+ * `maxWorkers` is an argument which is used both by Rindo _and_ by Jest,
505
+ * which means that we need to support parsing both string and number values.
506
+ */
507
+ const STRING_NUMBER_CLI_ARGS = ['maxWorkers'];
508
+ /**
509
+ * All the LogLevel-type options supported by the Rindo CLI
510
+ *
511
+ * This is a bit silly since there's only one such argument atm,
512
+ * but this approach lets us make sure that we're handling all
513
+ * our arguments in a type-safe way.
514
+ */
515
+ const LOG_LEVEL_CLI_ARGS = ['logLevel'];
516
+ /**
517
+ * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'`
518
+ */
519
+ const CLI_ARG_ALIASES = {
520
+ config: 'c',
521
+ help: 'h',
522
+ port: 'p',
523
+ version: 'v',
524
+ };
525
+
526
+ /**
527
+ * Parse command line arguments into a structured `ConfigFlags` object
528
+ *
529
+ * @param args an array of config flags
530
+ * @param sys an optional compiler system
531
+ * @returns a structured ConfigFlags object
532
+ */
302
533
  const parseFlags = (args, sys) => {
303
534
  const flags = {
304
535
  task: null,
305
536
  args: [],
306
537
  knownArgs: [],
307
- unknownArgs: null,
538
+ unknownArgs: [],
308
539
  };
309
540
  // cmd line has more priority over npm scripts cmd
310
- flags.args = args.slice();
541
+ flags.args = Array.isArray(args) ? args.slice() : [];
311
542
  if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
312
543
  flags.task = flags.args[0];
313
544
  }
314
- parseArgs(flags, flags.args, flags.knownArgs);
545
+ parseArgs(flags, flags.args);
315
546
  if (sys && sys.name === 'node') {
316
547
  const envArgs = getNpmConfigEnvArgs(sys);
317
- parseArgs(flags, envArgs, flags.knownArgs);
548
+ parseArgs(flags, envArgs);
318
549
  envArgs.forEach((envArg) => {
319
550
  if (!flags.args.includes(envArg)) {
320
551
  flags.args.push(envArg);
@@ -333,185 +564,230 @@ const parseFlags = (args, sys) => {
333
564
  return flags;
334
565
  };
335
566
  /**
336
- * Parse command line arguments that are whitelisted via the BOOLEAN_ARG_OPTS,
337
- * STRING_ARG_OPTS, and NUMBER_ARG_OPTS arrays in this file. Handles leading
338
- * dashes on arguments, aliases that are defined for a small number of argument
339
- * types, and parsing values for non-boolean arguments (e.g. port number).
567
+ * Parse command line arguments that are enumerated in the `config-flags`
568
+ * module. Handles leading dashes on arguments, aliases that are defined for a
569
+ * small number of arguments, and parsing values for non-boolean arguments
570
+ * (e.g. port number for the dev server).
340
571
  *
341
- * @param flags a ConfigFlags object
342
- * @param args an array of command-line arguments to parse
343
- * @param knownArgs an array to which all recognized, legal arguments are added
572
+ * @param flags a ConfigFlags object to which parsed arguments will be added
573
+ * @param args an array of command-line arguments to parse
344
574
  */
345
- const parseArgs = (flags, args, knownArgs) => {
346
- BOOLEAN_ARG_OPTS.forEach((booleanName) => {
347
- const alias = ARG_OPTS_ALIASES[booleanName];
348
- const flagKey = configCase(booleanName);
349
- if (typeof flags[flagKey] !== 'boolean') {
350
- flags[flagKey] = null;
575
+ const parseArgs = (flags, args) => {
576
+ BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
577
+ STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
578
+ NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
579
+ STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
580
+ LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
581
+ };
582
+ /**
583
+ * Parse a boolean CLI argument. For these, we support the following formats:
584
+ *
585
+ * - `--booleanArg`
586
+ * - `--boolean-arg`
587
+ * - `--noBooleanArg`
588
+ * - `--no-boolean-arg`
589
+ *
590
+ * The final two variants should be parsed to a value of `false` on the config
591
+ * object.
592
+ *
593
+ * @param flags the config flags object, while we'll modify
594
+ * @param args our CLI arguments
595
+ * @param configCaseName the argument we want to look at right now
596
+ */
597
+ const parseBooleanArg = (flags, args, configCaseName) => {
598
+ // we support both dash-case and PascalCase versions of the parameter
599
+ // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS
600
+ const alias = CLI_ARG_ALIASES[configCaseName];
601
+ const dashCaseName = toDashCase(configCaseName);
602
+ if (typeof flags[configCaseName] !== 'boolean') {
603
+ flags[configCaseName] = null;
604
+ }
605
+ args.forEach((cmdArg) => {
606
+ let value;
607
+ if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) {
608
+ value = true;
351
609
  }
352
- args.forEach((cmdArg) => {
353
- if (cmdArg === `--${booleanName}`) {
354
- flags[flagKey] = true;
355
- knownArgs.push(cmdArg);
356
- }
357
- else if (cmdArg === `--${flagKey}`) {
358
- flags[flagKey] = true;
359
- knownArgs.push(cmdArg);
360
- }
361
- else if (cmdArg === `--no-${booleanName}`) {
362
- flags[flagKey] = false;
363
- knownArgs.push(cmdArg);
364
- }
365
- else if (cmdArg === `--no${dashToPascalCase(booleanName)}`) {
366
- flags[flagKey] = false;
367
- knownArgs.push(cmdArg);
368
- }
369
- else if (alias && cmdArg === `-${alias}`) {
370
- flags[flagKey] = true;
371
- knownArgs.push(cmdArg);
372
- }
373
- });
374
- });
375
- STRING_ARG_OPTS.forEach((stringName) => {
376
- const alias = ARG_OPTS_ALIASES[stringName];
377
- const flagKey = configCase(stringName);
378
- if (typeof flags[flagKey] !== 'string') {
379
- flags[flagKey] = null;
610
+ else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) {
611
+ value = false;
380
612
  }
381
- for (let i = 0; i < args.length; i++) {
382
- const cmdArg = args[i];
383
- if (cmdArg.startsWith(`--${stringName}=`)) {
384
- const values = cmdArg.split('=');
385
- values.shift();
386
- flags[flagKey] = values.join('=');
387
- knownArgs.push(cmdArg);
388
- }
389
- else if (cmdArg === `--${stringName}`) {
390
- flags[flagKey] = args[i + 1];
391
- knownArgs.push(cmdArg);
392
- knownArgs.push(args[i + 1]);
393
- }
394
- else if (cmdArg === `--${flagKey}`) {
395
- flags[flagKey] = args[i + 1];
396
- knownArgs.push(cmdArg);
397
- knownArgs.push(args[i + 1]);
398
- }
399
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
400
- const values = cmdArg.split('=');
401
- values.shift();
402
- flags[flagKey] = values.join('=');
403
- knownArgs.push(cmdArg);
404
- }
405
- else if (alias) {
406
- if (cmdArg.startsWith(`-${alias}=`)) {
407
- const values = cmdArg.split('=');
408
- values.shift();
409
- flags[flagKey] = values.join('=');
410
- knownArgs.push(cmdArg);
411
- }
412
- else if (cmdArg === `-${alias}`) {
413
- flags[flagKey] = args[i + 1];
414
- knownArgs.push(args[i + 1]);
415
- }
416
- }
613
+ else if (alias && cmdArg === `-${alias}`) {
614
+ value = true;
615
+ }
616
+ if (value !== undefined && cmdArg !== undefined) {
617
+ flags[configCaseName] = value;
618
+ flags.knownArgs.push(cmdArg);
417
619
  }
418
620
  });
419
- NUMBER_ARG_OPTS.forEach((numberName) => {
420
- const alias = ARG_OPTS_ALIASES[numberName];
421
- const flagKey = configCase(numberName);
422
- if (typeof flags[flagKey] !== 'number') {
423
- flags[flagKey] = null;
621
+ };
622
+ /**
623
+ * Parse a string CLI argument
624
+ *
625
+ * @param flags the config flags object, while we'll modify
626
+ * @param args our CLI arguments
627
+ * @param configCaseName the argument we want to look at right now
628
+ */
629
+ const parseStringArg = (flags, args, configCaseName) => {
630
+ if (typeof flags[configCaseName] !== 'string') {
631
+ flags[configCaseName] = null;
632
+ }
633
+ const { value, matchingArg } = getValue(args, configCaseName);
634
+ if (value !== undefined && matchingArg !== undefined) {
635
+ flags[configCaseName] = value;
636
+ flags.knownArgs.push(matchingArg);
637
+ flags.knownArgs.push(value);
638
+ }
639
+ };
640
+ /**
641
+ * Parse a number CLI argument
642
+ *
643
+ * @param flags the config flags object, while we'll modify
644
+ * @param args our CLI arguments
645
+ * @param configCaseName the argument we want to look at right now
646
+ */
647
+ const parseNumberArg = (flags, args, configCaseName) => {
648
+ if (typeof flags[configCaseName] !== 'number') {
649
+ flags[configCaseName] = null;
650
+ }
651
+ const { value, matchingArg } = getValue(args, configCaseName);
652
+ if (value !== undefined && matchingArg !== undefined) {
653
+ flags[configCaseName] = parseInt(value, 10);
654
+ flags.knownArgs.push(matchingArg);
655
+ flags.knownArgs.push(value);
656
+ }
657
+ };
658
+ /**
659
+ * Parse a CLI argument which may be either a string or a number
660
+ *
661
+ * @param flags the config flags object, while we'll modify
662
+ * @param args our CLI arguments
663
+ * @param configCaseName the argument we want to look at right now
664
+ */
665
+ const parseStringNumberArg = (flags, args, configCaseName) => {
666
+ if (!['number', 'string'].includes(typeof flags[configCaseName])) {
667
+ flags[configCaseName] = null;
668
+ }
669
+ const { value, matchingArg } = getValue(args, configCaseName);
670
+ if (value !== undefined && matchingArg !== undefined) {
671
+ if (CLI_ARG_STRING_REGEX.test(value)) {
672
+ // if it matches the regex we treat it like a string
673
+ flags[configCaseName] = value;
424
674
  }
425
- for (let i = 0; i < args.length; i++) {
426
- const cmdArg = args[i];
427
- if (cmdArg.startsWith(`--${numberName}=`)) {
428
- const values = cmdArg.split('=');
429
- values.shift();
430
- flags[flagKey] = parseInt(values.join(''), 10);
431
- knownArgs.push(cmdArg);
432
- }
433
- else if (cmdArg === `--${numberName}`) {
434
- flags[flagKey] = parseInt(args[i + 1], 10);
435
- knownArgs.push(args[i + 1]);
436
- }
437
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
438
- const values = cmdArg.split('=');
439
- values.shift();
440
- flags[flagKey] = parseInt(values.join(''), 10);
441
- knownArgs.push(cmdArg);
442
- }
443
- else if (cmdArg === `--${flagKey}`) {
444
- flags[flagKey] = parseInt(args[i + 1], 10);
445
- knownArgs.push(args[i + 1]);
675
+ else {
676
+ // it was a number, great!
677
+ flags[configCaseName] = Number(value);
678
+ }
679
+ flags.knownArgs.push(matchingArg);
680
+ flags.knownArgs.push(value);
681
+ }
682
+ };
683
+ /**
684
+ * We use this regular expression to detect CLI parameters which
685
+ * should be parsed as string values (as opposed to numbers) for
686
+ * the argument types for which we support both a string and a
687
+ * number value.
688
+ *
689
+ * The regex tests for the presence of at least one character which is
690
+ * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
691
+ * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
692
+ * support the admittedly unlikely use of scientific notation, like `"4e+0"`
693
+ * for `4`).
694
+ *
695
+ * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
696
+ * `"5.0"`. If it matches a given string we conclude that the string should
697
+ * be parsed as a string literal, rather than using `Number` to convert it
698
+ * to a number.
699
+ */
700
+ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
701
+ /**
702
+ * Parse a LogLevel CLI argument. These can be only a specific
703
+ * set of strings, so this function takes care of validating that
704
+ * the value is correct.
705
+ *
706
+ * @param flags the config flags object, while we'll modify
707
+ * @param args our CLI arguments
708
+ * @param configCaseName the argument we want to look at right now
709
+ */
710
+ const parseLogLevelArg = (flags, args, configCaseName) => {
711
+ if (typeof flags[configCaseName] !== 'string') {
712
+ flags[configCaseName] = null;
713
+ }
714
+ const { value, matchingArg } = getValue(args, configCaseName);
715
+ if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) {
716
+ flags[configCaseName] = value;
717
+ flags.knownArgs.push(matchingArg);
718
+ flags.knownArgs.push(value);
719
+ }
720
+ };
721
+ /**
722
+ * Helper for pulling values out from the raw array of CLI arguments. This logic
723
+ * is shared between a few different types of CLI args.
724
+ *
725
+ * We look for arguments in the following formats:
726
+ *
727
+ * - `--my-cli-argument value`
728
+ * - `--my-cli-argument=value`
729
+ * - `--myCliArgument value`
730
+ * - `--myCliArgument=value`
731
+ *
732
+ * We also check for shortened aliases, which we define for a few arguments.
733
+ *
734
+ * @param args the CLI args we're dealing with
735
+ * @param configCaseName the ConfigFlag key which we're looking to pull out a value for
736
+ * @returns the value for the flag as well as the exact string which it matched from
737
+ * the user input.
738
+ */
739
+ const getValue = (args, configCaseName) => {
740
+ // for some CLI args we have a short alias, like 'c' for 'config'
741
+ const alias = CLI_ARG_ALIASES[configCaseName];
742
+ // we support supplying arguments in both dash-case and configCase
743
+ // for ease of use
744
+ const dashCaseName = toDashCase(configCaseName);
745
+ let value;
746
+ let matchingArg;
747
+ args.forEach((arg, i) => {
748
+ if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) {
749
+ value = getEqualsValue(arg);
750
+ matchingArg = arg;
751
+ }
752
+ else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) {
753
+ value = args[i + 1];
754
+ matchingArg = arg;
755
+ }
756
+ else if (alias) {
757
+ if (arg.startsWith(`-${alias}=`)) {
758
+ value = getEqualsValue(arg);
759
+ matchingArg = arg;
446
760
  }
447
- else if (alias) {
448
- if (cmdArg.startsWith(`-${alias}=`)) {
449
- const values = cmdArg.split('=');
450
- values.shift();
451
- flags[flagKey] = parseInt(values.join(''), 10);
452
- knownArgs.push(cmdArg);
453
- }
454
- else if (cmdArg === `-${alias}`) {
455
- flags[flagKey] = parseInt(args[i + 1], 10);
456
- knownArgs.push(args[i + 1]);
457
- }
761
+ else if (arg === `-${alias}`) {
762
+ value = args[i + 1];
763
+ matchingArg = arg;
458
764
  }
459
765
  }
460
766
  });
767
+ return { value, matchingArg };
461
768
  };
462
- const configCase = (prop) => {
463
- prop = dashToPascalCase(prop);
464
- return prop.charAt(0).toLowerCase() + prop.slice(1);
465
- };
466
- const BOOLEAN_ARG_OPTS = [
467
- 'build',
468
- 'cache',
469
- 'check-version',
470
- 'ci',
471
- 'compare',
472
- 'debug',
473
- 'dev',
474
- 'devtools',
475
- 'docs',
476
- 'e2e',
477
- 'es5',
478
- 'esm',
479
- 'headless',
480
- 'help',
481
- 'log',
482
- 'open',
483
- 'prerender',
484
- 'prerender-external',
485
- 'prod',
486
- 'profile',
487
- 'service-worker',
488
- 'screenshot',
489
- 'serve',
490
- 'skip-node-check',
491
- 'spec',
492
- 'ssr',
493
- 'stats',
494
- 'update-screenshot',
495
- 'verbose',
496
- 'version',
497
- 'watch',
498
- ];
499
- const NUMBER_ARG_OPTS = ['max-workers', 'port'];
500
- const STRING_ARG_OPTS = [
501
- 'address',
502
- 'config',
503
- 'docs-json',
504
- 'emulate',
505
- 'log-level',
506
- 'root',
507
- 'screenshot-connector',
508
- ];
509
- const ARG_OPTS_ALIASES = {
510
- config: 'c',
511
- help: 'h',
512
- port: 'p',
513
- version: 'v',
514
- };
769
+ /**
770
+ * When a parameter is set in the format `--foobar=12` at the CLI (as opposed to
771
+ * `--foobar 12`) we want to get the value after the `=` sign
772
+ *
773
+ * @param commandArgument the arg in question
774
+ * @returns the value after the `=`
775
+ */
776
+ const getEqualsValue = (commandArgument) => commandArgument.split('=').slice(1).join('=');
777
+ /**
778
+ * Small helper for getting type-system-level assurance that a `string` can be
779
+ * narrowed to a `LogLevel`
780
+ *
781
+ * @param maybeLogLevel the string to check
782
+ * @returns whether this is a `LogLevel`
783
+ */
784
+ const isLogLevel = (maybeLogLevel) =>
785
+ // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
786
+ // boolean` so a `string` cannot be passed to `includes` on a
787
+ // `ReadonlyArray` 😢 thus we `as any`
788
+ //
789
+ // see microsoft/TypeScript#31018 for some discussion of this
790
+ LOG_LEVELS.includes(maybeLogLevel);
515
791
  const getNpmConfigEnvArgs = (sys) => {
516
792
  // process.env.npm_config_argv
517
793
  // {"remain":["4444"],"cooked":["run","serve","--port","4444"],"original":["run","serve","--port","4444"]}
@@ -532,7 +808,7 @@ const getNpmConfigEnvArgs = (sys) => {
532
808
  const dependencies = [
533
809
  {
534
810
  name: "@rindo/core",
535
- version: "2.16.1",
811
+ version: "2.17.1",
536
812
  main: "compiler/rindo.js",
537
813
  resources: [
538
814
  "package.json",
@@ -857,12 +1133,11 @@ const tryFn = async (fn, ...args) => {
857
1133
  }
858
1134
  return null;
859
1135
  };
860
- const isInteractive = (sys, config, object) => {
861
- var _a;
1136
+ const isInteractive = (sys, flags, object) => {
862
1137
  const terminalInfo = object ||
863
1138
  Object.freeze({
864
1139
  tty: sys.isTTY() ? true : false,
865
- ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!((_a = config.flags) === null || _a === void 0 ? void 0 : _a.ci),
1140
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!flags.ci,
866
1141
  });
867
1142
  return terminalInfo.tty && !terminalInfo.ci;
868
1143
  };
@@ -886,19 +1161,19 @@ async function readJson(sys, path) {
886
1161
  }
887
1162
  /**
888
1163
  * Does the command have the debug flag?
889
- * @param config The config passed into the Rindo command
1164
+ * @param flags The configuration flags passed into the Rindo command
890
1165
  * @returns true if --debug has been passed, otherwise false
891
1166
  */
892
- function hasDebug(config) {
893
- return config.flags.debug;
1167
+ function hasDebug(flags) {
1168
+ return flags.debug;
894
1169
  }
895
1170
  /**
896
1171
  * Does the command have the verbose and debug flags?
897
- * @param config The config passed into the Rindo command
1172
+ * @param flags The configuration flags passed into the Rindo command
898
1173
  * @returns true if both --debug and --verbose have been passed, otherwise false
899
1174
  */
900
- function hasVerbose(config) {
901
- return config.flags.verbose && hasDebug(config);
1175
+ function hasVerbose(flags) {
1176
+ return flags.verbose && hasDebug(flags);
902
1177
  }
903
1178
 
904
1179
  /**
@@ -909,7 +1184,7 @@ function hasVerbose(config) {
909
1184
  * @returns true if telemetry should be sent, false otherwise
910
1185
  */
911
1186
  async function shouldTrack(config, sys, ci) {
912
- return !ci && isInteractive(sys, config) && (await checkTelemetry(sys));
1187
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
913
1188
  }
914
1189
 
915
1190
  const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
@@ -966,7 +1241,9 @@ async function updateConfig(sys, newOptions) {
966
1241
  return await writeConfig(sys, Object.assign(config, newOptions));
967
1242
  }
968
1243
 
1244
+ const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
969
1245
  const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1246
+ const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
970
1247
  const DOCS_CUSTOM = 'docs-custom';
971
1248
  const DOCS_JSON = 'docs-json';
972
1249
  const DOCS_README = 'docs-readme';
@@ -978,11 +1255,10 @@ const WWW = 'www';
978
1255
  *
979
1256
  * @param sys The system where the command is invoked
980
1257
  * @param config The config passed into the Rindo command
981
- * @param logger The tool used to do logging
982
1258
  * @param coreCompiler The compiler used to do builds
983
1259
  * @param result The results of a compiler build.
984
1260
  */
985
- async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) {
1261
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
986
1262
  const tracking = await shouldTrack(config, sys, config.flags.ci);
987
1263
  if (!tracking) {
988
1264
  return;
@@ -990,20 +1266,19 @@ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, r
990
1266
  const component_count = Object.keys(result.componentGraph).length;
991
1267
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
992
1268
  await sendMetric(sys, config, 'rindo_cli_command', data);
993
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1269
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
994
1270
  }
995
1271
  /**
996
1272
  * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
1273
+ *
997
1274
  * @param sys The system where the command is invoked
998
1275
  * @param config The config passed into the Rindo command
999
- * @param logger The tool used to do logging
1000
1276
  * @param coreCompiler The compiler used to do builds
1001
1277
  * @param action A Promise-based function to call in order to get the duration of any given command.
1002
1278
  * @returns void
1003
1279
  */
1004
- async function telemetryAction(sys, config, logger, coreCompiler, action) {
1005
- var _a;
1006
- const tracking = await shouldTrack(config, sys, !!((_a = config === null || config === void 0 ? void 0 : config.flags) === null || _a === void 0 ? void 0 : _a.ci));
1280
+ async function telemetryAction(sys, config, coreCompiler, action) {
1281
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1007
1282
  let duration = undefined;
1008
1283
  let error;
1009
1284
  if (action) {
@@ -1023,7 +1298,7 @@ async function telemetryAction(sys, config, logger, coreCompiler, action) {
1023
1298
  }
1024
1299
  const data = await prepareData(coreCompiler, config, sys, duration);
1025
1300
  await sendMetric(sys, config, 'rindo_cli_command', data);
1026
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1301
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1027
1302
  if (error) {
1028
1303
  throw error;
1029
1304
  }
@@ -1039,6 +1314,16 @@ async function getActiveTargets(config) {
1039
1314
  const result = config.outputTargets.map((t) => t.type);
1040
1315
  return Array.from(new Set(result));
1041
1316
  }
1317
+ /**
1318
+ * Prepare data for telemetry
1319
+ *
1320
+ * @param coreCompiler the core compiler
1321
+ * @param config the current Rindo config
1322
+ * @param sys the compiler system instance in use
1323
+ * @param duration_ms the duration of the action being tracked
1324
+ * @param component_count the number of components being built (optional)
1325
+ * @returns a Promise wrapping data for the telemetry endpoint
1326
+ */
1042
1327
  const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1043
1328
  const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1044
1329
  const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
@@ -1051,6 +1336,7 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1051
1336
  const cpu_model = sys.details.cpuModel;
1052
1337
  const build = coreCompiler.buildId || 'unknown';
1053
1338
  const has_app_pwa_config = hasAppTarget(config);
1339
+ const anonymizedConfig = anonymizeConfigForTelemetry(config);
1054
1340
  return {
1055
1341
  yarn,
1056
1342
  duration_ms,
@@ -1070,12 +1356,83 @@ const prepareData = async (coreCompiler, config, sys, duration_ms, component_cou
1070
1356
  typescript,
1071
1357
  rollup,
1072
1358
  has_app_pwa_config,
1359
+ config: anonymizedConfig,
1073
1360
  };
1074
1361
  };
1362
+ // props in output targets for which we retain their original values when
1363
+ // preparing a config for telemetry
1364
+ //
1365
+ // we omit the values of all other fields on output targets.
1366
+ const OUTPUT_TARGET_KEYS_TO_KEEP = ['type'];
1367
+ // top-level config props that we anonymize for telemetry
1368
+ const CONFIG_PROPS_TO_ANONYMIZE = [
1369
+ 'rootDir',
1370
+ 'fsNamespace',
1371
+ 'packageJsonFilePath',
1372
+ 'namespace',
1373
+ 'srcDir',
1374
+ 'srcIndexHtml',
1375
+ 'buildLogFilePath',
1376
+ 'cacheDir',
1377
+ 'configPath',
1378
+ 'tsconfig',
1379
+ ];
1380
+ // Props we delete entirely from the config for telemetry
1381
+ //
1382
+ // TODO: Investigate improving anonymization for tsCompilerOptions and devServer
1383
+ const CONFIG_PROPS_TO_DELETE = ['sys', 'logger', 'tsCompilerOptions', 'devServer'];
1384
+ /**
1385
+ * Anonymize the config for telemetry, replacing potentially revealing config props
1386
+ * with a placeholder string if they are present (this lets us still track how frequently
1387
+ * these config options are being used)
1388
+ *
1389
+ * @param config the config to anonymize
1390
+ * @returns an anonymized copy of the same config
1391
+ */
1392
+ const anonymizeConfigForTelemetry = (config) => {
1393
+ var _a;
1394
+ const anonymizedConfig = { ...config };
1395
+ for (const prop of CONFIG_PROPS_TO_ANONYMIZE) {
1396
+ if (anonymizedConfig[prop] !== undefined) {
1397
+ anonymizedConfig[prop] = 'omitted';
1398
+ }
1399
+ }
1400
+ anonymizedConfig.outputTargets = ((_a = config.outputTargets) !== null && _a !== void 0 ? _a : []).map((target) => {
1401
+ // Anonymize the outputTargets on our configuration, taking advantage of the
1402
+ // optional 2nd argument to `JSON.stringify`. If anything is not a string
1403
+ // we retain it so that any nested properties are handled, else we check
1404
+ // whether it's in our 'keep' list to decide whether to keep it or replace it
1405
+ // with `"omitted"`.
1406
+ const anonymizedOT = JSON.parse(JSON.stringify(target, (key, value) => {
1407
+ if (!(typeof value === 'string')) {
1408
+ return value;
1409
+ }
1410
+ if (OUTPUT_TARGET_KEYS_TO_KEEP.includes(key)) {
1411
+ return value;
1412
+ }
1413
+ return 'omitted';
1414
+ }));
1415
+ // this prop has to be handled separately because it is an array
1416
+ // so the replace function above will be called with all of its
1417
+ // members, giving us `["omitted", "omitted", ...]`.
1418
+ //
1419
+ // Instead, we check for its presence and manually copy over.
1420
+ if (isOutputTargetHydrate(target) && target.external) {
1421
+ anonymizedOT['external'] = target.external.concat();
1422
+ }
1423
+ return anonymizedOT;
1424
+ });
1425
+ // TODO: Investigate improving anonymization for tsCompilerOptions and devServer
1426
+ for (const prop of CONFIG_PROPS_TO_DELETE) {
1427
+ delete anonymizedConfig[prop];
1428
+ }
1429
+ return anonymizedConfig;
1430
+ };
1075
1431
  /**
1076
1432
  * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1077
1433
  * the dependencies and devDependencies properties. Pulls up the current installed version
1078
1434
  * of each package under the @rindo, @navify, and @jigra scopes.
1435
+ *
1079
1436
  * @param sys the system instance where telemetry is invoked
1080
1437
  * @param config the Rindo configuration associated with the current task that triggered telemetry
1081
1438
  * @returns an object listing all dev and production dependencies under the aforementioned scopes
@@ -1109,7 +1466,7 @@ async function getInstalledPackages(sys, config) {
1109
1466
  return { packages, packagesNoVersions };
1110
1467
  }
1111
1468
  catch (err) {
1112
- hasDebug(config) && console.error(err);
1469
+ hasDebug(config.flags) && console.error(err);
1113
1470
  return { packages, packagesNoVersions };
1114
1471
  }
1115
1472
  }
@@ -1158,6 +1515,7 @@ function sanitizeDeclaredVersion(version) {
1158
1515
  }
1159
1516
  /**
1160
1517
  * If telemetry is enabled, send a metric to an external data store
1518
+ *
1161
1519
  * @param sys the system instance where telemetry is invoked
1162
1520
  * @param config the Rindo configuration associated with the current task that triggered telemetry
1163
1521
  * @param name the name of a trackable metric. Note this name is not necessarily a scalar value to track, like
@@ -1173,10 +1531,11 @@ async function sendMetric(sys, config, name, value) {
1173
1531
  value,
1174
1532
  session_id,
1175
1533
  };
1176
- await sendTelemetry(sys, config, { type: 'telemetry', message });
1534
+ await sendTelemetry(sys, config, message);
1177
1535
  }
1178
1536
  /**
1179
1537
  * Used to read the config file's tokens.telemetry property.
1538
+ *
1180
1539
  * @param sys The system where the command is invoked
1181
1540
  * @returns string
1182
1541
  */
@@ -1198,7 +1557,7 @@ async function sendTelemetry(sys, config, data) {
1198
1557
  try {
1199
1558
  const now = new Date().toISOString();
1200
1559
  const body = {
1201
- metrics: [data.message],
1560
+ metrics: [data],
1202
1561
  sent_at: now,
1203
1562
  };
1204
1563
  // This request is only made if telemetry is on.
@@ -1209,15 +1568,15 @@ async function sendTelemetry(sys, config, data) {
1209
1568
  },
1210
1569
  body: JSON.stringify(body),
1211
1570
  });
1212
- hasVerbose(config) &&
1213
- console.debug('\nSent %O metric to events service (status: %O)', data.message.name, response.status, '\n');
1571
+ hasVerbose(config.flags) &&
1572
+ console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1214
1573
  if (response.status !== 204) {
1215
- hasVerbose(config) &&
1574
+ hasVerbose(config.flags) &&
1216
1575
  console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1217
1576
  }
1218
1577
  }
1219
1578
  catch (e) {
1220
- hasVerbose(config) && console.debug('Telemetry request failed:', e);
1579
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1221
1580
  }
1222
1581
  }
1223
1582
  /**
@@ -1274,7 +1633,7 @@ const taskBuild = async (coreCompiler, config, sys) => {
1274
1633
  const results = await compiler.build();
1275
1634
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1276
1635
  if (sys) {
1277
- await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results);
1636
+ await telemetryBuildFinishedAction(sys, config, coreCompiler, results);
1278
1637
  }
1279
1638
  await compiler.destroy();
1280
1639
  if (results.hasError) {
@@ -1312,7 +1671,8 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1312
1671
  typeof require === 'function' &&
1313
1672
  !!global.process &&
1314
1673
  typeof __filename === 'string' &&
1315
- (!global.origin || typeof global.origin !== 'string');
1674
+ (!global.origin || typeof global.origin !== 'string');
1675
+ const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1316
1676
 
1317
1677
  /**
1318
1678
  * Task to generate component boilerplate and write it to disk. This task can
@@ -1361,7 +1721,8 @@ const taskGenerate = async (coreCompiler, config) => {
1361
1721
  if (!writtenFiles) {
1362
1722
  return config.sys.exit(1);
1363
1723
  }
1364
- // TODO: Investigate moving these console.log calls to config.logger.info
1724
+ // We use `console.log` here rather than our `config.logger` because we don't want
1725
+ // our TUI messages to be prefixed with timestamps and so on.
1365
1726
  console.log();
1366
1727
  console.log(`${config.logger.gray('$')} rindo generate ${input}`);
1367
1728
  console.log();
@@ -1505,7 +1866,8 @@ export class ${toPascalCase(tagName)} {
1505
1866
  `;
1506
1867
  };
1507
1868
  /**
1508
- * Get the boilerplate for style.
1869
+ * Get the boilerplate for style for a generated component
1870
+ * @returns a boilerplate CSS block
1509
1871
  */
1510
1872
  const getStyleUrlBoilerplate = () => `:host {
1511
1873
  display: block;
@@ -1559,10 +1921,17 @@ describe('${tagName}', () => {
1559
1921
  */
1560
1922
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1561
1923
 
1562
- const taskTelemetry = async (config, sys, logger) => {
1924
+ /**
1925
+ * Entrypoint for the Telemetry task
1926
+ * @param flags configuration flags provided to Rindo when a task was called (either this task or a task that invokes
1927
+ * telemetry)
1928
+ * @param sys the abstraction for interfacing with the operating system
1929
+ * @param logger a logging implementation to log the results out to the user
1930
+ */
1931
+ const taskTelemetry = async (flags, sys, logger) => {
1563
1932
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1564
- const isEnabling = config.flags.args.includes('on');
1565
- const isDisabling = config.flags.args.includes('off');
1933
+ const isEnabling = flags.args.includes('on');
1934
+ const isDisabling = flags.args.includes('off');
1566
1935
  const INFORMATION = `Opt in or our of telemetry. Information about the data we collect is available on our website: ${logger.bold('https://rindojs.web.app/telemetry')}`;
1567
1936
  const THANK_YOU = `Thank you for helping to make Rindo better! 💖`;
1568
1937
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
@@ -1591,7 +1960,14 @@ const taskTelemetry = async (config, sys, logger) => {
1591
1960
  `);
1592
1961
  };
1593
1962
 
1594
- const taskHelp = async (config, logger, sys) => {
1963
+ /**
1964
+ * Entrypoint for the Help task, providing Rindo usage context to the user
1965
+ * @param flags configuration flags provided to Rindo when a task was call (either this task or a task that invokes
1966
+ * telemetry)
1967
+ * @param logger a logging implementation to log the results out to the user
1968
+ * @param sys the abstraction for interfacing with the operating system
1969
+ */
1970
+ const taskHelp = async (flags, logger, sys) => {
1595
1971
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1596
1972
  console.log(`
1597
1973
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
@@ -1624,7 +2000,7 @@ const taskHelp = async (config, logger, sys) => {
1624
2000
  `);
1625
2001
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1626
2002
  if (sys) {
1627
- await taskTelemetry(config, sys, logger);
2003
+ await taskTelemetry(flags, sys, logger);
1628
2004
  }
1629
2005
  console.log(`
1630
2006
  ${logger.bold('Examples:')}
@@ -1681,6 +2057,95 @@ const taskServe = async (config) => {
1681
2057
  });
1682
2058
  };
1683
2059
 
2060
+ /**
2061
+ * Creates an instance of a logger
2062
+ * @returns the new logger instance
2063
+ */
2064
+ const createLogger = () => {
2065
+ let useColors = IS_BROWSER_ENV;
2066
+ let level = 'info';
2067
+ return {
2068
+ enableColors: (uc) => (useColors = uc),
2069
+ getLevel: () => level,
2070
+ setLevel: (l) => (level = l),
2071
+ emoji: (e) => e,
2072
+ info: console.log.bind(console),
2073
+ warn: console.warn.bind(console),
2074
+ error: console.error.bind(console),
2075
+ debug: console.debug.bind(console),
2076
+ red: (msg) => msg,
2077
+ green: (msg) => msg,
2078
+ yellow: (msg) => msg,
2079
+ blue: (msg) => msg,
2080
+ magenta: (msg) => msg,
2081
+ cyan: (msg) => msg,
2082
+ gray: (msg) => msg,
2083
+ bold: (msg) => msg,
2084
+ dim: (msg) => msg,
2085
+ bgRed: (msg) => msg,
2086
+ createTimeSpan: (_startMsg, _debug = false) => ({
2087
+ duration: () => 0,
2088
+ finish: () => 0,
2089
+ }),
2090
+ printDiagnostics(diagnostics) {
2091
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
2092
+ },
2093
+ };
2094
+ };
2095
+ const logDiagnostic = (diagnostic, useColors) => {
2096
+ let color = BLUE;
2097
+ let prefix = 'Build';
2098
+ let msg = '';
2099
+ if (diagnostic.level === 'error') {
2100
+ color = RED;
2101
+ prefix = 'Error';
2102
+ }
2103
+ else if (diagnostic.level === 'warn') {
2104
+ color = YELLOW;
2105
+ prefix = 'Warning';
2106
+ }
2107
+ if (diagnostic.header) {
2108
+ prefix = diagnostic.header;
2109
+ }
2110
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
2111
+ if (filePath) {
2112
+ msg += filePath;
2113
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
2114
+ msg += ', line ' + diagnostic.lineNumber;
2115
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
2116
+ msg += ', column ' + diagnostic.columnNumber;
2117
+ }
2118
+ }
2119
+ msg += '\n';
2120
+ }
2121
+ msg += diagnostic.messageText;
2122
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
2123
+ diagnostic.lines.forEach((l) => {
2124
+ msg += '\n' + l.lineNumber + ': ' + l.text;
2125
+ });
2126
+ msg += '\n';
2127
+ }
2128
+ if (useColors) {
2129
+ const styledPrefix = [
2130
+ '%c' + prefix,
2131
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
2132
+ ];
2133
+ console.log(...styledPrefix, msg);
2134
+ }
2135
+ else if (diagnostic.level === 'error') {
2136
+ console.error(msg);
2137
+ }
2138
+ else if (diagnostic.level === 'warn') {
2139
+ console.warn(msg);
2140
+ }
2141
+ else {
2142
+ console.log(msg);
2143
+ }
2144
+ };
2145
+ const YELLOW = `#f39c12`;
2146
+ const RED = `#c0392b`;
2147
+ const BLUE = `#3498db`;
2148
+
1684
2149
  const run = async (init) => {
1685
2150
  const { args, logger, sys } = init;
1686
2151
  try {
@@ -1696,7 +2161,7 @@ const run = async (init) => {
1696
2161
  sys.applyGlobalPatch(sys.getCurrentDirectory());
1697
2162
  }
1698
2163
  if (task === 'help' || flags.help) {
1699
- await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys);
2164
+ await taskHelp({ task: 'help', args }, logger, sys);
1700
2165
  return;
1701
2166
  }
1702
2167
  startupLog(logger, task);
@@ -1722,7 +2187,7 @@ const run = async (init) => {
1722
2187
  startupLogVersion(logger, task, coreCompiler);
1723
2188
  loadedCompilerLog(sys, logger, flags, coreCompiler);
1724
2189
  if (task === 'info') {
1725
- await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => {
2190
+ await telemetryAction(sys, { flags: { task: 'info' }, logger }, coreCompiler, async () => {
1726
2191
  await taskInfo(coreCompiler, sys, logger);
1727
2192
  });
1728
2193
  return;
@@ -1745,7 +2210,7 @@ const run = async (init) => {
1745
2210
  sys.applyGlobalPatch(validated.config.rootDir);
1746
2211
  }
1747
2212
  await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
1748
- await telemetryAction(sys, validated.config, logger, coreCompiler, async () => {
2213
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
1749
2214
  await runTask(coreCompiler, validated.config, task, sys);
1750
2215
  });
1751
2216
  }
@@ -1758,40 +2223,42 @@ const run = async (init) => {
1758
2223
  }
1759
2224
  };
1760
2225
  const runTask = async (coreCompiler, config, task, sys) => {
1761
- config.flags = config.flags || { task };
1762
- config.outputTargets = config.outputTargets || [];
2226
+ var _a, _b;
2227
+ const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2228
+ const strictConfig = { ...config, flags: (_b = { ...config.flags }) !== null && _b !== void 0 ? _b : { task }, logger };
2229
+ strictConfig.outputTargets = strictConfig.outputTargets || [];
1763
2230
  switch (task) {
1764
2231
  case 'build':
1765
- await taskBuild(coreCompiler, config, sys);
2232
+ await taskBuild(coreCompiler, strictConfig, sys);
1766
2233
  break;
1767
2234
  case 'docs':
1768
- await taskDocs(coreCompiler, config);
2235
+ await taskDocs(coreCompiler, strictConfig);
1769
2236
  break;
1770
2237
  case 'generate':
1771
2238
  case 'g':
1772
- await taskGenerate(coreCompiler, config);
2239
+ await taskGenerate(coreCompiler, strictConfig);
1773
2240
  break;
1774
2241
  case 'help':
1775
- await taskHelp(config, config.logger, sys);
2242
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1776
2243
  break;
1777
2244
  case 'prerender':
1778
- await taskPrerender(coreCompiler, config);
2245
+ await taskPrerender(coreCompiler, strictConfig);
1779
2246
  break;
1780
2247
  case 'serve':
1781
- await taskServe(config);
2248
+ await taskServe(strictConfig);
1782
2249
  break;
1783
2250
  case 'telemetry':
1784
2251
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1785
2252
  if (sys) {
1786
- await taskTelemetry(config, sys, config.logger);
2253
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
1787
2254
  }
1788
2255
  break;
1789
2256
  case 'version':
1790
2257
  console.log(coreCompiler.version);
1791
2258
  break;
1792
2259
  default:
1793
- config.logger.error(`${config.logger.emoji('❌ ')}Invalid rindo command, please see the options below:`);
1794
- await taskHelp(config, config.logger, sys);
2260
+ strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid rindo command, please see the options below:`);
2261
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1795
2262
  return config.sys.exit(1);
1796
2263
  }
1797
2264
  };