@rindo/core 2.17.4 → 2.22.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 (108) hide show
  1. package/cli/config-flags.d.ts +33 -21
  2. package/cli/index.cjs +693 -401
  3. package/cli/index.d.ts +3 -0
  4. package/cli/index.js +693 -401
  5. package/cli/package.json +1 -1
  6. package/compiler/lib.dom.d.ts +898 -184
  7. package/compiler/lib.dom.iterable.d.ts +22 -4
  8. package/compiler/lib.es2015.collection.d.ts +62 -1
  9. package/compiler/lib.es2015.core.d.ts +3 -3
  10. package/compiler/lib.es2015.iterable.d.ts +2 -1
  11. package/compiler/lib.es2015.promise.d.ts +9 -4
  12. package/compiler/lib.es2015.proxy.d.ts +91 -2
  13. package/compiler/lib.es2015.reflect.d.ts +26 -3
  14. package/compiler/lib.es2015.symbol.wellknown.d.ts +3 -3
  15. package/compiler/lib.es2017.intl.d.ts +16 -1
  16. package/compiler/lib.es2019.d.ts +1 -0
  17. package/compiler/lib.es2019.intl.d.ts +25 -0
  18. package/compiler/lib.es2020.bigint.d.ts +7 -5
  19. package/compiler/lib.es2020.d.ts +2 -0
  20. package/compiler/lib.es2020.date.d.ts +44 -0
  21. package/compiler/lib.es2020.intl.d.ts +81 -16
  22. package/compiler/lib.es2020.number.d.ts +30 -0
  23. package/compiler/lib.es2021.intl.d.ts +116 -6
  24. package/compiler/lib.es2022.array.d.ts +123 -0
  25. package/compiler/lib.es2022.d.ts +27 -0
  26. package/compiler/lib.es2022.error.d.ts +75 -0
  27. package/compiler/lib.es2022.full.d.ts +25 -0
  28. package/compiler/lib.es2022.intl.d.ts +111 -0
  29. package/compiler/lib.es2022.object.d.ts +28 -0
  30. package/compiler/lib.es2022.sharedmemory.d.ts +27 -0
  31. package/compiler/lib.es2022.string.d.ts +27 -0
  32. package/compiler/lib.es5.d.ts +62 -31
  33. package/compiler/lib.esnext.d.ts +1 -1
  34. package/compiler/lib.esnext.intl.d.ts +8 -1
  35. package/compiler/lib.webworker.d.ts +540 -81
  36. package/compiler/lib.webworker.iterable.d.ts +19 -4
  37. package/compiler/package.json +1 -1
  38. package/compiler/rindo.d.ts +3 -25
  39. package/compiler/rindo.js +54678 -52205
  40. package/compiler/rindo.min.js +2 -2
  41. package/compiler/sys/in-memory-fs.d.ts +218 -0
  42. package/compiler/transpile.d.ts +32 -0
  43. package/dependencies.json +12 -1
  44. package/dev-server/client/app-error.d.ts +1 -1
  45. package/dev-server/client/index.d.ts +2 -2
  46. package/dev-server/client/index.js +241 -241
  47. package/dev-server/client/package.json +1 -1
  48. package/dev-server/connector.html +3 -3
  49. package/dev-server/index.d.ts +1 -1
  50. package/dev-server/index.js +2 -2
  51. package/dev-server/open-in-editor-api.js +1 -1
  52. package/dev-server/package.json +1 -1
  53. package/dev-server/server-process.js +1192 -1158
  54. package/dev-server/ws.js +1 -1
  55. package/internal/app-data/package.json +1 -1
  56. package/internal/client/css-shim.js +2 -2
  57. package/internal/client/dom.js +1 -1
  58. package/internal/client/index.js +1130 -823
  59. package/internal/client/package.json +1 -1
  60. package/internal/client/patch-browser.js +19 -1
  61. package/internal/client/patch-esm.js +1 -1
  62. package/internal/client/polyfills/css-shim.js +1 -1
  63. package/internal/client/shadow-css.js +1 -1
  64. package/internal/hydrate/index.js +154 -143
  65. package/internal/hydrate/package.json +1 -1
  66. package/internal/hydrate/runner.d.ts +1 -1
  67. package/internal/hydrate/runner.js +106 -106
  68. package/internal/package.json +1 -1
  69. package/internal/rindo-core/index.d.ts +9 -10
  70. package/internal/rindo-private.d.ts +149 -184
  71. package/internal/rindo-public-compiler.d.ts +83 -38
  72. package/internal/rindo-public-docs.d.ts +24 -0
  73. package/internal/rindo-public-runtime.d.ts +79 -7
  74. package/internal/testing/index.js +187 -175
  75. package/internal/testing/package.json +1 -1
  76. package/mock-doc/index.cjs +526 -501
  77. package/mock-doc/index.d.ts +15 -14
  78. package/mock-doc/index.js +526 -501
  79. package/mock-doc/package.json +1 -1
  80. package/package.json +48 -59
  81. package/readme.md +44 -31
  82. package/screenshot/compare/build/p-f4745c2f.entry.js +1 -1
  83. package/screenshot/index.d.ts +1 -1
  84. package/screenshot/index.js +13 -13
  85. package/screenshot/package.json +1 -1
  86. package/screenshot/pixel-match.js +983 -849
  87. package/sys/node/autoprefixer.js +2 -2
  88. package/sys/node/glob.js +1 -1
  89. package/sys/node/index.d.ts +4 -0
  90. package/sys/node/index.js +399 -413
  91. package/sys/node/package.json +1 -1
  92. package/sys/node/prompts.js +1 -1
  93. package/sys/node/worker.js +1 -1
  94. package/testing/index.d.ts +6 -6
  95. package/testing/index.js +684 -839
  96. package/testing/jest/jest-config.d.ts +1 -1
  97. package/testing/jest/jest-preprocessor.d.ts +3 -3
  98. package/testing/jest/jest-serializer.d.ts +1 -2
  99. package/testing/matchers/index.d.ts +3 -3
  100. package/testing/mock-fetch.d.ts +1 -1
  101. package/testing/mocks.d.ts +2 -2
  102. package/testing/package.json +1 -1
  103. package/testing/puppeteer/puppeteer-declarations.d.ts +5 -5
  104. package/testing/puppeteer/puppeteer-element.d.ts +2 -2
  105. package/testing/puppeteer/puppeteer-events.d.ts +1 -1
  106. package/testing/testing-logger.d.ts +1 -1
  107. package/testing/testing-utils.d.ts +6 -5
  108. package/testing/testing.d.ts +1 -1
package/cli/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Rindo CLI (CommonJS) v2.17.4 | MIT Licensed | https://rindojs.web.app
2
+ Rindo CLI (CommonJS) v2.22.2 | MIT Licensed | https://rindojs.web.app
3
3
  */
4
4
  'use strict';
5
5
 
@@ -25,41 +25,6 @@ function _interopNamespace(e) {
25
25
  return Object.freeze(n);
26
26
  }
27
27
 
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
28
  /**
64
29
  * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase,
65
30
  * or whatever you call it!)
@@ -72,6 +37,16 @@ const dashToPascalCase = (str) => str
72
37
  .split('-')
73
38
  .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
74
39
  .join('');
40
+ /**
41
+ * Convert a string to 'camelCase'
42
+ *
43
+ * @param str the string to convert
44
+ * @returns the converted string
45
+ */
46
+ const toCamelCase = (str) => {
47
+ const pascalCase = dashToPascalCase(str);
48
+ return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
49
+ };
75
50
  const isFunction = (v) => typeof v === 'function';
76
51
  const isString = (v) => typeof v === 'string';
77
52
 
@@ -223,18 +198,18 @@ const getEncodedRootLength = (path) => {
223
198
  return 0;
224
199
  const ch0 = path.charCodeAt(0);
225
200
  // POSIX or UNC
226
- if (ch0 === 47 /* slash */ || ch0 === 92 /* backslash */) {
201
+ if (ch0 === 47 /* CharacterCodes.slash */ || ch0 === 92 /* CharacterCodes.backslash */) {
227
202
  if (path.charCodeAt(1) !== ch0)
228
203
  return 1; // POSIX: "/" (or non-normalized "\")
229
- const p1 = path.indexOf(ch0 === 47 /* slash */ ? '/' : altDirectorySeparator, 2);
204
+ const p1 = path.indexOf(ch0 === 47 /* CharacterCodes.slash */ ? '/' : altDirectorySeparator, 2);
230
205
  if (p1 < 0)
231
206
  return path.length; // UNC: "//server" or "\\server"
232
207
  return p1 + 1; // UNC: "//server/" or "\\server\"
233
208
  }
234
209
  // DOS
235
- if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* colon */) {
210
+ if (isVolumeCharacter(ch0) && path.charCodeAt(1) === 58 /* CharacterCodes.colon */) {
236
211
  const ch2 = path.charCodeAt(2);
237
- if (ch2 === 47 /* slash */ || ch2 === 92 /* backslash */)
212
+ if (ch2 === 47 /* CharacterCodes.slash */ || ch2 === 92 /* CharacterCodes.backslash */)
238
213
  return 3; // DOS: "c:/" or "c:\"
239
214
  if (path.length === 2)
240
215
  return 2; // DOS: "c:" (but not "c:d")
@@ -256,7 +231,7 @@ const getEncodedRootLength = (path) => {
256
231
  isVolumeCharacter(path.charCodeAt(authorityEnd + 1))) {
257
232
  const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2);
258
233
  if (volumeSeparatorEnd !== -1) {
259
- if (path.charCodeAt(volumeSeparatorEnd) === 47 /* slash */) {
234
+ if (path.charCodeAt(volumeSeparatorEnd) === 47 /* CharacterCodes.slash */) {
260
235
  // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
261
236
  return ~(volumeSeparatorEnd + 1);
262
237
  }
@@ -274,15 +249,15 @@ const getEncodedRootLength = (path) => {
274
249
  // relative
275
250
  return 0;
276
251
  };
277
- const isVolumeCharacter = (charCode) => (charCode >= 97 /* a */ && charCode <= 122 /* z */) ||
278
- (charCode >= 65 /* A */ && charCode <= 90 /* Z */);
252
+ const isVolumeCharacter = (charCode) => (charCode >= 97 /* CharacterCodes.a */ && charCode <= 122 /* CharacterCodes.z */) ||
253
+ (charCode >= 65 /* CharacterCodes.A */ && charCode <= 90 /* CharacterCodes.Z */);
279
254
  const getFileUrlVolumeSeparatorEnd = (url, start) => {
280
255
  const ch0 = url.charCodeAt(start);
281
- if (ch0 === 58 /* colon */)
256
+ if (ch0 === 58 /* CharacterCodes.colon */)
282
257
  return start + 1;
283
- if (ch0 === 37 /* percent */ && url.charCodeAt(start + 1) === 51 /* _3 */) {
258
+ if (ch0 === 37 /* CharacterCodes.percent */ && url.charCodeAt(start + 1) === 51 /* CharacterCodes._3 */) {
284
259
  const ch2 = url.charCodeAt(start + 2);
285
- if (ch2 === 97 /* a */ || ch2 === 65 /* A */)
260
+ if (ch2 === 97 /* CharacterCodes.a */ || ch2 === 65 /* CharacterCodes.A */)
286
261
  return start + 3;
287
262
  }
288
263
  return -1;
@@ -297,6 +272,22 @@ const pathComponents = (path, rootLength) => {
297
272
  return [root, ...rest];
298
273
  };
299
274
 
275
+ /**
276
+ * Check whether a string is a member of a ReadonlyArray<string>
277
+ *
278
+ * We need a little helper for this because unfortunately `includes` is typed
279
+ * on `ReadonlyArray<T>` as `(el: T): boolean` so a `string` cannot be passed
280
+ * to `includes` on a `ReadonlyArray` 😢 thus we have a little helper function
281
+ * where we do the type coercion just once.
282
+ *
283
+ * see microsoft/TypeScript#31018 for some discussion of this
284
+ *
285
+ * @param readOnlyArray the array we're checking
286
+ * @param maybeMember a value which is possibly a member of the array
287
+ * @returns whether the array contains the member or not
288
+ */
289
+ const readOnlyArrayHasStringMember = (readOnlyArray, maybeMember) => readOnlyArray.includes(maybeMember);
290
+
300
291
  /**
301
292
  * Validates that a component tag meets required naming conventions to be used for a web component
302
293
  * @param tag the tag to validate
@@ -341,10 +332,33 @@ const validateComponentTag = (tag) => {
341
332
  return undefined;
342
333
  };
343
334
 
335
+ /**
336
+ * This sets the log level hierarchy for our terminal logger, ranging from
337
+ * most to least verbose.
338
+ *
339
+ * Ordering the levels like this lets us easily check whether we should log a
340
+ * message at a given time. For instance, if the log level is set to `'warn'`,
341
+ * then anything passed to the logger with level `'warn'` or `'error'` should
342
+ * be logged, but we should _not_ log anything with level `'info'` or `'debug'`.
343
+ *
344
+ * If we have a current log level `currentLevel` and a message with level
345
+ * `msgLevel` is passed to the logger, we can determine whether or not we should
346
+ * log it by checking if the log level on the message is further up or at the
347
+ * same level in the hierarchy than `currentLevel`, like so:
348
+ *
349
+ * ```ts
350
+ * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel)
351
+ * ```
352
+ *
353
+ * NOTE: for the reasons described above, do not change the order of the entries
354
+ * in this array without good reason!
355
+ */
356
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
357
+
344
358
  /**
345
359
  * All the Boolean options supported by the Rindo CLI
346
360
  */
347
- const BOOLEAN_CLI_ARGS = [
361
+ const BOOLEAN_CLI_FLAGS = [
348
362
  'build',
349
363
  'cache',
350
364
  'checkVersion',
@@ -427,7 +441,7 @@ const BOOLEAN_CLI_ARGS = [
427
441
  /**
428
442
  * All the Number options supported by the Rindo CLI
429
443
  */
430
- const NUMBER_CLI_ARGS = [
444
+ const NUMBER_CLI_FLAGS = [
431
445
  'port',
432
446
  // JEST CLI ARGS
433
447
  'maxConcurrency',
@@ -436,7 +450,7 @@ const NUMBER_CLI_ARGS = [
436
450
  /**
437
451
  * All the String options supported by the Rindo CLI
438
452
  */
439
- const STRING_CLI_ARGS = [
453
+ const STRING_CLI_FLAGS = [
440
454
  'address',
441
455
  'config',
442
456
  'docsApi',
@@ -475,7 +489,8 @@ const STRING_CLI_ARGS = [
475
489
  'testURL',
476
490
  'timers',
477
491
  'transform',
478
- // ARRAY ARGS
492
+ ];
493
+ const STRING_ARRAY_CLI_FLAGS = [
479
494
  'collectCoverageOnlyFrom',
480
495
  'coveragePathIgnorePatterns',
481
496
  'coverageReporters',
@@ -504,7 +519,7 @@ const STRING_CLI_ARGS = [
504
519
  * `maxWorkers` is an argument which is used both by Rindo _and_ by Jest,
505
520
  * which means that we need to support parsing both string and number values.
506
521
  */
507
- const STRING_NUMBER_CLI_ARGS = ['maxWorkers'];
522
+ const STRING_NUMBER_CLI_FLAGS = ['maxWorkers'];
508
523
  /**
509
524
  * All the LogLevel-type options supported by the Rindo CLI
510
525
  *
@@ -512,16 +527,21 @@ const STRING_NUMBER_CLI_ARGS = ['maxWorkers'];
512
527
  * but this approach lets us make sure that we're handling all
513
528
  * our arguments in a type-safe way.
514
529
  */
515
- const LOG_LEVEL_CLI_ARGS = ['logLevel'];
530
+ const LOG_LEVEL_CLI_FLAGS = ['logLevel'];
516
531
  /**
517
532
  * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'`
518
533
  */
519
- const CLI_ARG_ALIASES = {
520
- config: 'c',
521
- help: 'h',
522
- port: 'p',
523
- version: 'v',
534
+ const CLI_FLAG_ALIASES = {
535
+ c: 'config',
536
+ h: 'help',
537
+ p: 'port',
538
+ v: 'version',
524
539
  };
540
+ /**
541
+ * A regular expression which can be used to match a CLI flag for one of our
542
+ * short aliases.
543
+ */
544
+ const CLI_FLAG_REGEX = new RegExp(`^-[chpv]{1}$`);
525
545
  /**
526
546
  * Helper function for initializing a `ConfigFlags` object. Provide any overrides
527
547
  * for default values and off you go!
@@ -554,8 +574,14 @@ const parseFlags = (args, _sys) => {
554
574
  flags.args = Array.isArray(args) ? args.slice() : [];
555
575
  if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
556
576
  flags.task = flags.args[0];
577
+ // if the first argument was a "task" (like `build`, `test`, etc) then
578
+ // we go on to parse the _rest_ of the CLI args
579
+ parseArgs(flags, args.slice(1));
580
+ }
581
+ else {
582
+ // we didn't find a leading flag, so we should just parse them all
583
+ parseArgs(flags, flags.args);
557
584
  }
558
- parseArgs(flags, flags.args);
559
585
  if (flags.task != null) {
560
586
  const i = flags.args.indexOf(flags.task);
561
587
  if (i > -1) {
@@ -572,120 +598,259 @@ const parseFlags = (args, _sys) => {
572
598
  return flags;
573
599
  };
574
600
  /**
575
- * Parse command line arguments that are enumerated in the `config-flags`
576
- * module. Handles leading dashes on arguments, aliases that are defined for a
577
- * small number of arguments, and parsing values for non-boolean arguments
578
- * (e.g. port number for the dev server).
601
+ * Parse the supported command line flags which are enumerated in the
602
+ * `config-flags` module. Handles leading dashes on arguments, aliases that are
603
+ * defined for a small number of arguments, and parsing values for non-boolean
604
+ * arguments (e.g. port number for the dev server).
605
+ *
606
+ * This parses the following grammar:
607
+ *
608
+ * CLIArguments → ""
609
+ * | CLITerm ( " " CLITerm )* ;
610
+ * CLITerm → EqualsArg
611
+ * | AliasEqualsArg
612
+ * | AliasArg
613
+ * | NegativeDashArg
614
+ * | NegativeArg
615
+ * | SimpleArg ;
616
+ * EqualsArg → "--" ArgName "=" CLIValue ;
617
+ * AliasEqualsArg → "-" AliasName "=" CLIValue ;
618
+ * AliasArg → "-" AliasName ( " " CLIValue )? ;
619
+ * NegativeDashArg → "--no-" ArgName ;
620
+ * NegativeArg → "--no" ArgName ;
621
+ * SimpleArg → "--" ArgName ( " " CLIValue )? ;
622
+ * ArgName → /^[a-zA-Z-]+$/ ;
623
+ * AliasName → /^[a-z]{1}$/ ;
624
+ * CLIValue → '"' /^[a-zA-Z0-9]+$/ '"'
625
+ * | /^[a-zA-Z0-9]+$/ ;
626
+ *
627
+ * There are additional constraints (not shown in the grammar for brevity's sake)
628
+ * on the type of `CLIValue` which will be associated with a particular argument.
629
+ * We enforce this by declaring lists of boolean, string, etc arguments and
630
+ * checking the types of values before setting them.
631
+ *
632
+ * We don't need to turn the list of CLI arg tokens into any kind of
633
+ * intermediate representation since we aren't concerned with doing anything
634
+ * other than setting the correct values on our ConfigFlags object. So we just
635
+ * parse the array of string arguments using a recursive-descent approach
636
+ * (which is not very deep since our grammar is pretty simple) and make the
637
+ * modifications we need to make to the {@link ConfigFlags} object as we go.
579
638
  *
580
639
  * @param flags a ConfigFlags object to which parsed arguments will be added
581
640
  * @param args an array of command-line arguments to parse
582
641
  */
583
642
  const parseArgs = (flags, args) => {
584
- BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
585
- STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
586
- NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
587
- STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
588
- LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
643
+ const argsCopy = args.concat();
644
+ while (argsCopy.length > 0) {
645
+ // there are still unprocessed args to deal with
646
+ parseCLITerm(flags, argsCopy);
647
+ }
589
648
  };
590
649
  /**
591
- * Parse a boolean CLI argument. For these, we support the following formats:
592
- *
593
- * - `--booleanArg`
594
- * - `--boolean-arg`
595
- * - `--noBooleanArg`
596
- * - `--no-boolean-arg`
597
- *
598
- * The final two variants should be parsed to a value of `false` on the config
599
- * object.
650
+ * Given an array of CLI arguments, parse it and perform a series of side
651
+ * effects (setting values on the provided `ConfigFlags` object).
600
652
  *
601
- * @param flags the config flags object, while we'll modify
602
- * @param args our CLI arguments
603
- * @param configCaseName the argument we want to look at right now
604
- */
605
- const parseBooleanArg = (flags, args, configCaseName) => {
606
- // we support both dash-case and PascalCase versions of the parameter
607
- // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS
608
- const alias = CLI_ARG_ALIASES[configCaseName];
609
- const dashCaseName = toDashCase(configCaseName);
610
- if (typeof flags[configCaseName] !== 'boolean') {
611
- flags[configCaseName] = null;
612
- }
613
- args.forEach((cmdArg) => {
614
- let value;
615
- if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) {
616
- value = true;
617
- }
618
- else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) {
619
- value = false;
620
- }
621
- else if (alias && cmdArg === `-${alias}`) {
622
- value = true;
623
- }
624
- if (value !== undefined && cmdArg !== undefined) {
625
- flags[configCaseName] = value;
626
- flags.knownArgs.push(cmdArg);
627
- }
628
- });
653
+ * @param flags a {@link ConfigFlags} object which is updated as we parse the CLI
654
+ * arguments
655
+ * @param args a list of args to work through. This function (and some functions
656
+ * it calls) calls `Array.prototype.shift` to get the next argument to look at,
657
+ * so this parameter will be modified.
658
+ */
659
+ const parseCLITerm = (flags, args) => {
660
+ // pull off the first arg from the argument array
661
+ const arg = args.shift();
662
+ // array is empty, we're done!
663
+ if (arg === undefined)
664
+ return;
665
+ // EqualsArg → "--" ArgName "=" CLIValue ;
666
+ if (arg.startsWith('--') && arg.includes('=')) {
667
+ // we're dealing with an EqualsArg, we have a special helper for that
668
+ const [originalArg, value] = parseEqualsArg(arg);
669
+ setCLIArg(flags, arg.split('=')[0], normalizeFlagName(originalArg), value);
670
+ }
671
+ // AliasEqualsArg → "-" AliasName "=" CLIValue ;
672
+ else if (arg.startsWith('-') && arg.includes('=')) {
673
+ // we're dealing with an AliasEqualsArg, we have a special helper for that
674
+ const [originalArg, value] = parseEqualsArg(arg);
675
+ setCLIArg(flags, arg.split('=')[0], normalizeFlagName(originalArg), value);
676
+ }
677
+ // AliasArg → "-" AliasName ( " " CLIValue )? ;
678
+ else if (CLI_FLAG_REGEX.test(arg)) {
679
+ // this is a short alias, like `-c` for Config
680
+ setCLIArg(flags, arg, normalizeFlagName(arg), parseCLIValue(args));
681
+ }
682
+ // NegativeDashArg → "--no-" ArgName ;
683
+ else if (arg.startsWith('--no-') && arg.length > '--no-'.length) {
684
+ // this is a `NegativeDashArg` term, so we need to normalize the negative
685
+ // flag name and then set an appropriate value
686
+ const normalized = normalizeNegativeFlagName(arg);
687
+ setCLIArg(flags, arg, normalized, '');
688
+ }
689
+ // NegativeArg → "--no" ArgName ;
690
+ else if (arg.startsWith('--no') &&
691
+ !readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizeFlagName(arg)) &&
692
+ readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizeNegativeFlagName(arg))) {
693
+ // possibly dealing with a `NegativeArg` here. There is a little ambiguity
694
+ // here because we have arguments that already begin with `no` like
695
+ // `notify`, so we need to test if a normalized form of the raw argument is
696
+ // a valid and supported boolean flag.
697
+ setCLIArg(flags, arg, normalizeNegativeFlagName(arg), '');
698
+ }
699
+ // SimpleArg → "--" ArgName ( " " CLIValue )? ;
700
+ else if (arg.startsWith('--') && arg.length > '--'.length) {
701
+ setCLIArg(flags, arg, normalizeFlagName(arg), parseCLIValue(args));
702
+ }
703
+ // if we get here it is not an argument in our list of supported arguments.
704
+ // This doesn't necessarily mean we want to report an error or anything
705
+ // though! Instead, with unknown / unrecognized arguments we stick them into
706
+ // the `unknownArgs` array, which is used when we pass CLI args to Jest, for
707
+ // instance. So we just return void here.
629
708
  };
630
709
  /**
631
- * Parse a string CLI argument
710
+ * Normalize a 'negative' flag name, just to do a little pre-processing before
711
+ * we pass it to `setCLIArg`.
632
712
  *
633
- * @param flags the config flags object, while we'll modify
634
- * @param args our CLI arguments
635
- * @param configCaseName the argument we want to look at right now
713
+ * @param flagName the flag name to normalize
714
+ * @returns a normalized flag name
636
715
  */
637
- const parseStringArg = (flags, args, configCaseName) => {
638
- if (typeof flags[configCaseName] !== 'string') {
639
- flags[configCaseName] = null;
640
- }
641
- const { value, matchingArg } = getValue(args, configCaseName);
642
- if (value !== undefined && matchingArg !== undefined) {
643
- flags[configCaseName] = value;
644
- flags.knownArgs.push(matchingArg);
645
- flags.knownArgs.push(value);
646
- }
716
+ const normalizeNegativeFlagName = (flagName) => {
717
+ const trimmed = flagName.replace(/^--no[-]?/, '');
718
+ return normalizeFlagName(trimmed.charAt(0).toLowerCase() + trimmed.slice(1));
647
719
  };
648
720
  /**
649
- * Parse a number CLI argument
721
+ * Normalize a flag name by:
722
+ *
723
+ * - replacing any leading dashes (`--foo` -> `foo`)
724
+ * - converting `dash-case` to camelCase (if necessary)
725
+ *
726
+ * Normalizing in this context basically means converting the various
727
+ * supported flag spelling variants to the variant defined in our lists of
728
+ * supported arguments (e.g. BOOLEAN_CLI_FLAGS, etc). So, for instance,
729
+ * `--log-level` should be converted to `logLevel`.
730
+ *
731
+ * @param flagName the flag name to normalize
732
+ * @returns a normalized flag name
650
733
  *
651
- * @param flags the config flags object, while we'll modify
652
- * @param args our CLI arguments
653
- * @param configCaseName the argument we want to look at right now
654
734
  */
655
- const parseNumberArg = (flags, args, configCaseName) => {
656
- if (typeof flags[configCaseName] !== 'number') {
657
- flags[configCaseName] = null;
658
- }
659
- const { value, matchingArg } = getValue(args, configCaseName);
660
- if (value !== undefined && matchingArg !== undefined) {
661
- flags[configCaseName] = parseInt(value, 10);
662
- flags.knownArgs.push(matchingArg);
663
- flags.knownArgs.push(value);
664
- }
735
+ const normalizeFlagName = (flagName) => {
736
+ const trimmed = flagName.replace(/^-+/, '');
737
+ return trimmed.includes('-') ? toCamelCase(trimmed) : trimmed;
665
738
  };
666
739
  /**
667
- * Parse a CLI argument which may be either a string or a number
740
+ * Set a value on a provided {@link ConfigFlags} object, given an argument
741
+ * name and a raw string value. This function dispatches to other functions
742
+ * which make sure that the string value can be properly parsed into a JS
743
+ * runtime value of the right type (e.g. number, string, etc).
668
744
  *
669
- * @param flags the config flags object, while we'll modify
670
- * @param args our CLI arguments
671
- * @param configCaseName the argument we want to look at right now
672
- */
673
- const parseStringNumberArg = (flags, args, configCaseName) => {
674
- if (!['number', 'string'].includes(typeof flags[configCaseName])) {
675
- flags[configCaseName] = null;
745
+ * @throws if a value cannot be parsed to the right type for a given flag
746
+ * @param flags a {@link ConfigFlags} object
747
+ * @param rawArg the raw argument name matched by the parser
748
+ * @param normalizedArg an argument with leading control characters (`--`,
749
+ * `--no-`, etc) removed
750
+ * @param value the raw value to be set onto the config flags object
751
+ */
752
+ const setCLIArg = (flags, rawArg, normalizedArg, value) => {
753
+ normalizedArg = dereferenceAlias(normalizedArg);
754
+ // We're setting a boolean!
755
+ if (readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizedArg)) {
756
+ flags[normalizedArg] =
757
+ typeof value === 'string'
758
+ ? Boolean(value)
759
+ : // no value was supplied, default to true
760
+ true;
761
+ flags.knownArgs.push(rawArg);
762
+ }
763
+ // We're setting a string!
764
+ else if (readOnlyArrayHasStringMember(STRING_CLI_FLAGS, normalizedArg)) {
765
+ if (typeof value === 'string') {
766
+ flags[normalizedArg] = value;
767
+ flags.knownArgs.push(rawArg);
768
+ flags.knownArgs.push(value);
769
+ }
770
+ else {
771
+ throwCLIParsingError(rawArg, 'expected a string argument but received nothing');
772
+ }
676
773
  }
677
- const { value, matchingArg } = getValue(args, configCaseName);
678
- if (value !== undefined && matchingArg !== undefined) {
679
- if (CLI_ARG_STRING_REGEX.test(value)) {
680
- // if it matches the regex we treat it like a string
681
- flags[configCaseName] = value;
774
+ // We're setting a string, but it's one where the user can pass multiple values,
775
+ // like `--reporters="default" --reporters="jest-junit"`
776
+ else if (readOnlyArrayHasStringMember(STRING_ARRAY_CLI_FLAGS, normalizedArg)) {
777
+ if (typeof value === 'string') {
778
+ if (!Array.isArray(flags[normalizedArg])) {
779
+ flags[normalizedArg] = [];
780
+ }
781
+ const targetArray = flags[normalizedArg];
782
+ // this is irritating, but TS doesn't know that the `!Array.isArray`
783
+ // check above guarantees we have an array to work with here, and it
784
+ // doesn't want to narrow the type of `flags[normalizedArg]`, so we need
785
+ // to grab a reference to that array and then `Array.isArray` that. Bah!
786
+ if (Array.isArray(targetArray)) {
787
+ targetArray.push(value);
788
+ flags.knownArgs.push(rawArg);
789
+ flags.knownArgs.push(value);
790
+ }
682
791
  }
683
792
  else {
684
- // it was a number, great!
685
- flags[configCaseName] = Number(value);
793
+ throwCLIParsingError(rawArg, 'expected a string argument but received nothing');
794
+ }
795
+ }
796
+ // We're setting a number!
797
+ else if (readOnlyArrayHasStringMember(NUMBER_CLI_FLAGS, normalizedArg)) {
798
+ if (typeof value === 'string') {
799
+ const parsed = parseInt(value, 10);
800
+ if (isNaN(parsed)) {
801
+ throwNumberParsingError(rawArg, value);
802
+ }
803
+ else {
804
+ flags[normalizedArg] = parsed;
805
+ flags.knownArgs.push(rawArg);
806
+ flags.knownArgs.push(value);
807
+ }
808
+ }
809
+ else {
810
+ throwCLIParsingError(rawArg, 'expected a number argument but received nothing');
811
+ }
812
+ }
813
+ // We're setting a value which could be either a string _or_ a number
814
+ else if (readOnlyArrayHasStringMember(STRING_NUMBER_CLI_FLAGS, normalizedArg)) {
815
+ if (typeof value === 'string') {
816
+ if (CLI_ARG_STRING_REGEX.test(value)) {
817
+ // if it matches the regex we treat it like a string
818
+ flags[normalizedArg] = value;
819
+ }
820
+ else {
821
+ const parsed = Number(value);
822
+ if (isNaN(parsed)) {
823
+ // parsing didn't go so well, we gotta get out of here
824
+ // this is unlikely given our regex guard above
825
+ // but hey, this is ultimately JS so let's be safe
826
+ throwNumberParsingError(rawArg, value);
827
+ }
828
+ else {
829
+ flags[normalizedArg] = parsed;
830
+ }
831
+ }
832
+ flags.knownArgs.push(rawArg);
833
+ flags.knownArgs.push(value);
834
+ }
835
+ else {
836
+ throwCLIParsingError(rawArg, 'expected a string or a number but received nothing');
837
+ }
838
+ }
839
+ // We're setting the log level, which can only be a set of specific string values
840
+ else if (readOnlyArrayHasStringMember(LOG_LEVEL_CLI_FLAGS, normalizedArg)) {
841
+ if (typeof value === 'string') {
842
+ if (isLogLevel(value)) {
843
+ flags[normalizedArg] = value;
844
+ flags.knownArgs.push(rawArg);
845
+ flags.knownArgs.push(value);
846
+ }
847
+ else {
848
+ throwCLIParsingError(rawArg, `expected to receive a valid log level but received "${String(value)}"`);
849
+ }
850
+ }
851
+ else {
852
+ throwCLIParsingError(rawArg, 'expected to receive a valid log level but received nothing');
686
853
  }
687
- flags.knownArgs.push(matchingArg);
688
- flags.knownArgs.push(value);
689
854
  }
690
855
  };
691
856
  /**
@@ -706,73 +871,34 @@ const parseStringNumberArg = (flags, args, configCaseName) => {
706
871
  * to a number.
707
872
  */
708
873
  const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
874
+ const Empty = Symbol('Empty');
709
875
  /**
710
- * Parse a LogLevel CLI argument. These can be only a specific
711
- * set of strings, so this function takes care of validating that
712
- * the value is correct.
876
+ * A little helper which tries to parse a CLI value (as opposed to a flag) off
877
+ * of the argument array.
713
878
  *
714
- * @param flags the config flags object, while we'll modify
715
- * @param args our CLI arguments
716
- * @param configCaseName the argument we want to look at right now
717
- */
718
- const parseLogLevelArg = (flags, args, configCaseName) => {
719
- if (typeof flags[configCaseName] !== 'string') {
720
- flags[configCaseName] = null;
721
- }
722
- const { value, matchingArg } = getValue(args, configCaseName);
723
- if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) {
724
- flags[configCaseName] = value;
725
- flags.knownArgs.push(matchingArg);
726
- flags.knownArgs.push(value);
727
- }
728
- };
729
- /**
730
- * Helper for pulling values out from the raw array of CLI arguments. This logic
731
- * is shared between a few different types of CLI args.
732
- *
733
- * We look for arguments in the following formats:
734
- *
735
- * - `--my-cli-argument value`
736
- * - `--my-cli-argument=value`
737
- * - `--myCliArgument value`
738
- * - `--myCliArgument=value`
739
- *
740
- * We also check for shortened aliases, which we define for a few arguments.
741
- *
742
- * @param args the CLI args we're dealing with
743
- * @param configCaseName the ConfigFlag key which we're looking to pull out a value for
744
- * @returns the value for the flag as well as the exact string which it matched from
745
- * the user input.
746
- */
747
- const getValue = (args, configCaseName) => {
748
- // for some CLI args we have a short alias, like 'c' for 'config'
749
- const alias = CLI_ARG_ALIASES[configCaseName];
750
- // we support supplying arguments in both dash-case and configCase
751
- // for ease of use
752
- const dashCaseName = toDashCase(configCaseName);
753
- let value;
754
- let matchingArg;
755
- args.forEach((arg, i) => {
756
- if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) {
757
- // our argument was passed at the command-line in the format --argName=arg-value
758
- [matchingArg, value] = parseEqualsArg(arg);
759
- }
760
- else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) {
761
- // the next value in the array is assumed to be a value for this argument
762
- value = args[i + 1];
763
- matchingArg = arg;
764
- }
765
- else if (alias) {
766
- if (arg.startsWith(`-${alias}=`)) {
767
- [matchingArg, value] = parseEqualsArg(arg);
768
- }
769
- else if (arg === `-${alias}`) {
770
- value = args[i + 1];
771
- matchingArg = arg;
772
- }
879
+ * We support a variety of different argument formats, but all of them start
880
+ * with `-`, so we can check the first character to test whether the next token
881
+ * in our array of CLI arguments is a flag name or a value.
882
+ *
883
+ * @param args an array of CLI args
884
+ * @returns either a string result or an Empty sentinel
885
+ */
886
+ const parseCLIValue = (args) => {
887
+ // it's possible the arguments array is empty, if so, return empty
888
+ if (args[0] === undefined) {
889
+ return Empty;
890
+ }
891
+ // all we're concerned with here is that it does not start with `"-"`,
892
+ // which would indicate it should be parsed as a CLI flag and not a value.
893
+ if (!args[0].startsWith('-')) {
894
+ // It's not a flag, so we return the value and defer any specific parsing
895
+ // until later on.
896
+ const value = args.shift();
897
+ if (typeof value === 'string') {
898
+ return value;
773
899
  }
774
- });
775
- return { value, matchingArg };
900
+ }
901
+ return Empty;
776
902
  };
777
903
  /**
778
904
  * Parse an 'equals' argument, which is a CLI argument-value pair in the
@@ -808,8 +934,9 @@ const getValue = (args, configCaseName) => {
808
934
  * @returns a tuple containing the arg name and the value (if present)
809
935
  */
810
936
  const parseEqualsArg = (arg) => {
811
- const [originalArg, ...value] = arg.split('=');
812
- return [originalArg, value.join('=')];
937
+ const [originalArg, ...splitSections] = arg.split('=');
938
+ const value = splitSections.join('=');
939
+ return [originalArg, value === '' ? Empty : value];
813
940
  };
814
941
  /**
815
942
  * Small helper for getting type-system-level assurance that a `string` can be
@@ -818,18 +945,51 @@ const parseEqualsArg = (arg) => {
818
945
  * @param maybeLogLevel the string to check
819
946
  * @returns whether this is a `LogLevel`
820
947
  */
821
- const isLogLevel = (maybeLogLevel) =>
822
- // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
823
- // boolean` so a `string` cannot be passed to `includes` on a
824
- // `ReadonlyArray` 😢 thus we `as any`
825
- //
826
- // see microsoft/TypeScript#31018 for some discussion of this
827
- LOG_LEVELS.includes(maybeLogLevel);
948
+ const isLogLevel = (maybeLogLevel) => readOnlyArrayHasStringMember(LOG_LEVELS, maybeLogLevel);
949
+ /**
950
+ * A little helper for constructing and throwing an error message with info
951
+ * about what went wrong
952
+ *
953
+ * @param flag the flag which encountered the error
954
+ * @param message a message specific to the error which was encountered
955
+ */
956
+ const throwCLIParsingError = (flag, message) => {
957
+ throw new Error(`when parsing CLI flag "${flag}": ${message}`);
958
+ };
959
+ /**
960
+ * Throw a specific error for the situation where we ran into an issue parsing
961
+ * a number.
962
+ *
963
+ * @param flag the flag for which we encountered the issue
964
+ * @param value what we were trying to parse
965
+ */
966
+ const throwNumberParsingError = (flag, value) => {
967
+ throwCLIParsingError(flag, `expected a number but received "${value}"`);
968
+ };
969
+ /**
970
+ * A little helper to 'dereference' a flag alias, which if you squint a little
971
+ * you can think of like a pointer to a full flag name. Thus 'c' is like a
972
+ * pointer to 'config', so here we're doing something like `*c`. Of course, this
973
+ * being JS, this is just a metaphor!
974
+ *
975
+ * If no 'dereference' is found for the possible alias we just return the
976
+ * passed string unmodified.
977
+ *
978
+ * @param maybeAlias a string which _could_ be an alias to a full flag name
979
+ * @returns the full aliased flag name, if found, or the passed string if not
980
+ */
981
+ const dereferenceAlias = (maybeAlias) => {
982
+ const possibleDereference = CLI_FLAG_ALIASES[maybeAlias];
983
+ if (typeof possibleDereference === 'string') {
984
+ return possibleDereference;
985
+ }
986
+ return maybeAlias;
987
+ };
828
988
 
829
989
  const dependencies = [
830
990
  {
831
991
  name: "@rindo/core",
832
- version: "2.17.4",
992
+ version: "2.22.2",
833
993
  main: "compiler/rindo.js",
834
994
  resources: [
835
995
  "package.json",
@@ -866,13 +1026,16 @@ const dependencies = [
866
1026
  "compiler/lib.es2019.array.d.ts",
867
1027
  "compiler/lib.es2019.d.ts",
868
1028
  "compiler/lib.es2019.full.d.ts",
1029
+ "compiler/lib.es2019.intl.d.ts",
869
1030
  "compiler/lib.es2019.object.d.ts",
870
1031
  "compiler/lib.es2019.string.d.ts",
871
1032
  "compiler/lib.es2019.symbol.d.ts",
872
1033
  "compiler/lib.es2020.bigint.d.ts",
873
1034
  "compiler/lib.es2020.d.ts",
1035
+ "compiler/lib.es2020.date.d.ts",
874
1036
  "compiler/lib.es2020.full.d.ts",
875
1037
  "compiler/lib.es2020.intl.d.ts",
1038
+ "compiler/lib.es2020.number.d.ts",
876
1039
  "compiler/lib.es2020.promise.d.ts",
877
1040
  "compiler/lib.es2020.sharedmemory.d.ts",
878
1041
  "compiler/lib.es2020.string.d.ts",
@@ -883,6 +1046,14 @@ const dependencies = [
883
1046
  "compiler/lib.es2021.promise.d.ts",
884
1047
  "compiler/lib.es2021.string.d.ts",
885
1048
  "compiler/lib.es2021.weakref.d.ts",
1049
+ "compiler/lib.es2022.array.d.ts",
1050
+ "compiler/lib.es2022.d.ts",
1051
+ "compiler/lib.es2022.error.d.ts",
1052
+ "compiler/lib.es2022.full.d.ts",
1053
+ "compiler/lib.es2022.intl.d.ts",
1054
+ "compiler/lib.es2022.object.d.ts",
1055
+ "compiler/lib.es2022.sharedmemory.d.ts",
1056
+ "compiler/lib.es2022.string.d.ts",
886
1057
  "compiler/lib.es5.d.ts",
887
1058
  "compiler/lib.es6.d.ts",
888
1059
  "compiler/lib.esnext.d.ts",
@@ -927,16 +1098,112 @@ const dependencies = [
927
1098
  },
928
1099
  {
929
1100
  name: "terser",
930
- version: "5.6.1",
1101
+ version: "5.16.1",
931
1102
  main: "dist/bundle.min.js"
932
1103
  },
933
1104
  {
934
1105
  name: "typescript",
935
- version: "4.5.4",
1106
+ version: "4.9.4",
936
1107
  main: "lib/typescript.js"
937
1108
  }
938
1109
  ];
939
1110
 
1111
+ const IS_NODE_ENV = typeof global !== 'undefined' &&
1112
+ typeof require === 'function' &&
1113
+ !!global.process &&
1114
+ typeof __filename === 'string' &&
1115
+ (!global.origin || typeof global.origin !== 'string');
1116
+ const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1117
+
1118
+ /**
1119
+ * Creates an instance of a logger
1120
+ * @returns the new logger instance
1121
+ */
1122
+ const createLogger = () => {
1123
+ let useColors = IS_BROWSER_ENV;
1124
+ let level = 'info';
1125
+ return {
1126
+ enableColors: (uc) => (useColors = uc),
1127
+ getLevel: () => level,
1128
+ setLevel: (l) => (level = l),
1129
+ emoji: (e) => e,
1130
+ info: console.log.bind(console),
1131
+ warn: console.warn.bind(console),
1132
+ error: console.error.bind(console),
1133
+ debug: console.debug.bind(console),
1134
+ red: (msg) => msg,
1135
+ green: (msg) => msg,
1136
+ yellow: (msg) => msg,
1137
+ blue: (msg) => msg,
1138
+ magenta: (msg) => msg,
1139
+ cyan: (msg) => msg,
1140
+ gray: (msg) => msg,
1141
+ bold: (msg) => msg,
1142
+ dim: (msg) => msg,
1143
+ bgRed: (msg) => msg,
1144
+ createTimeSpan: (_startMsg, _debug = false) => ({
1145
+ duration: () => 0,
1146
+ finish: () => 0,
1147
+ }),
1148
+ printDiagnostics(diagnostics) {
1149
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
1150
+ },
1151
+ };
1152
+ };
1153
+ const logDiagnostic = (diagnostic, useColors) => {
1154
+ let color = BLUE;
1155
+ let prefix = 'Build';
1156
+ let msg = '';
1157
+ if (diagnostic.level === 'error') {
1158
+ color = RED;
1159
+ prefix = 'Error';
1160
+ }
1161
+ else if (diagnostic.level === 'warn') {
1162
+ color = YELLOW;
1163
+ prefix = 'Warning';
1164
+ }
1165
+ if (diagnostic.header) {
1166
+ prefix = diagnostic.header;
1167
+ }
1168
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
1169
+ if (filePath) {
1170
+ msg += filePath;
1171
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
1172
+ msg += ', line ' + diagnostic.lineNumber;
1173
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
1174
+ msg += ', column ' + diagnostic.columnNumber;
1175
+ }
1176
+ }
1177
+ msg += '\n';
1178
+ }
1179
+ msg += diagnostic.messageText;
1180
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
1181
+ diagnostic.lines.forEach((l) => {
1182
+ msg += '\n' + l.lineNumber + ': ' + l.text;
1183
+ });
1184
+ msg += '\n';
1185
+ }
1186
+ if (useColors) {
1187
+ const styledPrefix = [
1188
+ '%c' + prefix,
1189
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
1190
+ ];
1191
+ console.log(...styledPrefix, msg);
1192
+ }
1193
+ else if (diagnostic.level === 'error') {
1194
+ console.error(msg);
1195
+ }
1196
+ else if (diagnostic.level === 'warn') {
1197
+ console.warn(msg);
1198
+ }
1199
+ else {
1200
+ console.log(msg);
1201
+ }
1202
+ };
1203
+ const YELLOW = `#f39c12`;
1204
+ const RED = `#c0392b`;
1205
+ const BLUE = `#3498db`;
1206
+
940
1207
  /**
941
1208
  * Attempt to find a Rindo configuration file on the file system
942
1209
  * @param opts the options needed to find the configuration file
@@ -945,11 +1212,7 @@ const dependencies = [
945
1212
  const findConfig = async (opts) => {
946
1213
  const sys = opts.sys;
947
1214
  const cwd = sys.getCurrentDirectory();
948
- const results = {
949
- configPath: null,
950
- rootDir: normalizePath(cwd),
951
- diagnostics: [],
952
- };
1215
+ const rootDir = normalizePath(cwd);
953
1216
  let configPath = opts.configPath;
954
1217
  if (isString(configPath)) {
955
1218
  if (!sys.platformPath.isAbsolute(configPath)) {
@@ -964,8 +1227,13 @@ const findConfig = async (opts) => {
964
1227
  }
965
1228
  else {
966
1229
  // nothing was passed in, use the current working directory
967
- configPath = results.rootDir;
1230
+ configPath = rootDir;
968
1231
  }
1232
+ const results = {
1233
+ configPath,
1234
+ rootDir: normalizePath(cwd),
1235
+ diagnostics: [],
1236
+ };
969
1237
  const stat = await sys.stat(configPath);
970
1238
  if (stat.error) {
971
1239
  const diagnostic = buildError(results.diagnostics);
@@ -998,12 +1266,33 @@ const loadCoreCompiler = async (sys) => {
998
1266
  return globalThis.rindo;
999
1267
  };
1000
1268
 
1269
+ /**
1270
+ * Log the name of this package (`@rindo/core`) to an output stream
1271
+ *
1272
+ * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1273
+ *
1274
+ * The name of the package may not be logged, by design, for certain `task` types and logging levels
1275
+ *
1276
+ * @param logger the logging entity to use to output the name of the package
1277
+ * @param task the current task
1278
+ */
1001
1279
  const startupLog = (logger, task) => {
1002
1280
  if (task === 'info' || task === 'serve' || task === 'version') {
1003
1281
  return;
1004
1282
  }
1005
1283
  logger.info(logger.cyan(`@rindo/core`));
1006
1284
  };
1285
+ /**
1286
+ * Log this package's version to an output stream
1287
+ *
1288
+ * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1289
+ *
1290
+ * The package version may not be logged, by design, for certain `task` types and logging levels
1291
+ *
1292
+ * @param logger the logging entity to use for output
1293
+ * @param task the current task
1294
+ * @param coreCompiler the compiler instance to derive version information from
1295
+ */
1007
1296
  const startupLogVersion = (logger, task, coreCompiler) => {
1008
1297
  if (task === 'info' || task === 'serve' || task === 'version') {
1009
1298
  return;
@@ -1019,11 +1308,25 @@ const startupLogVersion = (logger, task, coreCompiler) => {
1019
1308
  startupMsg += logger.emoji(' ' + coreCompiler.vermoji);
1020
1309
  logger.info(startupMsg);
1021
1310
  };
1311
+ /**
1312
+ * Log details from a {@link CompilerSystem} used by Rindo to an output stream
1313
+ *
1314
+ * The output stream is determined by the {@link Logger} instance that is provided as an argument to this function
1315
+ *
1316
+ * @param sys the `CompilerSystem` to report details on
1317
+ * @param logger the logging entity to use for output
1318
+ * @param flags user set flags for the current invocation of Rindo
1319
+ * @param coreCompiler the compiler instance being used for this invocation of Rindo
1320
+ */
1022
1321
  const loadedCompilerLog = (sys, logger, flags, coreCompiler) => {
1023
1322
  const sysDetails = sys.details;
1024
1323
  const runtimeInfo = `${sys.name} ${sys.version}`;
1025
- const platformInfo = `${sysDetails.platform}, ${sysDetails.cpuModel}`;
1026
- const statsInfo = `cpus: ${sys.hardwareConcurrency}, freemem: ${Math.round(sysDetails.freemem() / 1000000)}MB, totalmem: ${Math.round(sysDetails.totalmem / 1000000)}MB`;
1324
+ const platformInfo = sysDetails
1325
+ ? `${sysDetails.platform}, ${sysDetails.cpuModel}`
1326
+ : `Unknown Platform, Unknown CPU Model`;
1327
+ const statsInfo = sysDetails
1328
+ ? `cpus: ${sys.hardwareConcurrency}, freemem: ${Math.round(sysDetails.freemem() / 1000000)}MB, totalmem: ${Math.round(sysDetails.totalmem / 1000000)}MB`
1329
+ : 'Unknown CPU Core Count, Unknown Memory';
1027
1330
  if (logger.getLevel() === 'debug') {
1028
1331
  logger.debug(runtimeInfo);
1029
1332
  logger.debug(platformInfo);
@@ -1037,6 +1340,14 @@ const loadedCompilerLog = (sys, logger, flags, coreCompiler) => {
1037
1340
  logger.info(statsInfo);
1038
1341
  }
1039
1342
  };
1343
+ /**
1344
+ * Log various warnings to an output stream
1345
+ *
1346
+ * The output stream is determined by the {@link Logger} instance attached to the `config` argument to this function
1347
+ *
1348
+ * @param coreCompiler the compiler instance being used for this invocation of Rindo
1349
+ * @param config a validated configuration object to be used for this run of Rindo
1350
+ */
1040
1351
  const startupCompilerLog = (coreCompiler, config) => {
1041
1352
  if (config.suppressLogs === true) {
1042
1353
  return;
@@ -1058,6 +1369,21 @@ const startupCompilerLog = (coreCompiler, config) => {
1058
1369
  }
1059
1370
  };
1060
1371
 
1372
+ const startCheckVersion = async (config, currentVersion) => {
1373
+ if (config.devMode && !config.flags.ci && !currentVersion.includes('-dev.') && isFunction(config.sys.checkVersion)) {
1374
+ return config.sys.checkVersion(config.logger, currentVersion);
1375
+ }
1376
+ return null;
1377
+ };
1378
+ const printCheckVersionResults = async (versionChecker) => {
1379
+ if (versionChecker) {
1380
+ const checkVersionResults = await versionChecker;
1381
+ if (isFunction(checkVersionResults)) {
1382
+ checkVersionResults();
1383
+ }
1384
+ }
1385
+ };
1386
+
1061
1387
  const taskPrerender = async (coreCompiler, config) => {
1062
1388
  startupCompilerLog(coreCompiler, config);
1063
1389
  const hydrateAppFilePath = config.flags.unknownArgs[0];
@@ -1089,21 +1415,6 @@ const runPrerenderTask = async (coreCompiler, config, hydrateAppFilePath, compon
1089
1415
  return diagnostics;
1090
1416
  };
1091
1417
 
1092
- const startCheckVersion = async (config, currentVersion) => {
1093
- if (config.devMode && !config.flags.ci && !currentVersion.includes('-dev.') && isFunction(config.sys.checkVersion)) {
1094
- return config.sys.checkVersion(config.logger, currentVersion);
1095
- }
1096
- return null;
1097
- };
1098
- const printCheckVersionResults = async (versionChecker) => {
1099
- if (versionChecker) {
1100
- const checkVersionResults = await versionChecker;
1101
- if (isFunction(checkVersionResults)) {
1102
- checkVersionResults();
1103
- }
1104
- }
1105
- };
1106
-
1107
1418
  const taskWatch = async (coreCompiler, config) => {
1108
1419
  let devServer = null;
1109
1420
  let exitCode = 0;
@@ -1150,6 +1461,15 @@ const taskWatch = async (coreCompiler, config) => {
1150
1461
  }
1151
1462
  };
1152
1463
 
1464
+ const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
1465
+ const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1466
+ const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
1467
+ const DOCS_CUSTOM = 'docs-custom';
1468
+ const DOCS_JSON = 'docs-json';
1469
+ const DOCS_README = 'docs-readme';
1470
+ const DOCS_VSCODE = 'docs-vscode';
1471
+ const WWW = 'www';
1472
+
1153
1473
  const tryFn = async (fn, ...args) => {
1154
1474
  try {
1155
1475
  return await fn(...args);
@@ -1163,12 +1483,12 @@ const isInteractive = (sys, flags, object) => {
1163
1483
  const terminalInfo = object ||
1164
1484
  Object.freeze({
1165
1485
  tty: sys.isTTY() ? true : false,
1166
- ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!flags.ci,
1486
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => { var _a; return !!((_a = sys.getEnvironmentVar) === null || _a === void 0 ? void 0 : _a.call(sys, v)); }).length > 0 || !!flags.ci,
1167
1487
  });
1168
1488
  return terminalInfo.tty && !terminalInfo.ci;
1169
1489
  };
1170
1490
  const UUID_REGEX = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i);
1171
- // Plucked from https://github.com/navify/jigra/blob/HEAD/cli/src/util/uuid.ts
1491
+ // Plucked from https://github.com/familyjs/jigra/blob/HEAD/cli/src/util/uuid.ts
1172
1492
  function uuidv4() {
1173
1493
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
1174
1494
  const r = (Math.random() * 16) | 0;
@@ -1192,7 +1512,7 @@ async function readJson(sys, path) {
1192
1512
  * @returns true if --debug has been passed, otherwise false
1193
1513
  */
1194
1514
  function hasDebug(flags) {
1195
- return flags.debug;
1515
+ return !!flags.debug;
1196
1516
  }
1197
1517
  /**
1198
1518
  * Does the command have the verbose and debug flags?
@@ -1200,25 +1520,14 @@ function hasDebug(flags) {
1200
1520
  * @returns true if both --debug and --verbose have been passed, otherwise false
1201
1521
  */
1202
1522
  function hasVerbose(flags) {
1203
- return flags.verbose && hasDebug(flags);
1204
- }
1205
-
1206
- /**
1207
- * Used to determine if tracking should occur.
1208
- * @param config The config passed into the Rindo command
1209
- * @param sys The system where the command is invoked
1210
- * @param ci whether or not the process is running in a Continuous Integration (CI) environment
1211
- * @returns true if telemetry should be sent, false otherwise
1212
- */
1213
- async function shouldTrack(config, sys, ci) {
1214
- return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
1523
+ return !!flags.verbose && hasDebug(flags);
1215
1524
  }
1216
1525
 
1217
1526
  const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
1218
- const defaultConfig = (sys) => sys.resolvePath(`${sys.homeDir()}/.navify/${isTest$1() ? 'tmp-config.json' : 'config.json'}`);
1219
- const defaultConfigDirectory = (sys) => sys.resolvePath(`${sys.homeDir()}/.navify`);
1527
+ const defaultConfig = (sys) => sys.resolvePath(`${sys.homeDir()}/.family/${isTest$1() ? 'tmp-config.json' : 'config.json'}`);
1528
+ const defaultConfigDirectory = (sys) => sys.resolvePath(`${sys.homeDir()}/.family`);
1220
1529
  /**
1221
- * Reads an Navify configuration file from disk, parses it, and performs any necessary corrections to it if certain
1530
+ * Reads an Family configuration file from disk, parses it, and performs any necessary corrections to it if certain
1222
1531
  * values are deemed to be malformed
1223
1532
  * @param sys The system where the command is invoked
1224
1533
  * @returns the config read from disk that has been potentially been updated
@@ -1232,7 +1541,7 @@ async function readConfig(sys) {
1232
1541
  };
1233
1542
  await writeConfig(sys, config);
1234
1543
  }
1235
- else if (!UUID_REGEX.test(config['tokens.telemetry'])) {
1544
+ else if (!config['tokens.telemetry'] || !UUID_REGEX.test(config['tokens.telemetry'])) {
1236
1545
  const newUuid = uuidv4();
1237
1546
  await writeConfig(sys, { ...config, 'tokens.telemetry': newUuid });
1238
1547
  config['tokens.telemetry'] = newUuid;
@@ -1240,7 +1549,7 @@ async function readConfig(sys) {
1240
1549
  return config;
1241
1550
  }
1242
1551
  /**
1243
- * Writes an Navify configuration file to disk.
1552
+ * Writes an Family configuration file to disk.
1244
1553
  * @param sys The system where the command is invoked
1245
1554
  * @param config The config passed into the Rindo command
1246
1555
  * @returns boolean If the command was successful
@@ -1258,7 +1567,7 @@ async function writeConfig(sys, config) {
1258
1567
  return result;
1259
1568
  }
1260
1569
  /**
1261
- * Update a subset of the Navify config.
1570
+ * Update a subset of the Family config.
1262
1571
  * @param sys The system where the command is invoked
1263
1572
  * @param newOptions The new options to save
1264
1573
  * @returns boolean If the command was successful
@@ -1268,14 +1577,16 @@ async function updateConfig(sys, newOptions) {
1268
1577
  return await writeConfig(sys, Object.assign(config, newOptions));
1269
1578
  }
1270
1579
 
1271
- const isOutputTargetHydrate = (o) => o.type === DIST_HYDRATE_SCRIPT;
1272
- const isOutputTargetDocs = (o) => o.type === DOCS_README || o.type === DOCS_JSON || o.type === DOCS_CUSTOM || o.type === DOCS_VSCODE;
1273
- const DIST_HYDRATE_SCRIPT = 'dist-hydrate-script';
1274
- const DOCS_CUSTOM = 'docs-custom';
1275
- const DOCS_JSON = 'docs-json';
1276
- const DOCS_README = 'docs-readme';
1277
- const DOCS_VSCODE = 'docs-vscode';
1278
- const WWW = 'www';
1580
+ /**
1581
+ * Used to determine if tracking should occur.
1582
+ * @param config The config passed into the Rindo command
1583
+ * @param sys The system where the command is invoked
1584
+ * @param ci whether or not the process is running in a Continuous Integration (CI) environment
1585
+ * @returns true if telemetry should be sent, false otherwise
1586
+ */
1587
+ async function shouldTrack(config, sys, ci) {
1588
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
1589
+ }
1279
1590
 
1280
1591
  /**
1281
1592
  * Used to within taskBuild to provide the component_count property.
@@ -1286,11 +1597,11 @@ const WWW = 'www';
1286
1597
  * @param result The results of a compiler build.
1287
1598
  */
1288
1599
  async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
1289
- const tracking = await shouldTrack(config, sys, config.flags.ci);
1600
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
1290
1601
  if (!tracking) {
1291
1602
  return;
1292
1603
  }
1293
- const component_count = Object.keys(result.componentGraph).length;
1604
+ const component_count = result.componentGraph ? Object.keys(result.componentGraph).length : undefined;
1294
1605
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
1295
1606
  await sendMetric(sys, config, 'rindo_cli_command', data);
1296
1607
  config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
@@ -1369,38 +1680,41 @@ function getActiveTargets(config) {
1369
1680
  * @returns a Promise wrapping data for the telemetry endpoint
1370
1681
  */
1371
1682
  const prepareData = async (coreCompiler, config, sys, duration_ms, component_count = undefined) => {
1683
+ var _a, _b, _c;
1372
1684
  const { typescript, rollup } = coreCompiler.versions || { typescript: 'unknown', rollup: 'unknown' };
1373
1685
  const { packages, packagesNoVersions } = await getInstalledPackages(sys, config);
1374
1686
  const targets = getActiveTargets(config);
1375
1687
  const yarn = isUsingYarn(sys);
1376
1688
  const rindo = coreCompiler.version || 'unknown';
1377
1689
  const system = `${sys.name} ${sys.version}`;
1378
- const os_name = sys.details.platform;
1379
- const os_version = sys.details.release;
1380
- const cpu_model = sys.details.cpuModel;
1690
+ const os_name = (_a = sys.details) === null || _a === void 0 ? void 0 : _a.platform;
1691
+ const os_version = (_b = sys.details) === null || _b === void 0 ? void 0 : _b.release;
1692
+ const cpu_model = (_c = sys.details) === null || _c === void 0 ? void 0 : _c.cpuModel;
1381
1693
  const build = coreCompiler.buildId || 'unknown';
1382
1694
  const has_app_pwa_config = hasAppTarget(config);
1383
1695
  const anonymizedConfig = anonymizeConfigForTelemetry(config);
1696
+ const is_browser_env = IS_BROWSER_ENV;
1384
1697
  return {
1385
- yarn,
1386
- duration_ms,
1698
+ arguments: config.flags.args,
1699
+ build,
1387
1700
  component_count,
1388
- targets,
1701
+ config: anonymizedConfig,
1702
+ cpu_model,
1703
+ duration_ms,
1704
+ has_app_pwa_config,
1705
+ is_browser_env,
1706
+ os_name,
1707
+ os_version,
1389
1708
  packages,
1390
1709
  packages_no_versions: packagesNoVersions,
1391
- arguments: config.flags.args,
1392
- task: config.flags.task,
1710
+ rollup,
1393
1711
  rindo,
1394
1712
  system,
1395
1713
  system_major: getMajorVersion(system),
1396
- os_name,
1397
- os_version,
1398
- cpu_model,
1399
- build,
1714
+ targets,
1715
+ task: config.flags.task,
1400
1716
  typescript,
1401
- rollup,
1402
- has_app_pwa_config,
1403
- config: anonymizedConfig,
1717
+ yarn,
1404
1718
  };
1405
1719
  };
1406
1720
  // props in output targets for which we retain their original values when
@@ -1424,7 +1738,16 @@ const CONFIG_PROPS_TO_ANONYMIZE = [
1424
1738
  // Props we delete entirely from the config for telemetry
1425
1739
  //
1426
1740
  // TODO: Investigate improving anonymization for tsCompilerOptions and devServer
1427
- const CONFIG_PROPS_TO_DELETE = ['sys', 'logger', 'tsCompilerOptions', 'devServer'];
1741
+ const CONFIG_PROPS_TO_DELETE = [
1742
+ 'commonjs',
1743
+ 'devServer',
1744
+ 'env',
1745
+ 'logger',
1746
+ 'rollupConfig',
1747
+ 'sys',
1748
+ 'testing',
1749
+ 'tsCompilerOptions',
1750
+ ];
1428
1751
  /**
1429
1752
  * Anonymize the config for telemetry, replacing potentially revealing config props
1430
1753
  * with a placeholder string if they are present (this lets us still track how frequently
@@ -1474,7 +1797,7 @@ const anonymizeConfigForTelemetry = (config) => {
1474
1797
  /**
1475
1798
  * Reads package-lock.json, yarn.lock, and package.json files in order to cross-reference
1476
1799
  * the dependencies and devDependencies properties. Pulls up the current installed version
1477
- * of each package under the @rindo, @navify, and @jigra scopes.
1800
+ * of each package under the @rindo, @familyjs, and @jigra scopes.
1478
1801
  *
1479
1802
  * @param sys the system instance where telemetry is invoked
1480
1803
  * @param config the Rindo configuration associated with the current task that triggered telemetry
@@ -1496,16 +1819,16 @@ async function getInstalledPackages(sys, config) {
1496
1819
  ...packageJson.devDependencies,
1497
1820
  ...packageJson.dependencies,
1498
1821
  });
1499
- // Collect packages only in the rindo, navify, or jigra org's:
1822
+ // Collect packages only in the rindo, familyjs, or jigra org's:
1500
1823
  // https://www.npmjs.com/org/rindo
1501
- const navifyPackages = rawPackages.filter(([k]) => k.startsWith('@rindo/') || k.startsWith('@navify/') || k.startsWith('@jigra/'));
1824
+ const familyPackages = rawPackages.filter(([k]) => k.startsWith('@rindo/') || k.startsWith('@familyjs/') || k.startsWith('@jigra/'));
1502
1825
  try {
1503
- packages = yarn ? await yarnPackages(sys, navifyPackages) : await npmPackages(sys, navifyPackages);
1826
+ packages = yarn ? await yarnPackages(sys, familyPackages) : await npmPackages(sys, familyPackages);
1504
1827
  }
1505
1828
  catch (e) {
1506
- packages = navifyPackages.map(([k, v]) => `${k}@${v.replace('^', '')}`);
1829
+ packages = familyPackages.map(([k, v]) => `${k}@${v.replace('^', '')}`);
1507
1830
  }
1508
- packagesNoVersions = navifyPackages.map(([k]) => `${k}`);
1831
+ packagesNoVersions = familyPackages.map(([k]) => `${k}`);
1509
1832
  return { packages, packagesNoVersions };
1510
1833
  }
1511
1834
  catch (err) {
@@ -1516,13 +1839,13 @@ async function getInstalledPackages(sys, config) {
1516
1839
  /**
1517
1840
  * Visits the npm lock file to find the exact versions that are installed
1518
1841
  * @param sys The system where the command is invoked
1519
- * @param navifyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@navify` from the package.json file.
1842
+ * @param familyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@familyjs` from the package.json file.
1520
1843
  * @returns an array of strings of all the packages and their versions.
1521
1844
  */
1522
- async function npmPackages(sys, navifyPackages) {
1845
+ async function npmPackages(sys, familyPackages) {
1523
1846
  const appRootDir = sys.getCurrentDirectory();
1524
1847
  const packageLockJson = await tryFn(readJson, sys, sys.resolvePath(appRootDir + '/package-lock.json'));
1525
- return navifyPackages.map(([k, v]) => {
1848
+ return familyPackages.map(([k, v]) => {
1526
1849
  var _a, _b, _c, _d;
1527
1850
  let version = (_d = (_b = (_a = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.dependencies[k]) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : (_c = packageLockJson === null || packageLockJson === void 0 ? void 0 : packageLockJson.devDependencies[k]) === null || _c === void 0 ? void 0 : _c.version) !== null && _d !== void 0 ? _d : v;
1528
1851
  version = version.includes('file:') ? sanitizeDeclaredVersion(v) : version;
@@ -1532,14 +1855,14 @@ async function npmPackages(sys, navifyPackages) {
1532
1855
  /**
1533
1856
  * Visits the yarn lock file to find the exact versions that are installed
1534
1857
  * @param sys The system where the command is invoked
1535
- * @param navifyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@navify` from the package.json file.
1858
+ * @param familyPackages a list of the found packages matching `@rindo`, `@jigra`, or `@familyjs` from the package.json file.
1536
1859
  * @returns an array of strings of all the packages and their versions.
1537
1860
  */
1538
- async function yarnPackages(sys, navifyPackages) {
1861
+ async function yarnPackages(sys, familyPackages) {
1539
1862
  const appRootDir = sys.getCurrentDirectory();
1540
1863
  const yarnLock = sys.readFileSync(sys.resolvePath(appRootDir + '/yarn.lock'));
1541
1864
  const yarnLockYml = sys.parseYarnLockFile(yarnLock);
1542
- return navifyPackages.map(([k, v]) => {
1865
+ return familyPackages.map(([k, v]) => {
1543
1866
  var _a;
1544
1867
  const identifiedVersion = `${k}@${v}`;
1545
1868
  let version = (_a = yarnLockYml.object[identifiedVersion]) === null || _a === void 0 ? void 0 : _a.version;
@@ -1604,7 +1927,7 @@ async function sendTelemetry(sys, config, data) {
1604
1927
  sent_at: now,
1605
1928
  };
1606
1929
  // This request is only made if telemetry is on.
1607
- const response = await sys.fetch('https://api-navifyjs.web.app/events/metrics', {
1930
+ const response = await sys.fetch('https://familyjs-api.web.app/events/metrics', {
1608
1931
  method: 'POST',
1609
1932
  headers: {
1610
1933
  'Content-Type': 'application/json',
@@ -1707,13 +2030,6 @@ const taskDocs = async (coreCompiler, config) => {
1707
2030
  await compiler.destroy();
1708
2031
  };
1709
2032
 
1710
- const IS_NODE_ENV = typeof global !== 'undefined' &&
1711
- typeof require === 'function' &&
1712
- !!global.process &&
1713
- typeof __filename === 'string' &&
1714
- (!global.origin || typeof global.origin !== 'string');
1715
- const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1716
-
1717
2033
  /**
1718
2034
  * Task to generate component boilerplate and write it to disk. This task can
1719
2035
  * cause the program to exit with an error under various circumstances, such as
@@ -1742,13 +2058,24 @@ const taskGenerate = async (coreCompiler, config) => {
1742
2058
  const { prompt } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../sys/node/prompts.js')); });
1743
2059
  const input = config.flags.unknownArgs.find((arg) => !arg.startsWith('-')) ||
1744
2060
  (await prompt({ name: 'tagName', type: 'text', message: 'Component tag name (dash-case):' })).tagName;
2061
+ if (undefined === input) {
2062
+ // in some shells (e.g. Windows PowerShell), hitting Ctrl+C results in a TypeError printed to the console.
2063
+ // explicitly return here to avoid printing the error message.
2064
+ return;
2065
+ }
1745
2066
  const { dir, base: componentName } = path.parse(input);
1746
2067
  const tagError = validateComponentTag(componentName);
1747
2068
  if (tagError) {
1748
2069
  config.logger.error(tagError);
1749
2070
  return config.sys.exit(1);
1750
2071
  }
1751
- const extensionsToGenerate = ['tsx', ...(await chooseFilesToGenerate())];
2072
+ const filesToGenerateExt = await chooseFilesToGenerate();
2073
+ if (undefined === filesToGenerateExt) {
2074
+ // in some shells (e.g. Windows PowerShell), hitting Ctrl+C results in a TypeError printed to the console.
2075
+ // explicitly return here to avoid printing the error message.
2076
+ return;
2077
+ }
2078
+ const extensionsToGenerate = ['tsx', ...filesToGenerateExt];
1752
2079
  const testFolder = extensionsToGenerate.some(isTest) ? 'test' : '';
1753
2080
  const outDir = path.join(absoluteSrcDir, 'components', dir, componentName);
1754
2081
  await config.sys.createDir(path.join(outDir, testFolder), { recursive: true });
@@ -1972,7 +2299,7 @@ const taskTelemetry = async (flags, sys, logger) => {
1972
2299
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1973
2300
  const isEnabling = flags.args.includes('on');
1974
2301
  const isDisabling = flags.args.includes('off');
1975
- 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')}`;
2302
+ const INFORMATION = `Opt in or out of telemetry. Information about the data we collect is available on our website: ${logger.bold('https://rindojs.web.app/telemetry')}`;
1976
2303
  const THANK_YOU = `Thank you for helping to make Rindo better! 💖`;
1977
2304
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
1978
2305
  const DISABLED_MESSAGE = `${logger.red('Disabled')}\n\n`;
@@ -2095,93 +2422,54 @@ const taskServe = async (config) => {
2095
2422
  };
2096
2423
 
2097
2424
  /**
2098
- * Creates an instance of a logger
2099
- * @returns the new logger instance
2425
+ * Entrypoint for any Rindo tests
2426
+ * @param config a validated Rindo configuration entity
2100
2427
  */
2101
- const createLogger = () => {
2102
- let useColors = IS_BROWSER_ENV;
2103
- let level = 'info';
2104
- return {
2105
- enableColors: (uc) => (useColors = uc),
2106
- getLevel: () => level,
2107
- setLevel: (l) => (level = l),
2108
- emoji: (e) => e,
2109
- info: console.log.bind(console),
2110
- warn: console.warn.bind(console),
2111
- error: console.error.bind(console),
2112
- debug: console.debug.bind(console),
2113
- red: (msg) => msg,
2114
- green: (msg) => msg,
2115
- yellow: (msg) => msg,
2116
- blue: (msg) => msg,
2117
- magenta: (msg) => msg,
2118
- cyan: (msg) => msg,
2119
- gray: (msg) => msg,
2120
- bold: (msg) => msg,
2121
- dim: (msg) => msg,
2122
- bgRed: (msg) => msg,
2123
- createTimeSpan: (_startMsg, _debug = false) => ({
2124
- duration: () => 0,
2125
- finish: () => 0,
2126
- }),
2127
- printDiagnostics(diagnostics) {
2128
- diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
2129
- },
2130
- };
2131
- };
2132
- const logDiagnostic = (diagnostic, useColors) => {
2133
- let color = BLUE;
2134
- let prefix = 'Build';
2135
- let msg = '';
2136
- if (diagnostic.level === 'error') {
2137
- color = RED;
2138
- prefix = 'Error';
2139
- }
2140
- else if (diagnostic.level === 'warn') {
2141
- color = YELLOW;
2142
- prefix = 'Warning';
2143
- }
2144
- if (diagnostic.header) {
2145
- prefix = diagnostic.header;
2428
+ const taskTest = async (config) => {
2429
+ if (!IS_NODE_ENV) {
2430
+ config.logger.error(`"test" command is currently only implemented for a NodeJS environment`);
2431
+ return config.sys.exit(1);
2146
2432
  }
2147
- const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
2148
- if (filePath) {
2149
- msg += filePath;
2150
- if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
2151
- msg += ', line ' + diagnostic.lineNumber;
2152
- if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
2153
- msg += ', column ' + diagnostic.columnNumber;
2154
- }
2433
+ config.buildDocs = false;
2434
+ const testingRunOpts = {
2435
+ e2e: !!config.flags.e2e,
2436
+ screenshot: !!config.flags.screenshot,
2437
+ spec: !!config.flags.spec,
2438
+ updateScreenshot: !!config.flags.updateScreenshot,
2439
+ };
2440
+ // always ensure we have jest modules installed
2441
+ const ensureModuleIds = ['@types/jest', 'jest', 'jest-cli'];
2442
+ if (testingRunOpts.e2e) {
2443
+ // if it's an e2e test, also make sure we're got
2444
+ // puppeteer modules installed and if browserExecutablePath is provided don't download Chromium use only puppeteer-core instead
2445
+ const puppeteer = config.testing.browserExecutablePath ? 'puppeteer-core' : 'puppeteer';
2446
+ ensureModuleIds.push(puppeteer);
2447
+ if (testingRunOpts.screenshot) {
2448
+ // ensure we've got pixelmatch for screenshots
2449
+ config.logger.warn(config.logger.yellow(`EXPERIMENTAL: screenshot visual diff testing is currently under heavy development and has not reached a stable status. However, any assistance testing would be appreciated.`));
2155
2450
  }
2156
- msg += '\n';
2157
- }
2158
- msg += diagnostic.messageText;
2159
- if (diagnostic.lines && diagnostic.lines.length > 0) {
2160
- diagnostic.lines.forEach((l) => {
2161
- msg += '\n' + l.lineNumber + ': ' + l.text;
2162
- });
2163
- msg += '\n';
2164
2451
  }
2165
- if (useColors) {
2166
- const styledPrefix = [
2167
- '%c' + prefix,
2168
- `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
2169
- ];
2170
- console.log(...styledPrefix, msg);
2171
- }
2172
- else if (diagnostic.level === 'error') {
2173
- console.error(msg);
2452
+ // ensure we've got the required modules installed
2453
+ const diagnostics = await config.sys.lazyRequire.ensure(config.rootDir, ensureModuleIds);
2454
+ if (diagnostics.length > 0) {
2455
+ config.logger.printDiagnostics(diagnostics);
2456
+ return config.sys.exit(1);
2174
2457
  }
2175
- else if (diagnostic.level === 'warn') {
2176
- console.warn(msg);
2458
+ try {
2459
+ // let's test!
2460
+ const { createTesting } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../testing/index.js')); });
2461
+ const testing = await createTesting(config);
2462
+ const passed = await testing.run(testingRunOpts);
2463
+ await testing.destroy();
2464
+ if (!passed) {
2465
+ return config.sys.exit(1);
2466
+ }
2177
2467
  }
2178
- else {
2179
- console.log(msg);
2468
+ catch (e) {
2469
+ config.logger.error(e);
2470
+ return config.sys.exit(1);
2180
2471
  }
2181
- };
2182
- const YELLOW = `#f39c12`;
2183
- const RED = `#c0392b`;
2184
- const BLUE = `#3498db`;
2472
+ };
2185
2473
 
2186
2474
  const run = async (init) => {
2187
2475
  const { args, logger, sys } = init;
@@ -2197,7 +2485,7 @@ const run = async (init) => {
2197
2485
  if (isFunction(sys.applyGlobalPatch)) {
2198
2486
  sys.applyGlobalPatch(sys.getCurrentDirectory());
2199
2487
  }
2200
- if (task === 'help' || flags.help) {
2488
+ if (!task || task === 'help' || flags.help) {
2201
2489
  await taskHelp(createConfigFlags({ task: 'help', args }), logger, sys);
2202
2490
  return;
2203
2491
  }
@@ -2266,15 +2554,16 @@ const run = async (init) => {
2266
2554
  * @public
2267
2555
  */
2268
2556
  const runTask = async (coreCompiler, config, task, sys) => {
2269
- var _a, _b, _c, _d, _e;
2557
+ var _a, _b, _c, _d, _e, _f;
2270
2558
  const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2271
2559
  const strictConfig = {
2272
2560
  ...config,
2273
2561
  flags: createConfigFlags((_b = config.flags) !== null && _b !== void 0 ? _b : { task }),
2274
2562
  logger,
2275
2563
  outputTargets: (_c = config.outputTargets) !== null && _c !== void 0 ? _c : [],
2276
- sys: (_d = sys !== null && sys !== void 0 ? sys : config.sys) !== null && _d !== void 0 ? _d : coreCompiler.createSystem({ logger }),
2277
- testing: (_e = config.testing) !== null && _e !== void 0 ? _e : {},
2564
+ rootDir: (_d = config.rootDir) !== null && _d !== void 0 ? _d : '/',
2565
+ sys: (_e = sys !== null && sys !== void 0 ? sys : config.sys) !== null && _e !== void 0 ? _e : coreCompiler.createSystem({ logger }),
2566
+ testing: (_f = config.testing) !== null && _f !== void 0 ? _f : {},
2278
2567
  };
2279
2568
  switch (task) {
2280
2569
  case 'build':
@@ -2299,6 +2588,9 @@ const runTask = async (coreCompiler, config, task, sys) => {
2299
2588
  case 'telemetry':
2300
2589
  await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
2301
2590
  break;
2591
+ case 'test':
2592
+ await taskTest(strictConfig);
2593
+ break;
2302
2594
  case 'version':
2303
2595
  console.log(coreCompiler.version);
2304
2596
  break;