@eslint-config-snapshot/cli 0.4.0 → 0.6.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.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Release minor version with improved interactive CLI logging tone and richer runtime context reporting.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @eslint-config-snapshot/api@0.6.0
13
+
14
+ ## 0.5.0
15
+
16
+ ### Minor Changes
17
+
18
+ - Add ESLint runtime version reporting by group in CLI summaries and improve pnpm/corepack isolated test resilience.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+ - @eslint-config-snapshot/api@0.5.0
24
+
3
25
  ## 0.4.0
4
26
 
5
27
  ### Minor 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("\u{1F50E} Checking 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("\u{1F50E} Checking 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"
@@ -267,16 +294,24 @@ async function executeUpdate(cwd, printSummary) {
267
294
  if (printSummary) {
268
295
  const summary = summarizeSnapshots(currentSnapshots);
269
296
  const color = createColorizer();
297
+ const eslintVersionsByGroup = shouldShowRunLogs() ? await resolveGroupEslintVersions(cwd) : /* @__PURE__ */ new Map();
270
298
  writeSectionTitle("Summary", color);
271
299
  process.stdout.write(
272
300
  `Baseline updated: ${summary.groups} groups, ${summary.rules} rules.
273
301
  Severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off.
274
302
  `
275
303
  );
304
+ writeEslintVersionSummary(eslintVersionsByGroup);
276
305
  }
277
306
  return 0;
278
307
  }
279
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("\u{1F50E} Checking current ESLint configuration...\n");
314
+ }
280
315
  const currentSnapshots = await computeCurrentSnapshots(cwd);
281
316
  if (format === "short") {
282
317
  process.stdout.write(formatShortPrint([...currentSnapshots.values()]));
@@ -291,6 +326,11 @@ async function executePrint(cwd, format) {
291
326
  }
292
327
  async function executeConfig(cwd, format) {
293
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("\u{1F50E} Resolving effective runtime configuration...\n");
333
+ }
294
334
  const config = await (0, import_api.loadConfig)(cwd);
295
335
  const resolved = await resolveWorkspaceAssignments(cwd, config);
296
336
  const payload = {
@@ -476,8 +516,8 @@ ${JSON.stringify(configObject, null, 2)}
476
516
  }
477
517
  async function askInitPreferences() {
478
518
  const { select } = await import("@inquirer/prompts");
479
- const target = await askInitTarget(select);
480
- const preset = await askInitPreset(select);
519
+ const target = await runPromptWithPausedTimer(() => askInitTarget(select));
520
+ const preset = await runPromptWithPausedTimer(() => askInitPreset(select));
481
521
  return { target, preset };
482
522
  }
483
523
  async function askInitTarget(selectPrompt) {
@@ -500,8 +540,10 @@ async function askInitPreset(selectPrompt) {
500
540
  });
501
541
  }
502
542
  function askQuestion(rl, prompt) {
543
+ pauseRunTimer();
503
544
  return new Promise((resolve) => {
504
545
  rl.question(prompt, (answer) => {
546
+ resumeRunTimer();
505
547
  resolve(answer);
506
548
  });
507
549
  });
@@ -647,11 +689,13 @@ async function askRecommendedGroupAssignments(workspaces) {
647
689
  'Recommended setup: default group "*" is a dynamic catch-all for every discovered workspace.\n'
648
690
  );
649
691
  process.stdout.write("Select only workspaces that should move to explicit static groups.\n");
650
- const overrides = await checkbox({
651
- message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
652
- choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
653
- pageSize: Math.min(12, Math.max(4, workspaces.length))
654
- });
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
+ );
655
699
  const assignments = /* @__PURE__ */ new Map();
656
700
  let nextGroup = 1;
657
701
  for (const workspace of overrides) {
@@ -659,13 +703,15 @@ async function askRecommendedGroupAssignments(workspaces) {
659
703
  while (usedGroups.includes(nextGroup)) {
660
704
  nextGroup += 1;
661
705
  }
662
- const selected = await select({
663
- message: `Select group for ${workspace}`,
664
- choices: [
665
- ...usedGroups.map((group) => ({ name: `group-${group}`, value: group })),
666
- { name: `create new group (group-${nextGroup})`, value: "new" }
667
- ]
668
- });
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
+ );
669
715
  const groupNumber = selected === "new" ? nextGroup : selected;
670
716
  assignments.set(workspace, groupNumber);
671
717
  }
@@ -708,7 +754,7 @@ function isDirectCliExecution() {
708
754
  if (isDirectCliExecution()) {
709
755
  void main();
710
756
  }
711
- function printWhatChanged(changes, currentSnapshots) {
757
+ function printWhatChanged(changes, currentSnapshots, eslintVersionsByGroup) {
712
758
  const color = createColorizer();
713
759
  const currentSummary = summarizeSnapshots(currentSnapshots);
714
760
  const changeSummary = summarizeChanges(changes);
@@ -720,6 +766,7 @@ function printWhatChanged(changes, currentSnapshots) {
720
766
  - severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
721
767
  `
722
768
  );
769
+ writeEslintVersionSummary(eslintVersionsByGroup);
723
770
  return 0;
724
771
  }
725
772
  process.stdout.write(color.red("Heads up: snapshot drift detected.\n"));
@@ -736,6 +783,8 @@ function printWhatChanged(changes, currentSnapshots) {
736
783
 
737
784
  `
738
785
  );
786
+ writeEslintVersionSummary(eslintVersionsByGroup);
787
+ process.stdout.write("\n");
739
788
  writeSectionTitle("Changes", color);
740
789
  for (const change of changes) {
741
790
  process.stdout.write(color.bold(`group ${change.groupId}
@@ -771,22 +820,7 @@ function summarizeChanges(changes) {
771
820
  return { introduced, removed, severity, options, workspace };
772
821
  }
773
822
  function summarizeSnapshots(snapshots) {
774
- let rules = 0;
775
- let error = 0;
776
- let warn = 0;
777
- let off = 0;
778
- for (const snapshot of snapshots.values()) {
779
- for (const entry of Object.values(snapshot.rules)) {
780
- rules += 1;
781
- if (entry[0] === "error") {
782
- error += 1;
783
- } else if (entry[0] === "warn") {
784
- warn += 1;
785
- } else {
786
- off += 1;
787
- }
788
- }
789
- }
823
+ const { rules, error, warn, off } = countRuleSeverities([...snapshots.values()].map((snapshot) => snapshot.rules));
790
824
  return { groups: snapshots.size, rules, error, warn, off };
791
825
  }
792
826
  function decorateDiffLine(line, color) {
@@ -816,6 +850,203 @@ function writeSubtleInfo(text) {
816
850
  const color = createColorizer();
817
851
  process.stdout.write(color.dim(text));
818
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 seconds = (elapsedMs / 1e3).toFixed(2);
895
+ if (exitCode === 0) {
896
+ writeSubtleInfo(`${color.green("\u2705")} Finished in ${seconds}s
897
+ `);
898
+ } else {
899
+ writeSubtleInfo(`${color.red("\u274C")} Finished in ${seconds}s
900
+ `);
901
+ }
902
+ activeRunTimer = void 0;
903
+ }
904
+ function pauseRunTimer() {
905
+ if (!activeRunTimer || activeRunTimer.pauseStartedAtMs !== void 0) {
906
+ return;
907
+ }
908
+ activeRunTimer.pauseStartedAtMs = Date.now();
909
+ }
910
+ function resumeRunTimer() {
911
+ if (!activeRunTimer || activeRunTimer.pauseStartedAtMs === void 0) {
912
+ return;
913
+ }
914
+ activeRunTimer.pausedMs += Date.now() - activeRunTimer.pauseStartedAtMs;
915
+ activeRunTimer.pauseStartedAtMs = void 0;
916
+ }
917
+ async function runPromptWithPausedTimer(prompt) {
918
+ pauseRunTimer();
919
+ try {
920
+ return await prompt();
921
+ } finally {
922
+ resumeRunTimer();
923
+ }
924
+ }
925
+ function readCliVersion() {
926
+ if (cachedCliVersion !== void 0) {
927
+ return cachedCliVersion;
928
+ }
929
+ const scriptPath = process.argv[1];
930
+ if (!scriptPath) {
931
+ cachedCliVersion = "unknown";
932
+ return cachedCliVersion;
933
+ }
934
+ let current = import_node_path.default.resolve(import_node_path.default.dirname(scriptPath));
935
+ while (true) {
936
+ const packageJsonPath = import_node_path.default.join(current, "package.json");
937
+ if ((0, import_node_fs.existsSync)(packageJsonPath)) {
938
+ try {
939
+ const raw = (0, import_node_fs.readFileSync)(packageJsonPath, "utf8");
940
+ const parsed = JSON.parse(raw);
941
+ cachedCliVersion = parsed.version ?? "unknown";
942
+ return cachedCliVersion;
943
+ } catch {
944
+ break;
945
+ }
946
+ }
947
+ const parent = import_node_path.default.dirname(current);
948
+ if (parent === current) {
949
+ break;
950
+ }
951
+ current = parent;
952
+ }
953
+ cachedCliVersion = "unknown";
954
+ return cachedCliVersion;
955
+ }
956
+ function writeRunContextHeader(cwd, commandLabel, configPath, storedSnapshots) {
957
+ if (!shouldShowRunLogs()) {
958
+ return;
959
+ }
960
+ const color = createColorizer();
961
+ process.stdout.write(color.bold(`\u2728 eslint-config-snapshot v${readCliVersion()}
962
+ `));
963
+ process.stdout.write(`\u{1F9ED} Command: ${commandLabel}
964
+ `);
965
+ process.stdout.write(`\u{1F4C1} Repository: ${cwd}
966
+ `);
967
+ process.stdout.write(`\u2699\uFE0F Config source: ${formatConfigSource(cwd, configPath)}
968
+ `);
969
+ process.stdout.write(`\u{1F5C2}\uFE0F Baseline: ${formatStoredSnapshotSummary(storedSnapshots)}
970
+
971
+ `);
972
+ }
973
+ function formatConfigSource(cwd, configPath) {
974
+ if (!configPath) {
975
+ return "built-in defaults";
976
+ }
977
+ const rel = (0, import_api.normalizePath)(import_node_path.default.relative(cwd, configPath));
978
+ if (import_node_path.default.basename(configPath) === "package.json") {
979
+ return `${rel} (eslint-config-snapshot field)`;
980
+ }
981
+ return rel;
982
+ }
983
+ function formatStoredSnapshotSummary(storedSnapshots) {
984
+ if (storedSnapshots.size === 0) {
985
+ return "none";
986
+ }
987
+ const summary = summarizeStoredSnapshots(storedSnapshots);
988
+ return `${summary.groups} groups, ${summary.rules} rules (severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off)`;
989
+ }
990
+ async function resolveGroupEslintVersions(cwd) {
991
+ const config = await (0, import_api.loadConfig)(cwd);
992
+ const { discovery, assignments } = await resolveWorkspaceAssignments(cwd, config);
993
+ const result = /* @__PURE__ */ new Map();
994
+ for (const group of assignments) {
995
+ const versions = /* @__PURE__ */ new Set();
996
+ for (const workspaceRel of group.workspaces) {
997
+ const workspaceAbs = import_node_path.default.resolve(discovery.rootAbs, workspaceRel);
998
+ versions.add((0, import_api.resolveEslintVersionForWorkspace)(workspaceAbs));
999
+ }
1000
+ result.set(group.name, [...versions].sort((a, b) => a.localeCompare(b)));
1001
+ }
1002
+ return result;
1003
+ }
1004
+ function writeEslintVersionSummary(eslintVersionsByGroup) {
1005
+ if (!shouldShowRunLogs() || eslintVersionsByGroup.size === 0) {
1006
+ return;
1007
+ }
1008
+ const allVersions = /* @__PURE__ */ new Set();
1009
+ for (const versions of eslintVersionsByGroup.values()) {
1010
+ for (const version of versions) {
1011
+ allVersions.add(version);
1012
+ }
1013
+ }
1014
+ const sortedAllVersions = [...allVersions].sort((a, b) => a.localeCompare(b));
1015
+ if (sortedAllVersions.length === 1) {
1016
+ process.stdout.write(`- eslint runtime: ${sortedAllVersions[0]} (all groups)
1017
+ `);
1018
+ return;
1019
+ }
1020
+ process.stdout.write("- eslint runtime by group:\n");
1021
+ const sortedEntries = [...eslintVersionsByGroup.entries()].sort((a, b) => a[0].localeCompare(b[0]));
1022
+ for (const [groupName, versions] of sortedEntries) {
1023
+ process.stdout.write(` - ${groupName}: ${versions.join(", ")}
1024
+ `);
1025
+ }
1026
+ }
1027
+ function summarizeStoredSnapshots(snapshots) {
1028
+ const { rules, error, warn, off } = countRuleSeverities([...snapshots.values()].map((snapshot) => snapshot.rules));
1029
+ return { groups: snapshots.size, rules, error, warn, off };
1030
+ }
1031
+ function countRuleSeverities(ruleObjects) {
1032
+ let rules = 0;
1033
+ let error = 0;
1034
+ let warn = 0;
1035
+ let off = 0;
1036
+ for (const rulesObject of ruleObjects) {
1037
+ for (const entry of Object.values(rulesObject)) {
1038
+ rules += 1;
1039
+ if (entry[0] === "error") {
1040
+ error += 1;
1041
+ } else if (entry[0] === "warn") {
1042
+ warn += 1;
1043
+ } else {
1044
+ off += 1;
1045
+ }
1046
+ }
1047
+ }
1048
+ return { rules, error, warn, off };
1049
+ }
819
1050
  function formatShortPrint(snapshots) {
820
1051
  const lines = [];
821
1052
  const sorted = [...snapshots].sort((a, b) => a.groupId.localeCompare(b.groupId));