@eslint-config-snapshot/cli 0.3.2 → 0.5.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @eslint-config-snapshot/cli
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add ESLint runtime version reporting by group in CLI summaries and improve pnpm/corepack isolated test resilience.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @eslint-config-snapshot/api@0.5.0
13
+
14
+ ## 0.4.0
15
+
16
+ ### Minor Changes
17
+
18
+ - Minor release with improved CLI output consistency and faster workspace extraction using ESLint API fallback strategy.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+ - @eslint-config-snapshot/api@0.4.0
24
+
3
25
  ## 0.3.2
4
26
 
5
27
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -39,39 +39,53 @@ module.exports = __toCommonJS(index_exports);
39
39
  var import_api = require("@eslint-config-snapshot/api");
40
40
  var import_commander = require("commander");
41
41
  var import_fast_glob = __toESM(require("fast-glob"), 1);
42
+ var import_node_fs = require("fs");
42
43
  var import_promises = require("fs/promises");
43
44
  var import_node_path = __toESM(require("path"), 1);
44
45
  var import_node_readline = require("readline");
45
46
  var SNAPSHOT_DIR = ".eslint-config-snapshot";
46
47
  var UPDATE_HINT = "Tip: when you intentionally accept changes, run `eslint-config-snapshot --update` to refresh the baseline.\n";
48
+ var activeRunTimer;
49
+ var cachedCliVersion;
47
50
  async function runCli(command, cwd, flags = []) {
48
51
  const argv = command ? [command, ...flags] : [...flags];
49
52
  return runArgv(argv, cwd);
50
53
  }
51
54
  async function runArgv(argv, cwd) {
52
- const hasCommandToken = argv.some((token) => !token.startsWith("-"));
53
- if (!hasCommandToken) {
54
- return runDefaultInvocation(argv, cwd);
55
- }
56
- let actionCode;
57
- const program = createProgram(cwd, (code) => {
58
- actionCode = code;
59
- });
55
+ const invocationLabel = resolveInvocationLabel(argv);
56
+ beginRunTimer(invocationLabel);
57
+ let exitCode = 1;
60
58
  try {
61
- await program.parseAsync(argv, { from: "user" });
62
- } catch (error) {
63
- if (error instanceof import_commander.CommanderError) {
64
- if (error.code === "commander.helpDisplayed") {
65
- return 0;
66
- }
67
- return error.exitCode;
59
+ const hasCommandToken = argv.some((token) => !token.startsWith("-"));
60
+ if (!hasCommandToken) {
61
+ exitCode = await runDefaultInvocation(argv, cwd);
62
+ return exitCode;
68
63
  }
69
- const message = error instanceof Error ? error.message : String(error);
70
- process.stderr.write(`${message}
64
+ let actionCode;
65
+ const program = createProgram(cwd, (code) => {
66
+ actionCode = code;
67
+ });
68
+ try {
69
+ await program.parseAsync(argv, { from: "user" });
70
+ } catch (error) {
71
+ if (error instanceof import_commander.CommanderError) {
72
+ if (error.code === "commander.helpDisplayed") {
73
+ exitCode = 0;
74
+ return exitCode;
75
+ }
76
+ exitCode = error.exitCode;
77
+ return exitCode;
78
+ }
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ process.stderr.write(`${message}
71
81
  `);
72
- return 1;
82
+ return 1;
83
+ }
84
+ exitCode = actionCode ?? 0;
85
+ return exitCode;
86
+ } finally {
87
+ endRunTimer(exitCode);
73
88
  }
74
- return actionCode ?? 0;
75
89
  }
76
90
  async function runDefaultInvocation(argv, cwd) {
77
91
  const known = /* @__PURE__ */ new Set(["-u", "--update", "-h", "--help"]);
@@ -177,6 +191,13 @@ function parseInitPreset(value) {
177
191
  }
178
192
  async function executeCheck(cwd, format, defaultInvocation = false) {
179
193
  const foundConfig = await (0, import_api.findConfigPath)(cwd);
194
+ const storedSnapshots = await loadStoredSnapshots(cwd);
195
+ if (format !== "status") {
196
+ writeRunContextHeader(cwd, defaultInvocation ? "check" : `check:${format}`, foundConfig?.path, storedSnapshots);
197
+ if (shouldShowRunLogs()) {
198
+ writeSubtleInfo("Analyzing current ESLint configuration...\n");
199
+ }
200
+ }
180
201
  if (!foundConfig) {
181
202
  writeSubtleInfo(
182
203
  "Tip: no explicit config found. Using safe built-in defaults. Run `eslint-config-snapshot init` to customize when needed.\n"
@@ -194,7 +215,6 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
194
215
  }
195
216
  throw error;
196
217
  }
197
- const storedSnapshots = await loadStoredSnapshots(cwd);
198
218
  if (storedSnapshots.size === 0) {
199
219
  const summary = summarizeSnapshots(currentSnapshots);
200
220
  process.stdout.write(
@@ -221,6 +241,7 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
221
241
  return 1;
222
242
  }
223
243
  const changes = compareSnapshotMaps(storedSnapshots, currentSnapshots);
244
+ const eslintVersionsByGroup = shouldShowRunLogs() ? await resolveGroupEslintVersions(cwd) : /* @__PURE__ */ new Map();
224
245
  if (format === "status") {
225
246
  if (changes.length === 0) {
226
247
  process.stdout.write("clean\n");
@@ -233,6 +254,7 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
233
254
  if (format === "diff") {
234
255
  if (changes.length === 0) {
235
256
  process.stdout.write("Great news: no snapshot changes detected.\n");
257
+ writeEslintVersionSummary(eslintVersionsByGroup);
236
258
  return 0;
237
259
  }
238
260
  for (const change of changes) {
@@ -242,10 +264,15 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
242
264
  writeSubtleInfo(UPDATE_HINT);
243
265
  return 1;
244
266
  }
245
- return printWhatChanged(changes, currentSnapshots);
267
+ return printWhatChanged(changes, currentSnapshots, eslintVersionsByGroup);
246
268
  }
247
269
  async function executeUpdate(cwd, printSummary) {
248
270
  const foundConfig = await (0, import_api.findConfigPath)(cwd);
271
+ const storedSnapshots = await loadStoredSnapshots(cwd);
272
+ writeRunContextHeader(cwd, "update", foundConfig?.path, storedSnapshots);
273
+ if (shouldShowRunLogs()) {
274
+ writeSubtleInfo("Analyzing current ESLint configuration...\n");
275
+ }
249
276
  if (!foundConfig) {
250
277
  writeSubtleInfo(
251
278
  "Tip: no explicit config found. Using safe built-in defaults. Run `eslint-config-snapshot init` to customize when needed.\n"
@@ -266,12 +293,25 @@ async function executeUpdate(cwd, printSummary) {
266
293
  await writeSnapshots(cwd, currentSnapshots);
267
294
  if (printSummary) {
268
295
  const summary = summarizeSnapshots(currentSnapshots);
269
- process.stdout.write(`Baseline updated: ${summary.groups} groups, ${summary.rules} rules.
270
- `);
296
+ const color = createColorizer();
297
+ const eslintVersionsByGroup = shouldShowRunLogs() ? await resolveGroupEslintVersions(cwd) : /* @__PURE__ */ new Map();
298
+ writeSectionTitle("Summary", color);
299
+ process.stdout.write(
300
+ `Baseline updated: ${summary.groups} groups, ${summary.rules} rules.
301
+ Severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off.
302
+ `
303
+ );
304
+ writeEslintVersionSummary(eslintVersionsByGroup);
271
305
  }
272
306
  return 0;
273
307
  }
274
308
  async function executePrint(cwd, format) {
309
+ const foundConfig = await (0, import_api.findConfigPath)(cwd);
310
+ const storedSnapshots = await loadStoredSnapshots(cwd);
311
+ writeRunContextHeader(cwd, `print:${format}`, foundConfig?.path, storedSnapshots);
312
+ if (shouldShowRunLogs()) {
313
+ writeSubtleInfo("Analyzing current ESLint configuration...\n");
314
+ }
275
315
  const currentSnapshots = await computeCurrentSnapshots(cwd);
276
316
  if (format === "short") {
277
317
  process.stdout.write(formatShortPrint([...currentSnapshots.values()]));
@@ -286,6 +326,11 @@ async function executePrint(cwd, format) {
286
326
  }
287
327
  async function executeConfig(cwd, format) {
288
328
  const foundConfig = await (0, import_api.findConfigPath)(cwd);
329
+ const storedSnapshots = await loadStoredSnapshots(cwd);
330
+ writeRunContextHeader(cwd, `config:${format}`, foundConfig?.path, storedSnapshots);
331
+ if (shouldShowRunLogs()) {
332
+ writeSubtleInfo("Resolving effective runtime configuration...\n");
333
+ }
289
334
  const config = await (0, import_api.loadConfig)(cwd);
290
335
  const resolved = await resolveWorkspaceAssignments(cwd, config);
291
336
  const payload = {
@@ -317,19 +362,20 @@ async function computeCurrentSnapshots(cwd) {
317
362
  const sampled = await (0, import_api.sampleWorkspaceFiles)(workspaceAbs, config.sampling);
318
363
  let extractedCount = 0;
319
364
  let lastExtractionError;
320
- for (const sampledRel of sampled) {
321
- const sampledAbs = import_node_path.default.resolve(workspaceAbs, sampledRel);
322
- try {
323
- extractedForGroup.push((0, import_api.extractRulesFromPrintConfig)(workspaceAbs, sampledAbs));
365
+ const sampledAbs = sampled.map((sampledRel) => import_node_path.default.resolve(workspaceAbs, sampledRel));
366
+ const results = await (0, import_api.extractRulesForWorkspaceSamples)(workspaceAbs, sampledAbs);
367
+ for (const result of results) {
368
+ if (result.rules) {
369
+ extractedForGroup.push(result.rules);
324
370
  extractedCount += 1;
325
- } catch (error) {
326
- const message = error instanceof Error ? error.message : String(error);
327
- if (message.startsWith("Invalid JSON from eslint --print-config") || message.startsWith("Empty ESLint print-config output")) {
328
- lastExtractionError = message;
329
- continue;
330
- }
331
- throw error;
371
+ continue;
372
+ }
373
+ const message = result.error instanceof Error ? result.error.message : String(result.error);
374
+ if (isRecoverableExtractionError(message)) {
375
+ lastExtractionError = message;
376
+ continue;
332
377
  }
378
+ throw result.error ?? new Error(message);
333
379
  }
334
380
  if (extractedCount === 0) {
335
381
  const context = lastExtractionError ? ` Last error: ${lastExtractionError}` : "";
@@ -343,6 +389,9 @@ async function computeCurrentSnapshots(cwd) {
343
389
  }
344
390
  return snapshots;
345
391
  }
392
+ function isRecoverableExtractionError(message) {
393
+ return message.startsWith("Invalid JSON from eslint --print-config") || message.startsWith("Empty ESLint print-config output") || message.includes("File ignored because of a matching ignore pattern") || message.includes("File ignored by default");
394
+ }
346
395
  async function resolveWorkspaceAssignments(cwd, config) {
347
396
  const discovery = await (0, import_api.discoverWorkspaces)({ cwd, workspaceInput: config.workspaceInput });
348
397
  const assignments = config.grouping.mode === "standalone" ? discovery.workspacesRel.map((workspace) => ({ name: workspace, workspaces: [workspace] })) : (0, import_api.assignGroupsByMatch)(discovery.workspacesRel, config.grouping.groups ?? [{ name: "default", match: ["**/*"] }]);
@@ -467,8 +516,8 @@ ${JSON.stringify(configObject, null, 2)}
467
516
  }
468
517
  async function askInitPreferences() {
469
518
  const { select } = await import("@inquirer/prompts");
470
- const target = await askInitTarget(select);
471
- const preset = await askInitPreset(select);
519
+ const target = await runPromptWithPausedTimer(() => askInitTarget(select));
520
+ const preset = await runPromptWithPausedTimer(() => askInitPreset(select));
472
521
  return { target, preset };
473
522
  }
474
523
  async function askInitTarget(selectPrompt) {
@@ -491,8 +540,10 @@ async function askInitPreset(selectPrompt) {
491
540
  });
492
541
  }
493
542
  function askQuestion(rl, prompt) {
543
+ pauseRunTimer();
494
544
  return new Promise((resolve) => {
495
545
  rl.question(prompt, (answer) => {
546
+ resumeRunTimer();
496
547
  resolve(answer);
497
548
  });
498
549
  });
@@ -638,11 +689,13 @@ async function askRecommendedGroupAssignments(workspaces) {
638
689
  'Recommended setup: default group "*" is a dynamic catch-all for every discovered workspace.\n'
639
690
  );
640
691
  process.stdout.write("Select only workspaces that should move to explicit static groups.\n");
641
- const overrides = await checkbox({
642
- message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
643
- choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
644
- pageSize: Math.min(12, Math.max(4, workspaces.length))
645
- });
692
+ const overrides = await runPromptWithPausedTimer(
693
+ () => checkbox({
694
+ message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
695
+ choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
696
+ pageSize: Math.min(12, Math.max(4, workspaces.length))
697
+ })
698
+ );
646
699
  const assignments = /* @__PURE__ */ new Map();
647
700
  let nextGroup = 1;
648
701
  for (const workspace of overrides) {
@@ -650,13 +703,15 @@ async function askRecommendedGroupAssignments(workspaces) {
650
703
  while (usedGroups.includes(nextGroup)) {
651
704
  nextGroup += 1;
652
705
  }
653
- const selected = await select({
654
- message: `Select group for ${workspace}`,
655
- choices: [
656
- ...usedGroups.map((group) => ({ name: `group-${group}`, value: group })),
657
- { name: `create new group (group-${nextGroup})`, value: "new" }
658
- ]
659
- });
706
+ const selected = await runPromptWithPausedTimer(
707
+ () => select({
708
+ message: `Select group for ${workspace}`,
709
+ choices: [
710
+ ...usedGroups.map((group) => ({ name: `group-${group}`, value: group })),
711
+ { name: `create new group (group-${nextGroup})`, value: "new" }
712
+ ]
713
+ })
714
+ );
660
715
  const groupNumber = selected === "new" ? nextGroup : selected;
661
716
  assignments.set(workspace, groupNumber);
662
717
  }
@@ -699,28 +754,38 @@ function isDirectCliExecution() {
699
754
  if (isDirectCliExecution()) {
700
755
  void main();
701
756
  }
702
- function printWhatChanged(changes, currentSnapshots) {
757
+ function printWhatChanged(changes, currentSnapshots, eslintVersionsByGroup) {
703
758
  const color = createColorizer();
704
759
  const currentSummary = summarizeSnapshots(currentSnapshots);
705
760
  const changeSummary = summarizeChanges(changes);
706
761
  if (changes.length === 0) {
707
762
  process.stdout.write(color.green("Great news: no snapshot drift detected.\n"));
763
+ writeSectionTitle("Summary", color);
708
764
  process.stdout.write(
709
- `Baseline status: ${currentSummary.groups} groups, ${currentSummary.rules} rules (severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off).
765
+ `- baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
766
+ - severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
710
767
  `
711
768
  );
769
+ writeEslintVersionSummary(eslintVersionsByGroup);
712
770
  return 0;
713
771
  }
714
772
  process.stdout.write(color.red("Heads up: snapshot drift detected.\n"));
773
+ writeSectionTitle("Summary", color);
715
774
  process.stdout.write(
716
- `Changed groups: ${changes.length} | introduced: ${changeSummary.introduced} | removed: ${changeSummary.removed} | severity: ${changeSummary.severity} | options: ${changeSummary.options} | workspace membership: ${changeSummary.workspace}
717
- `
718
- );
719
- process.stdout.write(
720
- `Current rules: ${currentSummary.rules} (severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off)
775
+ `- changed groups: ${changes.length}
776
+ - introduced rules: ${changeSummary.introduced}
777
+ - removed rules: ${changeSummary.removed}
778
+ - severity changes: ${changeSummary.severity}
779
+ - options changes: ${changeSummary.options}
780
+ - workspace membership changes: ${changeSummary.workspace}
781
+ - current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
782
+ - current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
721
783
 
722
784
  `
723
785
  );
786
+ writeEslintVersionSummary(eslintVersionsByGroup);
787
+ process.stdout.write("\n");
788
+ writeSectionTitle("Changes", color);
724
789
  for (const change of changes) {
725
790
  process.stdout.write(color.bold(`group ${change.groupId}
726
791
  `));
@@ -735,6 +800,10 @@ function printWhatChanged(changes, currentSnapshots) {
735
800
  writeSubtleInfo(UPDATE_HINT);
736
801
  return 1;
737
802
  }
803
+ function writeSectionTitle(title, color) {
804
+ process.stdout.write(`${color.bold(title)}
805
+ `);
806
+ }
738
807
  function summarizeChanges(changes) {
739
808
  let introduced = 0;
740
809
  let removed = 0;
@@ -751,22 +820,7 @@ function summarizeChanges(changes) {
751
820
  return { introduced, removed, severity, options, workspace };
752
821
  }
753
822
  function summarizeSnapshots(snapshots) {
754
- let rules = 0;
755
- let error = 0;
756
- let warn = 0;
757
- let off = 0;
758
- for (const snapshot of snapshots.values()) {
759
- for (const entry of Object.values(snapshot.rules)) {
760
- rules += 1;
761
- if (entry[0] === "error") {
762
- error += 1;
763
- } else if (entry[0] === "warn") {
764
- warn += 1;
765
- } else {
766
- off += 1;
767
- }
768
- }
769
- }
823
+ const { rules, error, warn, off } = countRuleSeverities([...snapshots.values()].map((snapshot) => snapshot.rules));
770
824
  return { groups: snapshots.size, rules, error, warn, off };
771
825
  }
772
826
  function decorateDiffLine(line, color) {
@@ -796,6 +850,199 @@ function writeSubtleInfo(text) {
796
850
  const color = createColorizer();
797
851
  process.stdout.write(color.dim(text));
798
852
  }
853
+ function resolveInvocationLabel(argv) {
854
+ const commandToken = argv.find((entry) => !entry.startsWith("-"));
855
+ if (commandToken) {
856
+ return commandToken;
857
+ }
858
+ if (argv.includes("-u") || argv.includes("--update")) {
859
+ return "update";
860
+ }
861
+ if (argv.includes("-h") || argv.includes("--help")) {
862
+ return "help";
863
+ }
864
+ return "check";
865
+ }
866
+ function shouldShowRunLogs() {
867
+ if (process.env.ESLINT_CONFIG_SNAPSHOT_NO_PROGRESS === "1") {
868
+ return false;
869
+ }
870
+ return process.stdout.isTTY === true;
871
+ }
872
+ function beginRunTimer(label) {
873
+ if (!shouldShowRunLogs()) {
874
+ activeRunTimer = void 0;
875
+ return;
876
+ }
877
+ activeRunTimer = {
878
+ label,
879
+ startedAtMs: Date.now(),
880
+ pausedMs: 0,
881
+ pauseStartedAtMs: void 0
882
+ };
883
+ }
884
+ function endRunTimer(exitCode) {
885
+ if (!activeRunTimer || !shouldShowRunLogs()) {
886
+ return;
887
+ }
888
+ if (activeRunTimer.pauseStartedAtMs !== void 0) {
889
+ activeRunTimer.pausedMs += Date.now() - activeRunTimer.pauseStartedAtMs;
890
+ activeRunTimer.pauseStartedAtMs = void 0;
891
+ }
892
+ const elapsedMs = Math.max(0, Date.now() - activeRunTimer.startedAtMs - activeRunTimer.pausedMs);
893
+ const color = createColorizer();
894
+ const status = exitCode === 0 ? color.green("done") : color.red("failed");
895
+ const seconds = (elapsedMs / 1e3).toFixed(2);
896
+ writeSubtleInfo(`Run ${status} in ${seconds}s
897
+ `);
898
+ activeRunTimer = void 0;
899
+ }
900
+ function pauseRunTimer() {
901
+ if (!activeRunTimer || activeRunTimer.pauseStartedAtMs !== void 0) {
902
+ return;
903
+ }
904
+ activeRunTimer.pauseStartedAtMs = Date.now();
905
+ }
906
+ function resumeRunTimer() {
907
+ if (!activeRunTimer || activeRunTimer.pauseStartedAtMs === void 0) {
908
+ return;
909
+ }
910
+ activeRunTimer.pausedMs += Date.now() - activeRunTimer.pauseStartedAtMs;
911
+ activeRunTimer.pauseStartedAtMs = void 0;
912
+ }
913
+ async function runPromptWithPausedTimer(prompt) {
914
+ pauseRunTimer();
915
+ try {
916
+ return await prompt();
917
+ } finally {
918
+ resumeRunTimer();
919
+ }
920
+ }
921
+ function readCliVersion() {
922
+ if (cachedCliVersion !== void 0) {
923
+ return cachedCliVersion;
924
+ }
925
+ const scriptPath = process.argv[1];
926
+ if (!scriptPath) {
927
+ cachedCliVersion = "unknown";
928
+ return cachedCliVersion;
929
+ }
930
+ let current = import_node_path.default.resolve(import_node_path.default.dirname(scriptPath));
931
+ while (true) {
932
+ const packageJsonPath = import_node_path.default.join(current, "package.json");
933
+ if ((0, import_node_fs.existsSync)(packageJsonPath)) {
934
+ try {
935
+ const raw = (0, import_node_fs.readFileSync)(packageJsonPath, "utf8");
936
+ const parsed = JSON.parse(raw);
937
+ cachedCliVersion = parsed.version ?? "unknown";
938
+ return cachedCliVersion;
939
+ } catch {
940
+ break;
941
+ }
942
+ }
943
+ const parent = import_node_path.default.dirname(current);
944
+ if (parent === current) {
945
+ break;
946
+ }
947
+ current = parent;
948
+ }
949
+ cachedCliVersion = "unknown";
950
+ return cachedCliVersion;
951
+ }
952
+ function writeRunContextHeader(cwd, commandLabel, configPath, storedSnapshots) {
953
+ if (!shouldShowRunLogs()) {
954
+ return;
955
+ }
956
+ const color = createColorizer();
957
+ process.stdout.write(color.bold(`eslint-config-snapshot v${readCliVersion()}
958
+ `));
959
+ process.stdout.write(`Command: ${commandLabel}
960
+ `);
961
+ process.stdout.write(`Repository: ${cwd}
962
+ `);
963
+ process.stdout.write(`Config: ${formatConfigSource(cwd, configPath)}
964
+ `);
965
+ process.stdout.write(`Baseline: ${formatStoredSnapshotSummary(storedSnapshots)}
966
+
967
+ `);
968
+ }
969
+ function formatConfigSource(cwd, configPath) {
970
+ if (!configPath) {
971
+ return "built-in defaults";
972
+ }
973
+ const rel = (0, import_api.normalizePath)(import_node_path.default.relative(cwd, configPath));
974
+ if (import_node_path.default.basename(configPath) === "package.json") {
975
+ return `${rel} (eslint-config-snapshot field)`;
976
+ }
977
+ return rel;
978
+ }
979
+ function formatStoredSnapshotSummary(storedSnapshots) {
980
+ if (storedSnapshots.size === 0) {
981
+ return "none";
982
+ }
983
+ const summary = summarizeStoredSnapshots(storedSnapshots);
984
+ return `${summary.groups} groups, ${summary.rules} rules (severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off)`;
985
+ }
986
+ async function resolveGroupEslintVersions(cwd) {
987
+ const config = await (0, import_api.loadConfig)(cwd);
988
+ const { discovery, assignments } = await resolveWorkspaceAssignments(cwd, config);
989
+ const result = /* @__PURE__ */ new Map();
990
+ for (const group of assignments) {
991
+ const versions = /* @__PURE__ */ new Set();
992
+ for (const workspaceRel of group.workspaces) {
993
+ const workspaceAbs = import_node_path.default.resolve(discovery.rootAbs, workspaceRel);
994
+ versions.add((0, import_api.resolveEslintVersionForWorkspace)(workspaceAbs));
995
+ }
996
+ result.set(group.name, [...versions].sort((a, b) => a.localeCompare(b)));
997
+ }
998
+ return result;
999
+ }
1000
+ function writeEslintVersionSummary(eslintVersionsByGroup) {
1001
+ if (!shouldShowRunLogs() || eslintVersionsByGroup.size === 0) {
1002
+ return;
1003
+ }
1004
+ const allVersions = /* @__PURE__ */ new Set();
1005
+ for (const versions of eslintVersionsByGroup.values()) {
1006
+ for (const version of versions) {
1007
+ allVersions.add(version);
1008
+ }
1009
+ }
1010
+ const sortedAllVersions = [...allVersions].sort((a, b) => a.localeCompare(b));
1011
+ if (sortedAllVersions.length === 1) {
1012
+ process.stdout.write(`- eslint runtime: ${sortedAllVersions[0]} (all groups)
1013
+ `);
1014
+ return;
1015
+ }
1016
+ process.stdout.write("- eslint runtime by group:\n");
1017
+ const sortedEntries = [...eslintVersionsByGroup.entries()].sort((a, b) => a[0].localeCompare(b[0]));
1018
+ for (const [groupName, versions] of sortedEntries) {
1019
+ process.stdout.write(` - ${groupName}: ${versions.join(", ")}
1020
+ `);
1021
+ }
1022
+ }
1023
+ function summarizeStoredSnapshots(snapshots) {
1024
+ const { rules, error, warn, off } = countRuleSeverities([...snapshots.values()].map((snapshot) => snapshot.rules));
1025
+ return { groups: snapshots.size, rules, error, warn, off };
1026
+ }
1027
+ function countRuleSeverities(ruleObjects) {
1028
+ let rules = 0;
1029
+ let error = 0;
1030
+ let warn = 0;
1031
+ let off = 0;
1032
+ for (const rulesObject of ruleObjects) {
1033
+ for (const entry of Object.values(rulesObject)) {
1034
+ rules += 1;
1035
+ if (entry[0] === "error") {
1036
+ error += 1;
1037
+ } else if (entry[0] === "warn") {
1038
+ warn += 1;
1039
+ } else {
1040
+ off += 1;
1041
+ }
1042
+ }
1043
+ }
1044
+ return { rules, error, warn, off };
1045
+ }
799
1046
  function formatShortPrint(snapshots) {
800
1047
  const lines = [];
801
1048
  const sorted = [...snapshots].sort((a, b) => a.groupId.localeCompare(b.groupId));