@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.js CHANGED
@@ -1,8 +1,50 @@
1
1
  /*!
2
- Rindo CLI v2.17.0 | MIT Licensed | https://rindojs.web.app
2
+ Rindo CLI v2.17.1 | MIT Licensed | https://rindojs.web.app
3
3
  */
4
- const toLowerCase = (str) => str.toLowerCase();
5
- const dashToPascalCase = (str) => toLowerCase(str)
4
+ /**
5
+ * This sets the log level hierarchy for our terminal logger, ranging from
6
+ * most to least verbose.
7
+ *
8
+ * Ordering the levels like this lets us easily check whether we should log a
9
+ * message at a given time. For instance, if the log level is set to `'warn'`,
10
+ * then anything passed to the logger with level `'warn'` or `'error'` should
11
+ * be logged, but we should _not_ log anything with level `'info'` or `'debug'`.
12
+ *
13
+ * If we have a current log level `currentLevel` and a message with level
14
+ * `msgLevel` is passed to the logger, we can determine whether or not we should
15
+ * log it by checking if the log level on the message is further up or at the
16
+ * same level in the hierarchy than `currentLevel`, like so:
17
+ *
18
+ * ```ts
19
+ * LOG_LEVELS.indexOf(msgLevel) >= LOG_LEVELS.indexOf(currentLevel)
20
+ * ```
21
+ *
22
+ * NOTE: for the reasons described above, do not change the order of the entries
23
+ * in this array without good reason!
24
+ */
25
+ const LOG_LEVELS = ['debug', 'info', 'warn', 'error'];
26
+
27
+ /**
28
+ * Convert a string from PascalCase to dash-case
29
+ *
30
+ * @param str the string to convert
31
+ * @returns a converted string
32
+ */
33
+ const toDashCase = (str) => str
34
+ .replace(/([A-Z0-9])/g, (match) => ` ${match[0]}`)
35
+ .trim()
36
+ .split(' ')
37
+ .join('-')
38
+ .toLowerCase();
39
+ /**
40
+ * Convert a string from dash-case / kebab-case to PascalCase (or CamelCase,
41
+ * or whatever you call it!)
42
+ *
43
+ * @param str a string to convert
44
+ * @returns a converted string
45
+ */
46
+ const dashToPascalCase = (str) => str
47
+ .toLowerCase()
6
48
  .split('-')
7
49
  .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
8
50
  .join('');
@@ -275,22 +317,211 @@ const validateComponentTag = (tag) => {
275
317
  return undefined;
276
318
  };
277
319
 
320
+ /**
321
+ * All the Boolean options supported by the Rindo CLI
322
+ */
323
+ const BOOLEAN_CLI_ARGS = [
324
+ 'build',
325
+ 'cache',
326
+ 'checkVersion',
327
+ 'ci',
328
+ 'compare',
329
+ 'debug',
330
+ 'dev',
331
+ 'devtools',
332
+ 'docs',
333
+ 'e2e',
334
+ 'es5',
335
+ 'esm',
336
+ 'headless',
337
+ 'help',
338
+ 'log',
339
+ 'open',
340
+ 'prerender',
341
+ 'prerenderExternal',
342
+ 'prod',
343
+ 'profile',
344
+ 'serviceWorker',
345
+ 'screenshot',
346
+ 'serve',
347
+ 'skipNodeCheck',
348
+ 'spec',
349
+ 'ssr',
350
+ 'stats',
351
+ 'updateScreenshot',
352
+ 'verbose',
353
+ 'version',
354
+ 'watch',
355
+ // JEST CLI OPTIONS
356
+ 'all',
357
+ 'automock',
358
+ 'bail',
359
+ // 'cache', Rindo already supports this argument
360
+ 'changedFilesWithAncestor',
361
+ // 'ci', Rindo already supports this argument
362
+ 'clearCache',
363
+ 'clearMocks',
364
+ 'collectCoverage',
365
+ 'color',
366
+ 'colors',
367
+ 'coverage',
368
+ // 'debug', Rindo already supports this argument
369
+ 'detectLeaks',
370
+ 'detectOpenHandles',
371
+ 'errorOnDeprecated',
372
+ 'expand',
373
+ 'findRelatedTests',
374
+ 'forceExit',
375
+ 'init',
376
+ 'injectGlobals',
377
+ 'json',
378
+ 'lastCommit',
379
+ 'listTests',
380
+ 'logHeapUsage',
381
+ 'noStackTrace',
382
+ 'notify',
383
+ 'onlyChanged',
384
+ 'onlyFailures',
385
+ 'passWithNoTests',
386
+ 'resetMocks',
387
+ 'resetModules',
388
+ 'restoreMocks',
389
+ 'runInBand',
390
+ 'runTestsByPath',
391
+ 'showConfig',
392
+ 'silent',
393
+ 'skipFilter',
394
+ 'testLocationInResults',
395
+ 'updateSnapshot',
396
+ 'useStderr',
397
+ // 'verbose', Rindo already supports this argument
398
+ // 'version', Rindo already supports this argument
399
+ // 'watch', Rindo already supports this argument
400
+ 'watchAll',
401
+ 'watchman',
402
+ ];
403
+ /**
404
+ * All the Number options supported by the Rindo CLI
405
+ */
406
+ const NUMBER_CLI_ARGS = [
407
+ 'port',
408
+ // JEST CLI ARGS
409
+ 'maxConcurrency',
410
+ 'testTimeout',
411
+ ];
412
+ /**
413
+ * All the String options supported by the Rindo CLI
414
+ */
415
+ const STRING_CLI_ARGS = [
416
+ 'address',
417
+ 'config',
418
+ 'docsApi',
419
+ 'docsJson',
420
+ 'emulate',
421
+ 'root',
422
+ 'screenshotConnector',
423
+ // JEST CLI ARGS
424
+ 'cacheDirectory',
425
+ 'changedSince',
426
+ 'collectCoverageFrom',
427
+ // 'config', Rindo already supports this argument
428
+ 'coverageDirectory',
429
+ 'coverageThreshold',
430
+ 'env',
431
+ 'filter',
432
+ 'globalSetup',
433
+ 'globalTeardown',
434
+ 'globals',
435
+ 'haste',
436
+ 'moduleNameMapper',
437
+ 'notifyMode',
438
+ 'outputFile',
439
+ 'preset',
440
+ 'prettierPath',
441
+ 'resolver',
442
+ 'rootDir',
443
+ 'runner',
444
+ 'testEnvironment',
445
+ 'testEnvironmentOptions',
446
+ 'testFailureExitCode',
447
+ 'testNamePattern',
448
+ 'testResultsProcessor',
449
+ 'testRunner',
450
+ 'testSequencer',
451
+ 'testURL',
452
+ 'timers',
453
+ 'transform',
454
+ // ARRAY ARGS
455
+ 'collectCoverageOnlyFrom',
456
+ 'coveragePathIgnorePatterns',
457
+ 'coverageReporters',
458
+ 'moduleDirectories',
459
+ 'moduleFileExtensions',
460
+ 'modulePathIgnorePatterns',
461
+ 'modulePaths',
462
+ 'projects',
463
+ 'reporters',
464
+ 'roots',
465
+ 'selectProjects',
466
+ 'setupFiles',
467
+ 'setupFilesAfterEnv',
468
+ 'snapshotSerializers',
469
+ 'testMatch',
470
+ 'testPathIgnorePatterns',
471
+ 'testPathPattern',
472
+ 'testRegex',
473
+ 'transformIgnorePatterns',
474
+ 'unmockedModulePathPatterns',
475
+ 'watchPathIgnorePatterns',
476
+ ];
477
+ /**
478
+ * All the CLI arguments which may have string or number values
479
+ *
480
+ * `maxWorkers` is an argument which is used both by Rindo _and_ by Jest,
481
+ * which means that we need to support parsing both string and number values.
482
+ */
483
+ const STRING_NUMBER_CLI_ARGS = ['maxWorkers'];
484
+ /**
485
+ * All the LogLevel-type options supported by the Rindo CLI
486
+ *
487
+ * This is a bit silly since there's only one such argument atm,
488
+ * but this approach lets us make sure that we're handling all
489
+ * our arguments in a type-safe way.
490
+ */
491
+ const LOG_LEVEL_CLI_ARGS = ['logLevel'];
492
+ /**
493
+ * For a small subset of CLI options we support a short alias e.g. `'h'` for `'help'`
494
+ */
495
+ const CLI_ARG_ALIASES = {
496
+ config: 'c',
497
+ help: 'h',
498
+ port: 'p',
499
+ version: 'v',
500
+ };
501
+
502
+ /**
503
+ * Parse command line arguments into a structured `ConfigFlags` object
504
+ *
505
+ * @param args an array of config flags
506
+ * @param sys an optional compiler system
507
+ * @returns a structured ConfigFlags object
508
+ */
278
509
  const parseFlags = (args, sys) => {
279
510
  const flags = {
280
511
  task: null,
281
512
  args: [],
282
513
  knownArgs: [],
283
- unknownArgs: null,
514
+ unknownArgs: [],
284
515
  };
285
516
  // cmd line has more priority over npm scripts cmd
286
- flags.args = args.slice();
517
+ flags.args = Array.isArray(args) ? args.slice() : [];
287
518
  if (flags.args.length > 0 && flags.args[0] && !flags.args[0].startsWith('-')) {
288
519
  flags.task = flags.args[0];
289
520
  }
290
- parseArgs(flags, flags.args, flags.knownArgs);
521
+ parseArgs(flags, flags.args);
291
522
  if (sys && sys.name === 'node') {
292
523
  const envArgs = getNpmConfigEnvArgs(sys);
293
- parseArgs(flags, envArgs, flags.knownArgs);
524
+ parseArgs(flags, envArgs);
294
525
  envArgs.forEach((envArg) => {
295
526
  if (!flags.args.includes(envArg)) {
296
527
  flags.args.push(envArg);
@@ -309,185 +540,230 @@ const parseFlags = (args, sys) => {
309
540
  return flags;
310
541
  };
311
542
  /**
312
- * Parse command line arguments that are whitelisted via the BOOLEAN_ARG_OPTS,
313
- * STRING_ARG_OPTS, and NUMBER_ARG_OPTS arrays in this file. Handles leading
314
- * dashes on arguments, aliases that are defined for a small number of argument
315
- * types, and parsing values for non-boolean arguments (e.g. port number).
543
+ * Parse command line arguments that are enumerated in the `config-flags`
544
+ * module. Handles leading dashes on arguments, aliases that are defined for a
545
+ * small number of arguments, and parsing values for non-boolean arguments
546
+ * (e.g. port number for the dev server).
316
547
  *
317
- * @param flags a ConfigFlags object
318
- * @param args an array of command-line arguments to parse
319
- * @param knownArgs an array to which all recognized, legal arguments are added
548
+ * @param flags a ConfigFlags object to which parsed arguments will be added
549
+ * @param args an array of command-line arguments to parse
320
550
  */
321
- const parseArgs = (flags, args, knownArgs) => {
322
- BOOLEAN_ARG_OPTS.forEach((booleanName) => {
323
- const alias = ARG_OPTS_ALIASES[booleanName];
324
- const flagKey = configCase(booleanName);
325
- if (typeof flags[flagKey] !== 'boolean') {
326
- flags[flagKey] = null;
551
+ const parseArgs = (flags, args) => {
552
+ BOOLEAN_CLI_ARGS.forEach((argName) => parseBooleanArg(flags, args, argName));
553
+ STRING_CLI_ARGS.forEach((argName) => parseStringArg(flags, args, argName));
554
+ NUMBER_CLI_ARGS.forEach((argName) => parseNumberArg(flags, args, argName));
555
+ STRING_NUMBER_CLI_ARGS.forEach((argName) => parseStringNumberArg(flags, args, argName));
556
+ LOG_LEVEL_CLI_ARGS.forEach((argName) => parseLogLevelArg(flags, args, argName));
557
+ };
558
+ /**
559
+ * Parse a boolean CLI argument. For these, we support the following formats:
560
+ *
561
+ * - `--booleanArg`
562
+ * - `--boolean-arg`
563
+ * - `--noBooleanArg`
564
+ * - `--no-boolean-arg`
565
+ *
566
+ * The final two variants should be parsed to a value of `false` on the config
567
+ * object.
568
+ *
569
+ * @param flags the config flags object, while we'll modify
570
+ * @param args our CLI arguments
571
+ * @param configCaseName the argument we want to look at right now
572
+ */
573
+ const parseBooleanArg = (flags, args, configCaseName) => {
574
+ // we support both dash-case and PascalCase versions of the parameter
575
+ // argName is 'configCase' version which can be found in BOOLEAN_ARG_OPTS
576
+ const alias = CLI_ARG_ALIASES[configCaseName];
577
+ const dashCaseName = toDashCase(configCaseName);
578
+ if (typeof flags[configCaseName] !== 'boolean') {
579
+ flags[configCaseName] = null;
580
+ }
581
+ args.forEach((cmdArg) => {
582
+ let value;
583
+ if (cmdArg === `--${configCaseName}` || cmdArg === `--${dashCaseName}`) {
584
+ value = true;
327
585
  }
328
- args.forEach((cmdArg) => {
329
- if (cmdArg === `--${booleanName}`) {
330
- flags[flagKey] = true;
331
- knownArgs.push(cmdArg);
332
- }
333
- else if (cmdArg === `--${flagKey}`) {
334
- flags[flagKey] = true;
335
- knownArgs.push(cmdArg);
336
- }
337
- else if (cmdArg === `--no-${booleanName}`) {
338
- flags[flagKey] = false;
339
- knownArgs.push(cmdArg);
340
- }
341
- else if (cmdArg === `--no${dashToPascalCase(booleanName)}`) {
342
- flags[flagKey] = false;
343
- knownArgs.push(cmdArg);
344
- }
345
- else if (alias && cmdArg === `-${alias}`) {
346
- flags[flagKey] = true;
347
- knownArgs.push(cmdArg);
348
- }
349
- });
350
- });
351
- STRING_ARG_OPTS.forEach((stringName) => {
352
- const alias = ARG_OPTS_ALIASES[stringName];
353
- const flagKey = configCase(stringName);
354
- if (typeof flags[flagKey] !== 'string') {
355
- flags[flagKey] = null;
586
+ else if (cmdArg === `--no-${dashCaseName}` || cmdArg === `--no${dashToPascalCase(dashCaseName)}`) {
587
+ value = false;
356
588
  }
357
- for (let i = 0; i < args.length; i++) {
358
- const cmdArg = args[i];
359
- if (cmdArg.startsWith(`--${stringName}=`)) {
360
- const values = cmdArg.split('=');
361
- values.shift();
362
- flags[flagKey] = values.join('=');
363
- knownArgs.push(cmdArg);
364
- }
365
- else if (cmdArg === `--${stringName}`) {
366
- flags[flagKey] = args[i + 1];
367
- knownArgs.push(cmdArg);
368
- knownArgs.push(args[i + 1]);
369
- }
370
- else if (cmdArg === `--${flagKey}`) {
371
- flags[flagKey] = args[i + 1];
372
- knownArgs.push(cmdArg);
373
- knownArgs.push(args[i + 1]);
374
- }
375
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
376
- const values = cmdArg.split('=');
377
- values.shift();
378
- flags[flagKey] = values.join('=');
379
- knownArgs.push(cmdArg);
380
- }
381
- else if (alias) {
382
- if (cmdArg.startsWith(`-${alias}=`)) {
383
- const values = cmdArg.split('=');
384
- values.shift();
385
- flags[flagKey] = values.join('=');
386
- knownArgs.push(cmdArg);
387
- }
388
- else if (cmdArg === `-${alias}`) {
389
- flags[flagKey] = args[i + 1];
390
- knownArgs.push(args[i + 1]);
391
- }
392
- }
589
+ else if (alias && cmdArg === `-${alias}`) {
590
+ value = true;
591
+ }
592
+ if (value !== undefined && cmdArg !== undefined) {
593
+ flags[configCaseName] = value;
594
+ flags.knownArgs.push(cmdArg);
393
595
  }
394
596
  });
395
- NUMBER_ARG_OPTS.forEach((numberName) => {
396
- const alias = ARG_OPTS_ALIASES[numberName];
397
- const flagKey = configCase(numberName);
398
- if (typeof flags[flagKey] !== 'number') {
399
- flags[flagKey] = null;
597
+ };
598
+ /**
599
+ * Parse a string CLI argument
600
+ *
601
+ * @param flags the config flags object, while we'll modify
602
+ * @param args our CLI arguments
603
+ * @param configCaseName the argument we want to look at right now
604
+ */
605
+ const parseStringArg = (flags, args, configCaseName) => {
606
+ if (typeof flags[configCaseName] !== 'string') {
607
+ flags[configCaseName] = null;
608
+ }
609
+ const { value, matchingArg } = getValue(args, configCaseName);
610
+ if (value !== undefined && matchingArg !== undefined) {
611
+ flags[configCaseName] = value;
612
+ flags.knownArgs.push(matchingArg);
613
+ flags.knownArgs.push(value);
614
+ }
615
+ };
616
+ /**
617
+ * Parse a number CLI argument
618
+ *
619
+ * @param flags the config flags object, while we'll modify
620
+ * @param args our CLI arguments
621
+ * @param configCaseName the argument we want to look at right now
622
+ */
623
+ const parseNumberArg = (flags, args, configCaseName) => {
624
+ if (typeof flags[configCaseName] !== 'number') {
625
+ flags[configCaseName] = null;
626
+ }
627
+ const { value, matchingArg } = getValue(args, configCaseName);
628
+ if (value !== undefined && matchingArg !== undefined) {
629
+ flags[configCaseName] = parseInt(value, 10);
630
+ flags.knownArgs.push(matchingArg);
631
+ flags.knownArgs.push(value);
632
+ }
633
+ };
634
+ /**
635
+ * Parse a CLI argument which may be either a string or a number
636
+ *
637
+ * @param flags the config flags object, while we'll modify
638
+ * @param args our CLI arguments
639
+ * @param configCaseName the argument we want to look at right now
640
+ */
641
+ const parseStringNumberArg = (flags, args, configCaseName) => {
642
+ if (!['number', 'string'].includes(typeof flags[configCaseName])) {
643
+ flags[configCaseName] = null;
644
+ }
645
+ const { value, matchingArg } = getValue(args, configCaseName);
646
+ if (value !== undefined && matchingArg !== undefined) {
647
+ if (CLI_ARG_STRING_REGEX.test(value)) {
648
+ // if it matches the regex we treat it like a string
649
+ flags[configCaseName] = value;
400
650
  }
401
- for (let i = 0; i < args.length; i++) {
402
- const cmdArg = args[i];
403
- if (cmdArg.startsWith(`--${numberName}=`)) {
404
- const values = cmdArg.split('=');
405
- values.shift();
406
- flags[flagKey] = parseInt(values.join(''), 10);
407
- knownArgs.push(cmdArg);
408
- }
409
- else if (cmdArg === `--${numberName}`) {
410
- flags[flagKey] = parseInt(args[i + 1], 10);
411
- knownArgs.push(args[i + 1]);
412
- }
413
- else if (cmdArg.startsWith(`--${flagKey}=`)) {
414
- const values = cmdArg.split('=');
415
- values.shift();
416
- flags[flagKey] = parseInt(values.join(''), 10);
417
- knownArgs.push(cmdArg);
418
- }
419
- else if (cmdArg === `--${flagKey}`) {
420
- flags[flagKey] = parseInt(args[i + 1], 10);
421
- knownArgs.push(args[i + 1]);
651
+ else {
652
+ // it was a number, great!
653
+ flags[configCaseName] = Number(value);
654
+ }
655
+ flags.knownArgs.push(matchingArg);
656
+ flags.knownArgs.push(value);
657
+ }
658
+ };
659
+ /**
660
+ * We use this regular expression to detect CLI parameters which
661
+ * should be parsed as string values (as opposed to numbers) for
662
+ * the argument types for which we support both a string and a
663
+ * number value.
664
+ *
665
+ * The regex tests for the presence of at least one character which is
666
+ * _not_ a digit (`\d`), a period (`\.`), or one of the characters `"e"`,
667
+ * `"E"`, `"+"`, or `"-"` (the latter four characters are necessary to
668
+ * support the admittedly unlikely use of scientific notation, like `"4e+0"`
669
+ * for `4`).
670
+ *
671
+ * Thus we'll match a string like `"50%"`, but not a string like `"50"` or
672
+ * `"5.0"`. If it matches a given string we conclude that the string should
673
+ * be parsed as a string literal, rather than using `Number` to convert it
674
+ * to a number.
675
+ */
676
+ const CLI_ARG_STRING_REGEX = /[^\d\.Ee\+\-]+/g;
677
+ /**
678
+ * Parse a LogLevel CLI argument. These can be only a specific
679
+ * set of strings, so this function takes care of validating that
680
+ * the value is correct.
681
+ *
682
+ * @param flags the config flags object, while we'll modify
683
+ * @param args our CLI arguments
684
+ * @param configCaseName the argument we want to look at right now
685
+ */
686
+ const parseLogLevelArg = (flags, args, configCaseName) => {
687
+ if (typeof flags[configCaseName] !== 'string') {
688
+ flags[configCaseName] = null;
689
+ }
690
+ const { value, matchingArg } = getValue(args, configCaseName);
691
+ if (value !== undefined && matchingArg !== undefined && isLogLevel(value)) {
692
+ flags[configCaseName] = value;
693
+ flags.knownArgs.push(matchingArg);
694
+ flags.knownArgs.push(value);
695
+ }
696
+ };
697
+ /**
698
+ * Helper for pulling values out from the raw array of CLI arguments. This logic
699
+ * is shared between a few different types of CLI args.
700
+ *
701
+ * We look for arguments in the following formats:
702
+ *
703
+ * - `--my-cli-argument value`
704
+ * - `--my-cli-argument=value`
705
+ * - `--myCliArgument value`
706
+ * - `--myCliArgument=value`
707
+ *
708
+ * We also check for shortened aliases, which we define for a few arguments.
709
+ *
710
+ * @param args the CLI args we're dealing with
711
+ * @param configCaseName the ConfigFlag key which we're looking to pull out a value for
712
+ * @returns the value for the flag as well as the exact string which it matched from
713
+ * the user input.
714
+ */
715
+ const getValue = (args, configCaseName) => {
716
+ // for some CLI args we have a short alias, like 'c' for 'config'
717
+ const alias = CLI_ARG_ALIASES[configCaseName];
718
+ // we support supplying arguments in both dash-case and configCase
719
+ // for ease of use
720
+ const dashCaseName = toDashCase(configCaseName);
721
+ let value;
722
+ let matchingArg;
723
+ args.forEach((arg, i) => {
724
+ if (arg.startsWith(`--${dashCaseName}=`) || arg.startsWith(`--${configCaseName}=`)) {
725
+ value = getEqualsValue(arg);
726
+ matchingArg = arg;
727
+ }
728
+ else if (arg === `--${dashCaseName}` || arg === `--${configCaseName}`) {
729
+ value = args[i + 1];
730
+ matchingArg = arg;
731
+ }
732
+ else if (alias) {
733
+ if (arg.startsWith(`-${alias}=`)) {
734
+ value = getEqualsValue(arg);
735
+ matchingArg = arg;
422
736
  }
423
- else if (alias) {
424
- if (cmdArg.startsWith(`-${alias}=`)) {
425
- const values = cmdArg.split('=');
426
- values.shift();
427
- flags[flagKey] = parseInt(values.join(''), 10);
428
- knownArgs.push(cmdArg);
429
- }
430
- else if (cmdArg === `-${alias}`) {
431
- flags[flagKey] = parseInt(args[i + 1], 10);
432
- knownArgs.push(args[i + 1]);
433
- }
737
+ else if (arg === `-${alias}`) {
738
+ value = args[i + 1];
739
+ matchingArg = arg;
434
740
  }
435
741
  }
436
742
  });
743
+ return { value, matchingArg };
437
744
  };
438
- const configCase = (prop) => {
439
- prop = dashToPascalCase(prop);
440
- return prop.charAt(0).toLowerCase() + prop.slice(1);
441
- };
442
- const BOOLEAN_ARG_OPTS = [
443
- 'build',
444
- 'cache',
445
- 'check-version',
446
- 'ci',
447
- 'compare',
448
- 'debug',
449
- 'dev',
450
- 'devtools',
451
- 'docs',
452
- 'e2e',
453
- 'es5',
454
- 'esm',
455
- 'headless',
456
- 'help',
457
- 'log',
458
- 'open',
459
- 'prerender',
460
- 'prerender-external',
461
- 'prod',
462
- 'profile',
463
- 'service-worker',
464
- 'screenshot',
465
- 'serve',
466
- 'skip-node-check',
467
- 'spec',
468
- 'ssr',
469
- 'stats',
470
- 'update-screenshot',
471
- 'verbose',
472
- 'version',
473
- 'watch',
474
- ];
475
- const NUMBER_ARG_OPTS = ['max-workers', 'port'];
476
- const STRING_ARG_OPTS = [
477
- 'address',
478
- 'config',
479
- 'docs-json',
480
- 'emulate',
481
- 'log-level',
482
- 'root',
483
- 'screenshot-connector',
484
- ];
485
- const ARG_OPTS_ALIASES = {
486
- config: 'c',
487
- help: 'h',
488
- port: 'p',
489
- version: 'v',
490
- };
745
+ /**
746
+ * When a parameter is set in the format `--foobar=12` at the CLI (as opposed to
747
+ * `--foobar 12`) we want to get the value after the `=` sign
748
+ *
749
+ * @param commandArgument the arg in question
750
+ * @returns the value after the `=`
751
+ */
752
+ const getEqualsValue = (commandArgument) => commandArgument.split('=').slice(1).join('=');
753
+ /**
754
+ * Small helper for getting type-system-level assurance that a `string` can be
755
+ * narrowed to a `LogLevel`
756
+ *
757
+ * @param maybeLogLevel the string to check
758
+ * @returns whether this is a `LogLevel`
759
+ */
760
+ const isLogLevel = (maybeLogLevel) =>
761
+ // unfortunately `includes` is typed on `ReadonlyArray<T>` as `(el: T):
762
+ // boolean` so a `string` cannot be passed to `includes` on a
763
+ // `ReadonlyArray` 😢 thus we `as any`
764
+ //
765
+ // see microsoft/TypeScript#31018 for some discussion of this
766
+ LOG_LEVELS.includes(maybeLogLevel);
491
767
  const getNpmConfigEnvArgs = (sys) => {
492
768
  // process.env.npm_config_argv
493
769
  // {"remain":["4444"],"cooked":["run","serve","--port","4444"],"original":["run","serve","--port","4444"]}
@@ -508,7 +784,7 @@ const getNpmConfigEnvArgs = (sys) => {
508
784
  const dependencies = [
509
785
  {
510
786
  name: "@rindo/core",
511
- version: "2.17.0",
787
+ version: "2.17.1",
512
788
  main: "compiler/rindo.js",
513
789
  resources: [
514
790
  "package.json",
@@ -833,12 +1109,11 @@ const tryFn = async (fn, ...args) => {
833
1109
  }
834
1110
  return null;
835
1111
  };
836
- const isInteractive = (sys, config, object) => {
837
- var _a;
1112
+ const isInteractive = (sys, flags, object) => {
838
1113
  const terminalInfo = object ||
839
1114
  Object.freeze({
840
1115
  tty: sys.isTTY() ? true : false,
841
- ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!((_a = config.flags) === null || _a === void 0 ? void 0 : _a.ci),
1116
+ ci: ['CI', 'BUILD_ID', 'BUILD_NUMBER', 'BITBUCKET_COMMIT', 'CODEBUILD_BUILD_ARN'].filter((v) => !!sys.getEnvironmentVar(v)).length > 0 || !!flags.ci,
842
1117
  });
843
1118
  return terminalInfo.tty && !terminalInfo.ci;
844
1119
  };
@@ -862,19 +1137,19 @@ async function readJson(sys, path) {
862
1137
  }
863
1138
  /**
864
1139
  * Does the command have the debug flag?
865
- * @param config The config passed into the Rindo command
1140
+ * @param flags The configuration flags passed into the Rindo command
866
1141
  * @returns true if --debug has been passed, otherwise false
867
1142
  */
868
- function hasDebug(config) {
869
- return config.flags.debug;
1143
+ function hasDebug(flags) {
1144
+ return flags.debug;
870
1145
  }
871
1146
  /**
872
1147
  * Does the command have the verbose and debug flags?
873
- * @param config The config passed into the Rindo command
1148
+ * @param flags The configuration flags passed into the Rindo command
874
1149
  * @returns true if both --debug and --verbose have been passed, otherwise false
875
1150
  */
876
- function hasVerbose(config) {
877
- return config.flags.verbose && hasDebug(config);
1151
+ function hasVerbose(flags) {
1152
+ return flags.verbose && hasDebug(flags);
878
1153
  }
879
1154
 
880
1155
  /**
@@ -885,7 +1160,7 @@ function hasVerbose(config) {
885
1160
  * @returns true if telemetry should be sent, false otherwise
886
1161
  */
887
1162
  async function shouldTrack(config, sys, ci) {
888
- return !ci && isInteractive(sys, config) && (await checkTelemetry(sys));
1163
+ return !ci && isInteractive(sys, config.flags) && (await checkTelemetry(sys));
889
1164
  }
890
1165
 
891
1166
  const isTest$1 = () => process.env.JEST_WORKER_ID !== undefined;
@@ -956,11 +1231,10 @@ const WWW = 'www';
956
1231
  *
957
1232
  * @param sys The system where the command is invoked
958
1233
  * @param config The config passed into the Rindo command
959
- * @param logger The tool used to do logging
960
1234
  * @param coreCompiler The compiler used to do builds
961
1235
  * @param result The results of a compiler build.
962
1236
  */
963
- async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, result) {
1237
+ async function telemetryBuildFinishedAction(sys, config, coreCompiler, result) {
964
1238
  const tracking = await shouldTrack(config, sys, config.flags.ci);
965
1239
  if (!tracking) {
966
1240
  return;
@@ -968,21 +1242,19 @@ async function telemetryBuildFinishedAction(sys, config, logger, coreCompiler, r
968
1242
  const component_count = Object.keys(result.componentGraph).length;
969
1243
  const data = await prepareData(coreCompiler, config, sys, result.duration, component_count);
970
1244
  await sendMetric(sys, config, 'rindo_cli_command', data);
971
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1245
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
972
1246
  }
973
1247
  /**
974
1248
  * A function to wrap a compiler task function around. Will send telemetry if, and only if, the machine allows.
975
1249
  *
976
1250
  * @param sys The system where the command is invoked
977
1251
  * @param config The config passed into the Rindo command
978
- * @param logger The tool used to do logging
979
1252
  * @param coreCompiler The compiler used to do builds
980
1253
  * @param action A Promise-based function to call in order to get the duration of any given command.
981
1254
  * @returns void
982
1255
  */
983
- async function telemetryAction(sys, config, logger, coreCompiler, action) {
984
- var _a;
985
- const tracking = await shouldTrack(config, sys, !!((_a = config === null || config === void 0 ? void 0 : config.flags) === null || _a === void 0 ? void 0 : _a.ci));
1256
+ async function telemetryAction(sys, config, coreCompiler, action) {
1257
+ const tracking = await shouldTrack(config, sys, !!config.flags.ci);
986
1258
  let duration = undefined;
987
1259
  let error;
988
1260
  if (action) {
@@ -1002,7 +1274,7 @@ async function telemetryAction(sys, config, logger, coreCompiler, action) {
1002
1274
  }
1003
1275
  const data = await prepareData(coreCompiler, config, sys, duration);
1004
1276
  await sendMetric(sys, config, 'rindo_cli_command', data);
1005
- logger.debug(`${logger.blue('Telemetry')}: ${logger.gray(JSON.stringify(data))}`);
1277
+ config.logger.debug(`${config.logger.blue('Telemetry')}: ${config.logger.gray(JSON.stringify(data))}`);
1006
1278
  if (error) {
1007
1279
  throw error;
1008
1280
  }
@@ -1170,7 +1442,7 @@ async function getInstalledPackages(sys, config) {
1170
1442
  return { packages, packagesNoVersions };
1171
1443
  }
1172
1444
  catch (err) {
1173
- hasDebug(config) && console.error(err);
1445
+ hasDebug(config.flags) && console.error(err);
1174
1446
  return { packages, packagesNoVersions };
1175
1447
  }
1176
1448
  }
@@ -1272,15 +1544,15 @@ async function sendTelemetry(sys, config, data) {
1272
1544
  },
1273
1545
  body: JSON.stringify(body),
1274
1546
  });
1275
- hasVerbose(config) &&
1547
+ hasVerbose(config.flags) &&
1276
1548
  console.debug('\nSent %O metric to events service (status: %O)', data.name, response.status, '\n');
1277
1549
  if (response.status !== 204) {
1278
- hasVerbose(config) &&
1550
+ hasVerbose(config.flags) &&
1279
1551
  console.debug('\nBad response from events service. Request body: %O', response.body.toString(), '\n');
1280
1552
  }
1281
1553
  }
1282
1554
  catch (e) {
1283
- hasVerbose(config) && console.debug('Telemetry request failed:', e);
1555
+ hasVerbose(config.flags) && console.debug('Telemetry request failed:', e);
1284
1556
  }
1285
1557
  }
1286
1558
  /**
@@ -1337,7 +1609,7 @@ const taskBuild = async (coreCompiler, config, sys) => {
1337
1609
  const results = await compiler.build();
1338
1610
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1339
1611
  if (sys) {
1340
- await telemetryBuildFinishedAction(sys, config, config.logger, coreCompiler, results);
1612
+ await telemetryBuildFinishedAction(sys, config, coreCompiler, results);
1341
1613
  }
1342
1614
  await compiler.destroy();
1343
1615
  if (results.hasError) {
@@ -1375,7 +1647,8 @@ const IS_NODE_ENV = typeof global !== 'undefined' &&
1375
1647
  typeof require === 'function' &&
1376
1648
  !!global.process &&
1377
1649
  typeof __filename === 'string' &&
1378
- (!global.origin || typeof global.origin !== 'string');
1650
+ (!global.origin || typeof global.origin !== 'string');
1651
+ const IS_BROWSER_ENV = typeof location !== 'undefined' && typeof navigator !== 'undefined' && typeof XMLHttpRequest !== 'undefined';
1379
1652
 
1380
1653
  /**
1381
1654
  * Task to generate component boilerplate and write it to disk. This task can
@@ -1569,7 +1842,8 @@ export class ${toPascalCase(tagName)} {
1569
1842
  `;
1570
1843
  };
1571
1844
  /**
1572
- * Get the boilerplate for style.
1845
+ * Get the boilerplate for style for a generated component
1846
+ * @returns a boilerplate CSS block
1573
1847
  */
1574
1848
  const getStyleUrlBoilerplate = () => `:host {
1575
1849
  display: block;
@@ -1623,10 +1897,17 @@ describe('${tagName}', () => {
1623
1897
  */
1624
1898
  const toPascalCase = (str) => str.split('-').reduce((res, part) => res + part[0].toUpperCase() + part.slice(1), '');
1625
1899
 
1626
- const taskTelemetry = async (config, sys, logger) => {
1900
+ /**
1901
+ * Entrypoint for the Telemetry task
1902
+ * @param flags configuration flags provided to Rindo when a task was called (either this task or a task that invokes
1903
+ * telemetry)
1904
+ * @param sys the abstraction for interfacing with the operating system
1905
+ * @param logger a logging implementation to log the results out to the user
1906
+ */
1907
+ const taskTelemetry = async (flags, sys, logger) => {
1627
1908
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1628
- const isEnabling = config.flags.args.includes('on');
1629
- const isDisabling = config.flags.args.includes('off');
1909
+ const isEnabling = flags.args.includes('on');
1910
+ const isDisabling = flags.args.includes('off');
1630
1911
  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')}`;
1631
1912
  const THANK_YOU = `Thank you for helping to make Rindo better! 💖`;
1632
1913
  const ENABLED_MESSAGE = `${logger.green('Enabled')}. ${THANK_YOU}\n\n`;
@@ -1655,7 +1936,14 @@ const taskTelemetry = async (config, sys, logger) => {
1655
1936
  `);
1656
1937
  };
1657
1938
 
1658
- const taskHelp = async (config, logger, sys) => {
1939
+ /**
1940
+ * Entrypoint for the Help task, providing Rindo usage context to the user
1941
+ * @param flags configuration flags provided to Rindo when a task was call (either this task or a task that invokes
1942
+ * telemetry)
1943
+ * @param logger a logging implementation to log the results out to the user
1944
+ * @param sys the abstraction for interfacing with the operating system
1945
+ */
1946
+ const taskHelp = async (flags, logger, sys) => {
1659
1947
  const prompt = logger.dim(sys.details.platform === 'windows' ? '>' : '$');
1660
1948
  console.log(`
1661
1949
  ${logger.bold('Build:')} ${logger.dim('Build components for development or production.')}
@@ -1688,7 +1976,7 @@ const taskHelp = async (config, logger, sys) => {
1688
1976
  `);
1689
1977
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1690
1978
  if (sys) {
1691
- await taskTelemetry(config, sys, logger);
1979
+ await taskTelemetry(flags, sys, logger);
1692
1980
  }
1693
1981
  console.log(`
1694
1982
  ${logger.bold('Examples:')}
@@ -1745,6 +2033,95 @@ const taskServe = async (config) => {
1745
2033
  });
1746
2034
  };
1747
2035
 
2036
+ /**
2037
+ * Creates an instance of a logger
2038
+ * @returns the new logger instance
2039
+ */
2040
+ const createLogger = () => {
2041
+ let useColors = IS_BROWSER_ENV;
2042
+ let level = 'info';
2043
+ return {
2044
+ enableColors: (uc) => (useColors = uc),
2045
+ getLevel: () => level,
2046
+ setLevel: (l) => (level = l),
2047
+ emoji: (e) => e,
2048
+ info: console.log.bind(console),
2049
+ warn: console.warn.bind(console),
2050
+ error: console.error.bind(console),
2051
+ debug: console.debug.bind(console),
2052
+ red: (msg) => msg,
2053
+ green: (msg) => msg,
2054
+ yellow: (msg) => msg,
2055
+ blue: (msg) => msg,
2056
+ magenta: (msg) => msg,
2057
+ cyan: (msg) => msg,
2058
+ gray: (msg) => msg,
2059
+ bold: (msg) => msg,
2060
+ dim: (msg) => msg,
2061
+ bgRed: (msg) => msg,
2062
+ createTimeSpan: (_startMsg, _debug = false) => ({
2063
+ duration: () => 0,
2064
+ finish: () => 0,
2065
+ }),
2066
+ printDiagnostics(diagnostics) {
2067
+ diagnostics.forEach((diagnostic) => logDiagnostic(diagnostic, useColors));
2068
+ },
2069
+ };
2070
+ };
2071
+ const logDiagnostic = (diagnostic, useColors) => {
2072
+ let color = BLUE;
2073
+ let prefix = 'Build';
2074
+ let msg = '';
2075
+ if (diagnostic.level === 'error') {
2076
+ color = RED;
2077
+ prefix = 'Error';
2078
+ }
2079
+ else if (diagnostic.level === 'warn') {
2080
+ color = YELLOW;
2081
+ prefix = 'Warning';
2082
+ }
2083
+ if (diagnostic.header) {
2084
+ prefix = diagnostic.header;
2085
+ }
2086
+ const filePath = diagnostic.relFilePath || diagnostic.absFilePath;
2087
+ if (filePath) {
2088
+ msg += filePath;
2089
+ if (typeof diagnostic.lineNumber === 'number' && diagnostic.lineNumber > 0) {
2090
+ msg += ', line ' + diagnostic.lineNumber;
2091
+ if (typeof diagnostic.columnNumber === 'number' && diagnostic.columnNumber > 0) {
2092
+ msg += ', column ' + diagnostic.columnNumber;
2093
+ }
2094
+ }
2095
+ msg += '\n';
2096
+ }
2097
+ msg += diagnostic.messageText;
2098
+ if (diagnostic.lines && diagnostic.lines.length > 0) {
2099
+ diagnostic.lines.forEach((l) => {
2100
+ msg += '\n' + l.lineNumber + ': ' + l.text;
2101
+ });
2102
+ msg += '\n';
2103
+ }
2104
+ if (useColors) {
2105
+ const styledPrefix = [
2106
+ '%c' + prefix,
2107
+ `background: ${color}; color: white; padding: 2px 3px; border-radius: 2px; font-size: 0.8em;`,
2108
+ ];
2109
+ console.log(...styledPrefix, msg);
2110
+ }
2111
+ else if (diagnostic.level === 'error') {
2112
+ console.error(msg);
2113
+ }
2114
+ else if (diagnostic.level === 'warn') {
2115
+ console.warn(msg);
2116
+ }
2117
+ else {
2118
+ console.log(msg);
2119
+ }
2120
+ };
2121
+ const YELLOW = `#f39c12`;
2122
+ const RED = `#c0392b`;
2123
+ const BLUE = `#3498db`;
2124
+
1748
2125
  const run = async (init) => {
1749
2126
  const { args, logger, sys } = init;
1750
2127
  try {
@@ -1760,7 +2137,7 @@ const run = async (init) => {
1760
2137
  sys.applyGlobalPatch(sys.getCurrentDirectory());
1761
2138
  }
1762
2139
  if (task === 'help' || flags.help) {
1763
- await taskHelp({ flags: { task: 'help', args }, outputTargets: [] }, logger, sys);
2140
+ await taskHelp({ task: 'help', args }, logger, sys);
1764
2141
  return;
1765
2142
  }
1766
2143
  startupLog(logger, task);
@@ -1786,7 +2163,7 @@ const run = async (init) => {
1786
2163
  startupLogVersion(logger, task, coreCompiler);
1787
2164
  loadedCompilerLog(sys, logger, flags, coreCompiler);
1788
2165
  if (task === 'info') {
1789
- await telemetryAction(sys, { flags: { task: 'info' }, outputTargets: [] }, logger, coreCompiler, async () => {
2166
+ await telemetryAction(sys, { flags: { task: 'info' }, logger }, coreCompiler, async () => {
1790
2167
  await taskInfo(coreCompiler, sys, logger);
1791
2168
  });
1792
2169
  return;
@@ -1809,7 +2186,7 @@ const run = async (init) => {
1809
2186
  sys.applyGlobalPatch(validated.config.rootDir);
1810
2187
  }
1811
2188
  await sys.ensureResources({ rootDir: validated.config.rootDir, logger, dependencies: dependencies });
1812
- await telemetryAction(sys, validated.config, logger, coreCompiler, async () => {
2189
+ await telemetryAction(sys, validated.config, coreCompiler, async () => {
1813
2190
  await runTask(coreCompiler, validated.config, task, sys);
1814
2191
  });
1815
2192
  }
@@ -1822,40 +2199,42 @@ const run = async (init) => {
1822
2199
  }
1823
2200
  };
1824
2201
  const runTask = async (coreCompiler, config, task, sys) => {
1825
- config.flags = config.flags || { task };
1826
- config.outputTargets = config.outputTargets || [];
2202
+ var _a, _b;
2203
+ const logger = (_a = config.logger) !== null && _a !== void 0 ? _a : createLogger();
2204
+ const strictConfig = { ...config, flags: (_b = { ...config.flags }) !== null && _b !== void 0 ? _b : { task }, logger };
2205
+ strictConfig.outputTargets = strictConfig.outputTargets || [];
1827
2206
  switch (task) {
1828
2207
  case 'build':
1829
- await taskBuild(coreCompiler, config, sys);
2208
+ await taskBuild(coreCompiler, strictConfig, sys);
1830
2209
  break;
1831
2210
  case 'docs':
1832
- await taskDocs(coreCompiler, config);
2211
+ await taskDocs(coreCompiler, strictConfig);
1833
2212
  break;
1834
2213
  case 'generate':
1835
2214
  case 'g':
1836
- await taskGenerate(coreCompiler, config);
2215
+ await taskGenerate(coreCompiler, strictConfig);
1837
2216
  break;
1838
2217
  case 'help':
1839
- await taskHelp(config, config.logger, sys);
2218
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1840
2219
  break;
1841
2220
  case 'prerender':
1842
- await taskPrerender(coreCompiler, config);
2221
+ await taskPrerender(coreCompiler, strictConfig);
1843
2222
  break;
1844
2223
  case 'serve':
1845
- await taskServe(config);
2224
+ await taskServe(strictConfig);
1846
2225
  break;
1847
2226
  case 'telemetry':
1848
2227
  // TODO: make this parameter no longer optional, remove the surrounding if statement
1849
2228
  if (sys) {
1850
- await taskTelemetry(config, sys, config.logger);
2229
+ await taskTelemetry(strictConfig.flags, sys, strictConfig.logger);
1851
2230
  }
1852
2231
  break;
1853
2232
  case 'version':
1854
2233
  console.log(coreCompiler.version);
1855
2234
  break;
1856
2235
  default:
1857
- config.logger.error(`${config.logger.emoji('❌ ')}Invalid rindo command, please see the options below:`);
1858
- await taskHelp(config, config.logger, sys);
2236
+ strictConfig.logger.error(`${strictConfig.logger.emoji('❌ ')}Invalid rindo command, please see the options below:`);
2237
+ await taskHelp(strictConfig.flags, strictConfig.logger, sys);
1859
2238
  return config.sys.exit(1);
1860
2239
  }
1861
2240
  };