@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/dist/index.js CHANGED
@@ -7,51 +7,66 @@ import {
7
7
  buildSnapshot,
8
8
  diffSnapshots,
9
9
  discoverWorkspaces,
10
- extractRulesFromPrintConfig,
10
+ extractRulesForWorkspaceSamples,
11
11
  findConfigPath,
12
12
  getConfigScaffold,
13
13
  hasDiff,
14
14
  loadConfig,
15
15
  normalizePath,
16
16
  readSnapshotFile,
17
+ resolveEslintVersionForWorkspace,
17
18
  sampleWorkspaceFiles,
18
19
  writeSnapshotFile
19
20
  } from "@eslint-config-snapshot/api";
20
21
  import { Command, CommanderError, InvalidArgumentError } from "commander";
21
22
  import fg from "fast-glob";
23
+ import { existsSync, readFileSync } from "fs";
22
24
  import { access, mkdir, readFile, writeFile } from "fs/promises";
23
25
  import path from "path";
24
26
  import { createInterface } from "readline";
25
27
  var SNAPSHOT_DIR = ".eslint-config-snapshot";
26
28
  var UPDATE_HINT = "Tip: when you intentionally accept changes, run `eslint-config-snapshot --update` to refresh the baseline.\n";
29
+ var activeRunTimer;
30
+ var cachedCliVersion;
27
31
  async function runCli(command, cwd, flags = []) {
28
32
  const argv = command ? [command, ...flags] : [...flags];
29
33
  return runArgv(argv, cwd);
30
34
  }
31
35
  async function runArgv(argv, cwd) {
32
- const hasCommandToken = argv.some((token) => !token.startsWith("-"));
33
- if (!hasCommandToken) {
34
- return runDefaultInvocation(argv, cwd);
35
- }
36
- let actionCode;
37
- const program = createProgram(cwd, (code) => {
38
- actionCode = code;
39
- });
36
+ const invocationLabel = resolveInvocationLabel(argv);
37
+ beginRunTimer(invocationLabel);
38
+ let exitCode = 1;
40
39
  try {
41
- await program.parseAsync(argv, { from: "user" });
42
- } catch (error) {
43
- if (error instanceof CommanderError) {
44
- if (error.code === "commander.helpDisplayed") {
45
- return 0;
46
- }
47
- return error.exitCode;
40
+ const hasCommandToken = argv.some((token) => !token.startsWith("-"));
41
+ if (!hasCommandToken) {
42
+ exitCode = await runDefaultInvocation(argv, cwd);
43
+ return exitCode;
48
44
  }
49
- const message = error instanceof Error ? error.message : String(error);
50
- process.stderr.write(`${message}
45
+ let actionCode;
46
+ const program = createProgram(cwd, (code) => {
47
+ actionCode = code;
48
+ });
49
+ try {
50
+ await program.parseAsync(argv, { from: "user" });
51
+ } catch (error) {
52
+ if (error instanceof CommanderError) {
53
+ if (error.code === "commander.helpDisplayed") {
54
+ exitCode = 0;
55
+ return exitCode;
56
+ }
57
+ exitCode = error.exitCode;
58
+ return exitCode;
59
+ }
60
+ const message = error instanceof Error ? error.message : String(error);
61
+ process.stderr.write(`${message}
51
62
  `);
52
- return 1;
63
+ return 1;
64
+ }
65
+ exitCode = actionCode ?? 0;
66
+ return exitCode;
67
+ } finally {
68
+ endRunTimer(exitCode);
53
69
  }
54
- return actionCode ?? 0;
55
70
  }
56
71
  async function runDefaultInvocation(argv, cwd) {
57
72
  const known = /* @__PURE__ */ new Set(["-u", "--update", "-h", "--help"]);
@@ -157,6 +172,13 @@ function parseInitPreset(value) {
157
172
  }
158
173
  async function executeCheck(cwd, format, defaultInvocation = false) {
159
174
  const foundConfig = await findConfigPath(cwd);
175
+ const storedSnapshots = await loadStoredSnapshots(cwd);
176
+ if (format !== "status") {
177
+ writeRunContextHeader(cwd, defaultInvocation ? "check" : `check:${format}`, foundConfig?.path, storedSnapshots);
178
+ if (shouldShowRunLogs()) {
179
+ writeSubtleInfo("Analyzing current ESLint configuration...\n");
180
+ }
181
+ }
160
182
  if (!foundConfig) {
161
183
  writeSubtleInfo(
162
184
  "Tip: no explicit config found. Using safe built-in defaults. Run `eslint-config-snapshot init` to customize when needed.\n"
@@ -174,7 +196,6 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
174
196
  }
175
197
  throw error;
176
198
  }
177
- const storedSnapshots = await loadStoredSnapshots(cwd);
178
199
  if (storedSnapshots.size === 0) {
179
200
  const summary = summarizeSnapshots(currentSnapshots);
180
201
  process.stdout.write(
@@ -201,6 +222,7 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
201
222
  return 1;
202
223
  }
203
224
  const changes = compareSnapshotMaps(storedSnapshots, currentSnapshots);
225
+ const eslintVersionsByGroup = shouldShowRunLogs() ? await resolveGroupEslintVersions(cwd) : /* @__PURE__ */ new Map();
204
226
  if (format === "status") {
205
227
  if (changes.length === 0) {
206
228
  process.stdout.write("clean\n");
@@ -213,6 +235,7 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
213
235
  if (format === "diff") {
214
236
  if (changes.length === 0) {
215
237
  process.stdout.write("Great news: no snapshot changes detected.\n");
238
+ writeEslintVersionSummary(eslintVersionsByGroup);
216
239
  return 0;
217
240
  }
218
241
  for (const change of changes) {
@@ -222,10 +245,15 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
222
245
  writeSubtleInfo(UPDATE_HINT);
223
246
  return 1;
224
247
  }
225
- return printWhatChanged(changes, currentSnapshots);
248
+ return printWhatChanged(changes, currentSnapshots, eslintVersionsByGroup);
226
249
  }
227
250
  async function executeUpdate(cwd, printSummary) {
228
251
  const foundConfig = await findConfigPath(cwd);
252
+ const storedSnapshots = await loadStoredSnapshots(cwd);
253
+ writeRunContextHeader(cwd, "update", foundConfig?.path, storedSnapshots);
254
+ if (shouldShowRunLogs()) {
255
+ writeSubtleInfo("Analyzing current ESLint configuration...\n");
256
+ }
229
257
  if (!foundConfig) {
230
258
  writeSubtleInfo(
231
259
  "Tip: no explicit config found. Using safe built-in defaults. Run `eslint-config-snapshot init` to customize when needed.\n"
@@ -246,12 +274,25 @@ async function executeUpdate(cwd, printSummary) {
246
274
  await writeSnapshots(cwd, currentSnapshots);
247
275
  if (printSummary) {
248
276
  const summary = summarizeSnapshots(currentSnapshots);
249
- process.stdout.write(`Baseline updated: ${summary.groups} groups, ${summary.rules} rules.
250
- `);
277
+ const color = createColorizer();
278
+ const eslintVersionsByGroup = shouldShowRunLogs() ? await resolveGroupEslintVersions(cwd) : /* @__PURE__ */ new Map();
279
+ writeSectionTitle("Summary", color);
280
+ process.stdout.write(
281
+ `Baseline updated: ${summary.groups} groups, ${summary.rules} rules.
282
+ Severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off.
283
+ `
284
+ );
285
+ writeEslintVersionSummary(eslintVersionsByGroup);
251
286
  }
252
287
  return 0;
253
288
  }
254
289
  async function executePrint(cwd, format) {
290
+ const foundConfig = await findConfigPath(cwd);
291
+ const storedSnapshots = await loadStoredSnapshots(cwd);
292
+ writeRunContextHeader(cwd, `print:${format}`, foundConfig?.path, storedSnapshots);
293
+ if (shouldShowRunLogs()) {
294
+ writeSubtleInfo("Analyzing current ESLint configuration...\n");
295
+ }
255
296
  const currentSnapshots = await computeCurrentSnapshots(cwd);
256
297
  if (format === "short") {
257
298
  process.stdout.write(formatShortPrint([...currentSnapshots.values()]));
@@ -266,6 +307,11 @@ async function executePrint(cwd, format) {
266
307
  }
267
308
  async function executeConfig(cwd, format) {
268
309
  const foundConfig = await findConfigPath(cwd);
310
+ const storedSnapshots = await loadStoredSnapshots(cwd);
311
+ writeRunContextHeader(cwd, `config:${format}`, foundConfig?.path, storedSnapshots);
312
+ if (shouldShowRunLogs()) {
313
+ writeSubtleInfo("Resolving effective runtime configuration...\n");
314
+ }
269
315
  const config = await loadConfig(cwd);
270
316
  const resolved = await resolveWorkspaceAssignments(cwd, config);
271
317
  const payload = {
@@ -297,19 +343,20 @@ async function computeCurrentSnapshots(cwd) {
297
343
  const sampled = await sampleWorkspaceFiles(workspaceAbs, config.sampling);
298
344
  let extractedCount = 0;
299
345
  let lastExtractionError;
300
- for (const sampledRel of sampled) {
301
- const sampledAbs = path.resolve(workspaceAbs, sampledRel);
302
- try {
303
- extractedForGroup.push(extractRulesFromPrintConfig(workspaceAbs, sampledAbs));
346
+ const sampledAbs = sampled.map((sampledRel) => path.resolve(workspaceAbs, sampledRel));
347
+ const results = await extractRulesForWorkspaceSamples(workspaceAbs, sampledAbs);
348
+ for (const result of results) {
349
+ if (result.rules) {
350
+ extractedForGroup.push(result.rules);
304
351
  extractedCount += 1;
305
- } catch (error) {
306
- const message = error instanceof Error ? error.message : String(error);
307
- if (message.startsWith("Invalid JSON from eslint --print-config") || message.startsWith("Empty ESLint print-config output")) {
308
- lastExtractionError = message;
309
- continue;
310
- }
311
- throw error;
352
+ continue;
353
+ }
354
+ const message = result.error instanceof Error ? result.error.message : String(result.error);
355
+ if (isRecoverableExtractionError(message)) {
356
+ lastExtractionError = message;
357
+ continue;
312
358
  }
359
+ throw result.error ?? new Error(message);
313
360
  }
314
361
  if (extractedCount === 0) {
315
362
  const context = lastExtractionError ? ` Last error: ${lastExtractionError}` : "";
@@ -323,6 +370,9 @@ async function computeCurrentSnapshots(cwd) {
323
370
  }
324
371
  return snapshots;
325
372
  }
373
+ function isRecoverableExtractionError(message) {
374
+ 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");
375
+ }
326
376
  async function resolveWorkspaceAssignments(cwd, config) {
327
377
  const discovery = await discoverWorkspaces({ cwd, workspaceInput: config.workspaceInput });
328
378
  const assignments = config.grouping.mode === "standalone" ? discovery.workspacesRel.map((workspace) => ({ name: workspace, workspaces: [workspace] })) : assignGroupsByMatch(discovery.workspacesRel, config.grouping.groups ?? [{ name: "default", match: ["**/*"] }]);
@@ -447,8 +497,8 @@ ${JSON.stringify(configObject, null, 2)}
447
497
  }
448
498
  async function askInitPreferences() {
449
499
  const { select } = await import("@inquirer/prompts");
450
- const target = await askInitTarget(select);
451
- const preset = await askInitPreset(select);
500
+ const target = await runPromptWithPausedTimer(() => askInitTarget(select));
501
+ const preset = await runPromptWithPausedTimer(() => askInitPreset(select));
452
502
  return { target, preset };
453
503
  }
454
504
  async function askInitTarget(selectPrompt) {
@@ -471,8 +521,10 @@ async function askInitPreset(selectPrompt) {
471
521
  });
472
522
  }
473
523
  function askQuestion(rl, prompt) {
524
+ pauseRunTimer();
474
525
  return new Promise((resolve) => {
475
526
  rl.question(prompt, (answer) => {
527
+ resumeRunTimer();
476
528
  resolve(answer);
477
529
  });
478
530
  });
@@ -618,11 +670,13 @@ async function askRecommendedGroupAssignments(workspaces) {
618
670
  'Recommended setup: default group "*" is a dynamic catch-all for every discovered workspace.\n'
619
671
  );
620
672
  process.stdout.write("Select only workspaces that should move to explicit static groups.\n");
621
- const overrides = await checkbox({
622
- message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
623
- choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
624
- pageSize: Math.min(12, Math.max(4, workspaces.length))
625
- });
673
+ const overrides = await runPromptWithPausedTimer(
674
+ () => checkbox({
675
+ message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
676
+ choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
677
+ pageSize: Math.min(12, Math.max(4, workspaces.length))
678
+ })
679
+ );
626
680
  const assignments = /* @__PURE__ */ new Map();
627
681
  let nextGroup = 1;
628
682
  for (const workspace of overrides) {
@@ -630,13 +684,15 @@ async function askRecommendedGroupAssignments(workspaces) {
630
684
  while (usedGroups.includes(nextGroup)) {
631
685
  nextGroup += 1;
632
686
  }
633
- const selected = await select({
634
- message: `Select group for ${workspace}`,
635
- choices: [
636
- ...usedGroups.map((group) => ({ name: `group-${group}`, value: group })),
637
- { name: `create new group (group-${nextGroup})`, value: "new" }
638
- ]
639
- });
687
+ const selected = await runPromptWithPausedTimer(
688
+ () => select({
689
+ message: `Select group for ${workspace}`,
690
+ choices: [
691
+ ...usedGroups.map((group) => ({ name: `group-${group}`, value: group })),
692
+ { name: `create new group (group-${nextGroup})`, value: "new" }
693
+ ]
694
+ })
695
+ );
640
696
  const groupNumber = selected === "new" ? nextGroup : selected;
641
697
  assignments.set(workspace, groupNumber);
642
698
  }
@@ -679,28 +735,38 @@ function isDirectCliExecution() {
679
735
  if (isDirectCliExecution()) {
680
736
  void main();
681
737
  }
682
- function printWhatChanged(changes, currentSnapshots) {
738
+ function printWhatChanged(changes, currentSnapshots, eslintVersionsByGroup) {
683
739
  const color = createColorizer();
684
740
  const currentSummary = summarizeSnapshots(currentSnapshots);
685
741
  const changeSummary = summarizeChanges(changes);
686
742
  if (changes.length === 0) {
687
743
  process.stdout.write(color.green("Great news: no snapshot drift detected.\n"));
744
+ writeSectionTitle("Summary", color);
688
745
  process.stdout.write(
689
- `Baseline status: ${currentSummary.groups} groups, ${currentSummary.rules} rules (severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off).
746
+ `- baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
747
+ - severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
690
748
  `
691
749
  );
750
+ writeEslintVersionSummary(eslintVersionsByGroup);
692
751
  return 0;
693
752
  }
694
753
  process.stdout.write(color.red("Heads up: snapshot drift detected.\n"));
754
+ writeSectionTitle("Summary", color);
695
755
  process.stdout.write(
696
- `Changed groups: ${changes.length} | introduced: ${changeSummary.introduced} | removed: ${changeSummary.removed} | severity: ${changeSummary.severity} | options: ${changeSummary.options} | workspace membership: ${changeSummary.workspace}
697
- `
698
- );
699
- process.stdout.write(
700
- `Current rules: ${currentSummary.rules} (severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off)
756
+ `- changed groups: ${changes.length}
757
+ - introduced rules: ${changeSummary.introduced}
758
+ - removed rules: ${changeSummary.removed}
759
+ - severity changes: ${changeSummary.severity}
760
+ - options changes: ${changeSummary.options}
761
+ - workspace membership changes: ${changeSummary.workspace}
762
+ - current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
763
+ - current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
701
764
 
702
765
  `
703
766
  );
767
+ writeEslintVersionSummary(eslintVersionsByGroup);
768
+ process.stdout.write("\n");
769
+ writeSectionTitle("Changes", color);
704
770
  for (const change of changes) {
705
771
  process.stdout.write(color.bold(`group ${change.groupId}
706
772
  `));
@@ -715,6 +781,10 @@ function printWhatChanged(changes, currentSnapshots) {
715
781
  writeSubtleInfo(UPDATE_HINT);
716
782
  return 1;
717
783
  }
784
+ function writeSectionTitle(title, color) {
785
+ process.stdout.write(`${color.bold(title)}
786
+ `);
787
+ }
718
788
  function summarizeChanges(changes) {
719
789
  let introduced = 0;
720
790
  let removed = 0;
@@ -731,22 +801,7 @@ function summarizeChanges(changes) {
731
801
  return { introduced, removed, severity, options, workspace };
732
802
  }
733
803
  function summarizeSnapshots(snapshots) {
734
- let rules = 0;
735
- let error = 0;
736
- let warn = 0;
737
- let off = 0;
738
- for (const snapshot of snapshots.values()) {
739
- for (const entry of Object.values(snapshot.rules)) {
740
- rules += 1;
741
- if (entry[0] === "error") {
742
- error += 1;
743
- } else if (entry[0] === "warn") {
744
- warn += 1;
745
- } else {
746
- off += 1;
747
- }
748
- }
749
- }
804
+ const { rules, error, warn, off } = countRuleSeverities([...snapshots.values()].map((snapshot) => snapshot.rules));
750
805
  return { groups: snapshots.size, rules, error, warn, off };
751
806
  }
752
807
  function decorateDiffLine(line, color) {
@@ -776,6 +831,199 @@ function writeSubtleInfo(text) {
776
831
  const color = createColorizer();
777
832
  process.stdout.write(color.dim(text));
778
833
  }
834
+ function resolveInvocationLabel(argv) {
835
+ const commandToken = argv.find((entry) => !entry.startsWith("-"));
836
+ if (commandToken) {
837
+ return commandToken;
838
+ }
839
+ if (argv.includes("-u") || argv.includes("--update")) {
840
+ return "update";
841
+ }
842
+ if (argv.includes("-h") || argv.includes("--help")) {
843
+ return "help";
844
+ }
845
+ return "check";
846
+ }
847
+ function shouldShowRunLogs() {
848
+ if (process.env.ESLINT_CONFIG_SNAPSHOT_NO_PROGRESS === "1") {
849
+ return false;
850
+ }
851
+ return process.stdout.isTTY === true;
852
+ }
853
+ function beginRunTimer(label) {
854
+ if (!shouldShowRunLogs()) {
855
+ activeRunTimer = void 0;
856
+ return;
857
+ }
858
+ activeRunTimer = {
859
+ label,
860
+ startedAtMs: Date.now(),
861
+ pausedMs: 0,
862
+ pauseStartedAtMs: void 0
863
+ };
864
+ }
865
+ function endRunTimer(exitCode) {
866
+ if (!activeRunTimer || !shouldShowRunLogs()) {
867
+ return;
868
+ }
869
+ if (activeRunTimer.pauseStartedAtMs !== void 0) {
870
+ activeRunTimer.pausedMs += Date.now() - activeRunTimer.pauseStartedAtMs;
871
+ activeRunTimer.pauseStartedAtMs = void 0;
872
+ }
873
+ const elapsedMs = Math.max(0, Date.now() - activeRunTimer.startedAtMs - activeRunTimer.pausedMs);
874
+ const color = createColorizer();
875
+ const status = exitCode === 0 ? color.green("done") : color.red("failed");
876
+ const seconds = (elapsedMs / 1e3).toFixed(2);
877
+ writeSubtleInfo(`Run ${status} in ${seconds}s
878
+ `);
879
+ activeRunTimer = void 0;
880
+ }
881
+ function pauseRunTimer() {
882
+ if (!activeRunTimer || activeRunTimer.pauseStartedAtMs !== void 0) {
883
+ return;
884
+ }
885
+ activeRunTimer.pauseStartedAtMs = Date.now();
886
+ }
887
+ function resumeRunTimer() {
888
+ if (!activeRunTimer || activeRunTimer.pauseStartedAtMs === void 0) {
889
+ return;
890
+ }
891
+ activeRunTimer.pausedMs += Date.now() - activeRunTimer.pauseStartedAtMs;
892
+ activeRunTimer.pauseStartedAtMs = void 0;
893
+ }
894
+ async function runPromptWithPausedTimer(prompt) {
895
+ pauseRunTimer();
896
+ try {
897
+ return await prompt();
898
+ } finally {
899
+ resumeRunTimer();
900
+ }
901
+ }
902
+ function readCliVersion() {
903
+ if (cachedCliVersion !== void 0) {
904
+ return cachedCliVersion;
905
+ }
906
+ const scriptPath = process.argv[1];
907
+ if (!scriptPath) {
908
+ cachedCliVersion = "unknown";
909
+ return cachedCliVersion;
910
+ }
911
+ let current = path.resolve(path.dirname(scriptPath));
912
+ while (true) {
913
+ const packageJsonPath = path.join(current, "package.json");
914
+ if (existsSync(packageJsonPath)) {
915
+ try {
916
+ const raw = readFileSync(packageJsonPath, "utf8");
917
+ const parsed = JSON.parse(raw);
918
+ cachedCliVersion = parsed.version ?? "unknown";
919
+ return cachedCliVersion;
920
+ } catch {
921
+ break;
922
+ }
923
+ }
924
+ const parent = path.dirname(current);
925
+ if (parent === current) {
926
+ break;
927
+ }
928
+ current = parent;
929
+ }
930
+ cachedCliVersion = "unknown";
931
+ return cachedCliVersion;
932
+ }
933
+ function writeRunContextHeader(cwd, commandLabel, configPath, storedSnapshots) {
934
+ if (!shouldShowRunLogs()) {
935
+ return;
936
+ }
937
+ const color = createColorizer();
938
+ process.stdout.write(color.bold(`eslint-config-snapshot v${readCliVersion()}
939
+ `));
940
+ process.stdout.write(`Command: ${commandLabel}
941
+ `);
942
+ process.stdout.write(`Repository: ${cwd}
943
+ `);
944
+ process.stdout.write(`Config: ${formatConfigSource(cwd, configPath)}
945
+ `);
946
+ process.stdout.write(`Baseline: ${formatStoredSnapshotSummary(storedSnapshots)}
947
+
948
+ `);
949
+ }
950
+ function formatConfigSource(cwd, configPath) {
951
+ if (!configPath) {
952
+ return "built-in defaults";
953
+ }
954
+ const rel = normalizePath(path.relative(cwd, configPath));
955
+ if (path.basename(configPath) === "package.json") {
956
+ return `${rel} (eslint-config-snapshot field)`;
957
+ }
958
+ return rel;
959
+ }
960
+ function formatStoredSnapshotSummary(storedSnapshots) {
961
+ if (storedSnapshots.size === 0) {
962
+ return "none";
963
+ }
964
+ const summary = summarizeStoredSnapshots(storedSnapshots);
965
+ return `${summary.groups} groups, ${summary.rules} rules (severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off)`;
966
+ }
967
+ async function resolveGroupEslintVersions(cwd) {
968
+ const config = await loadConfig(cwd);
969
+ const { discovery, assignments } = await resolveWorkspaceAssignments(cwd, config);
970
+ const result = /* @__PURE__ */ new Map();
971
+ for (const group of assignments) {
972
+ const versions = /* @__PURE__ */ new Set();
973
+ for (const workspaceRel of group.workspaces) {
974
+ const workspaceAbs = path.resolve(discovery.rootAbs, workspaceRel);
975
+ versions.add(resolveEslintVersionForWorkspace(workspaceAbs));
976
+ }
977
+ result.set(group.name, [...versions].sort((a, b) => a.localeCompare(b)));
978
+ }
979
+ return result;
980
+ }
981
+ function writeEslintVersionSummary(eslintVersionsByGroup) {
982
+ if (!shouldShowRunLogs() || eslintVersionsByGroup.size === 0) {
983
+ return;
984
+ }
985
+ const allVersions = /* @__PURE__ */ new Set();
986
+ for (const versions of eslintVersionsByGroup.values()) {
987
+ for (const version of versions) {
988
+ allVersions.add(version);
989
+ }
990
+ }
991
+ const sortedAllVersions = [...allVersions].sort((a, b) => a.localeCompare(b));
992
+ if (sortedAllVersions.length === 1) {
993
+ process.stdout.write(`- eslint runtime: ${sortedAllVersions[0]} (all groups)
994
+ `);
995
+ return;
996
+ }
997
+ process.stdout.write("- eslint runtime by group:\n");
998
+ const sortedEntries = [...eslintVersionsByGroup.entries()].sort((a, b) => a[0].localeCompare(b[0]));
999
+ for (const [groupName, versions] of sortedEntries) {
1000
+ process.stdout.write(` - ${groupName}: ${versions.join(", ")}
1001
+ `);
1002
+ }
1003
+ }
1004
+ function summarizeStoredSnapshots(snapshots) {
1005
+ const { rules, error, warn, off } = countRuleSeverities([...snapshots.values()].map((snapshot) => snapshot.rules));
1006
+ return { groups: snapshots.size, rules, error, warn, off };
1007
+ }
1008
+ function countRuleSeverities(ruleObjects) {
1009
+ let rules = 0;
1010
+ let error = 0;
1011
+ let warn = 0;
1012
+ let off = 0;
1013
+ for (const rulesObject of ruleObjects) {
1014
+ for (const entry of Object.values(rulesObject)) {
1015
+ rules += 1;
1016
+ if (entry[0] === "error") {
1017
+ error += 1;
1018
+ } else if (entry[0] === "warn") {
1019
+ warn += 1;
1020
+ } else {
1021
+ off += 1;
1022
+ }
1023
+ }
1024
+ }
1025
+ return { rules, error, warn, off };
1026
+ }
779
1027
  function formatShortPrint(snapshots) {
780
1028
  const lines = [];
781
1029
  const sorted = [...snapshots].sort((a, b) => a.groupId.localeCompare(b.groupId));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint-config-snapshot/cli",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,6 +30,6 @@
30
30
  "@inquirer/prompts": "^8.2.0",
31
31
  "commander": "^14.0.3",
32
32
  "fast-glob": "^3.3.3",
33
- "@eslint-config-snapshot/api": "0.3.2"
33
+ "@eslint-config-snapshot/api": "0.5.0"
34
34
  }
35
35
  }