@rindo/core 2.17.0 → 2.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/cli/config-flags.d.ts +102 -0
  2. package/cli/index.cjs +598 -219
  3. package/cli/index.d.ts +2 -1
  4. package/cli/index.js +598 -219
  5. package/cli/package.json +1 -1
  6. package/compiler/package.json +1 -1
  7. package/compiler/rindo.js +337 -173
  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 +2 -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 +1 -1
  32. package/mock-doc/index.js +1 -1
  33. package/mock-doc/package.json +1 -1
  34. package/package.json +5 -3
  35. package/screenshot/package.json +1 -1
  36. package/sys/node/index.js +4 -4
  37. package/sys/node/package.json +1 -1
  38. package/sys/node/worker.js +1 -1
  39. package/testing/index.d.ts +1 -1
  40. package/testing/index.js +40 -24
  41. package/testing/jest/jest-config.d.ts +1 -1
  42. package/testing/jest/jest-runner.d.ts +3 -2
  43. package/testing/jest/jest-screenshot.d.ts +1 -1
  44. package/testing/mocks.d.ts +27 -2
  45. package/testing/package.json +1 -1
  46. package/testing/puppeteer/puppeteer-browser.d.ts +2 -2
  47. package/testing/testing-utils.d.ts +74 -2
  48. package/testing/testing.d.ts +2 -2
package/cli/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Rindo CLI (CommonJS) v2.17.0 | 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.17.0",
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;
@@ -980,11 +1255,10 @@ const WWW = 'www';
980
1255
  *
981
1256
  * @param sys The system where the command is invoked
982
1257
  * @param config The config passed into the Rindo command
983
- * @param logger The tool used to do logging
984
1258
  * @param coreCompiler The compiler used to do builds
985
1259
  * @param result The results of a compiler build.
986
1260
  */
987
- async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) {
1261
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
988
1262
  const tracking = await shouldTrack(config, sys, config.flags.ci);
989
1263
  if (!tracking) {
990
1264
  return;
@@ -992,21 +1266,19 @@ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, r
992
1266
  const component_count = Object.keys(result.componentGraph).length;
993
1267
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
994
1268
  await sendMetric(sys, config, 'rindo_cli_command', data);
995
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1269
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
996
1270
  }
997
1271
  /**
998
1272
  * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
999
1273
  *
1000
1274
  * @param sys The system where the command is invoked
1001
1275
  * @param config The config passed into the Rindo command
1002
- * @param logger The tool used to do logging
1003
1276
  * @param coreCompiler The compiler used to do builds
1004
1277
  * @param action A Promise-based function to call in order to get the duration of any given command.
1005
1278
  * @returns void
1006
1279
  */
1007
- async function telemetryAction(sys, config, logger, coreCompiler, action) {
1008
- var _a;
1009
- 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);
1010
1282
  let duration = undefined;
1011
1283
  let error;
1012
1284
  if (action) {
@@ -1026,7 +1298,7 @@ async function telemetryAction(sys, config, logger, coreCompiler, action) {
1026
1298
  }
1027
1299
  const data = await prepareData(coreCompiler, config, sys, duration);
1028
1300
  await sendMetric(sys, config, 'rindo_cli_command', data);
1029
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1301
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1030
1302
  if (error) {
1031
1303
  throw error;
1032
1304
  }
@@ -1194,7 +1466,7 @@ async function getInstalledPackages(sys, config) {
1194
1466
  return { packages, packagesNoVersions };
1195
1467
  }
1196
1468
  catch (err) {
1197
- hasDebug(config) && console.error(err);
1469
+ hasDebug(config.flags) && console.error(err);
1198
1470
  return { packages, packagesNoVersions };
1199
1471
  }
1200
1472
  }
@@ -1296,15 +1568,15 @@ async function sendTelemetry(sys, config, data) {
1296
1568
  },
1297
1569
  body: JSON.stringify(body),
1298
1570
  });
1299
- hasVerbose(config) &&
1571
+ hasVerbose(config.flags) &&
1300
1572
  console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1301
1573
  if (response.status !== 204) {
1302
- hasVerbose(config) &&
1574
+ hasVerbose(config.flags) &&
1303
1575
  console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1304
1576
  }
1305
1577
  }
1306
1578
  catch (e) {
1307
- hasVerbose(config) && console.debug('Telemetry request failed:', e);
1579
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1308
1580
  }
1309
1581
  }
1310
1582
  /**
@@ -1361,7 +1633,7 @@ const taskBuild = async (coreCompiler, config, sys) => {
1361
1633
  const results = await compiler.build();
1362
1634
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1363
1635
  if (sys) {
1364
- await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results);
1636
+ await telemetryBuildFinishedAction(sys, config, coreCompiler, results);
1365
1637
  }
1366
1638
  await compiler.destroy();
1367
1639
  if (results.hasError) {
@@ -1399,7 +1671,8 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1399
1671
  typeof require === 'function' &&
1400
1672
  !!global.process &&
1401
1673
  typeof __filename === 'string' &&
1402
- (!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';
1403
1676
 
1404
1677
  /**
1405
1678
  * Task to generate component boilerplate and write it to disk. This task can
@@ -1593,7 +1866,8 @@ export class ${toPascalCase(tagName)} {
1593
1866
  `;
1594
1867
  };
1595
1868
  /**
1596
- * Get the boilerplate for style.
1869
+ * Get the boilerplate for style for a generated component
1870
+ * @returns a boilerplate CSS block
1597
1871
  */
1598
1872
  const getStyleUrlBoilerplate = () => `:host {
1599
1873
  display: block;
@@ -1647,10 +1921,17 @@ describe('${tagName}', () => {
1647
1921
  */
1648
1922
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1649
1923
 
1650
- 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) => {
1651
1932
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1652
- const isEnabling = config.flags.args.includes('on');
1653
- const isDisabling = config.flags.args.includes('off');
1933
+ const isEnabling = flags.args.includes('on');
1934
+ const isDisabling = flags.args.includes('off');
1654
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')}`;
1655
1936
  const THANK_YOU = `Thank you for helping to make Rindo better! 💖`;
1656
1937
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
@@ -1679,7 +1960,14 @@ const taskTelemetry = async (config, sys, logger) => {
1679
1960
  `);
1680
1961
  };
1681
1962
 
1682
- 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) => {
1683
1971
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1684
1972
  console.log(`
1685
1973
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
@@ -1712,7 +2000,7 @@ const taskHelp = async (config, logger, sys) => {
1712
2000
  `);
1713
2001
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1714
2002
  if (sys) {
1715
- await taskTelemetry(config, sys, logger);
2003
+ await taskTelemetry(flags, sys, logger);
1716
2004
  }
1717
2005
  console.log(`
1718
2006
  ${logger.bold('Examples:')}
@@ -1769,6 +2057,95 @@ const taskServe = async (config) => {
1769
2057
  });
1770
2058
  };
1771
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
+
1772
2149
  const run = async (init) => {
1773
2150
  const { args, logger, sys } = init;
1774
2151
  try {
@@ -1784,7 +2161,7 @@ const run = async (init) => {
1784
2161
  sys.applyGlobalPatch(sys.getCurrentDirectory());
1785
2162
  }
1786
2163
  if (task === 'help' || flags.help) {
1787
- await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys);
2164
+ await taskHelp({ task: 'help', args }, logger, sys);
1788
2165
  return;
1789
2166
  }
1790
2167
  startupLog(logger, task);
@@ -1810,7 +2187,7 @@ const run = async (init) => {
1810
2187
  startupLogVersion(logger, task, coreCompiler);
1811
2188
  loadedCompilerLog(sys, logger, flags, coreCompiler);
1812
2189
  if (task === 'info') {
1813
- await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => {
2190
+ await telemetryAction(sys, { flags: { task: 'info' }, logger }, coreCompiler, async () => {
1814
2191
  await taskInfo(coreCompiler, sys, logger);
1815
2192
  });
1816
2193
  return;
@@ -1833,7 +2210,7 @@ const run = async (init) => {
1833
2210
  sys.applyGlobalPatch(validated.config.rootDir);
1834
2211
  }
1835
2212
  await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
1836
- await telemetryAction(sys, validated.config, logger, coreCompiler, async () => {
2213
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
1837
2214
  await runTask(coreCompiler, validated.config, task, sys);
1838
2215
  });
1839
2216
  }
@@ -1846,40 +2223,42 @@ const run = async (init) => {
1846
2223
  }
1847
2224
  };
1848
2225
  const runTask = async (coreCompiler, config, task, sys) => {
1849
- config.flags = config.flags || { task };
1850
- 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 || [];
1851
2230
  switch (task) {
1852
2231
  case 'build':
1853
- await taskBuild(coreCompiler, config, sys);
2232
+ await taskBuild(coreCompiler, strictConfig, sys);
1854
2233
  break;
1855
2234
  case 'docs':
1856
- await taskDocs(coreCompiler, config);
2235
+ await taskDocs(coreCompiler, strictConfig);
1857
2236
  break;
1858
2237
  case 'generate':
1859
2238
  case 'g':
1860
- await taskGenerate(coreCompiler, config);
2239
+ await taskGenerate(coreCompiler, strictConfig);
1861
2240
  break;
1862
2241
  case 'help':
1863
- await taskHelp(config, config.logger, sys);
2242
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1864
2243
  break;
1865
2244
  case 'prerender':
1866
- await taskPrerender(coreCompiler, config);
2245
+ await taskPrerender(coreCompiler, strictConfig);
1867
2246
  break;
1868
2247
  case 'serve':
1869
- await taskServe(config);
2248
+ await taskServe(strictConfig);
1870
2249
  break;
1871
2250
  case 'telemetry':
1872
2251
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1873
2252
  if (sys) {
1874
- await taskTelemetry(config, sys, config.logger);
2253
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
1875
2254
  }
1876
2255
  break;
1877
2256
  case 'version':
1878
2257
  console.log(coreCompiler.version);
1879
2258
  break;
1880
2259
  default:
1881
- config.logger.error(`${config.logger.emoji('❌ ')}Invalid rindo command, please see the options below:`);
1882
- 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);
1883
2262
  return config.sys.exit(1);
1884
2263
  }
1885
2264
  };