@eslint-config-snapshot/cli 0.6.0 → 0.9.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 +33 -0
- package/dist/index.cjs +100 -17
- package/dist/index.js +100 -17
- package/package.json +2 -2
- package/src/index.ts +122 -24
- package/test/cli.terminal.integration.test.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @eslint-config-snapshot/cli
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Release minor version with robust CLI version resolution and improved runtime log UX.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @eslint-config-snapshot/api@0.9.0
|
|
13
|
+
|
|
14
|
+
## 0.8.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- Release minor version with improved human-readable CLI runtime logs and consistent output spacing.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies
|
|
23
|
+
- @eslint-config-snapshot/api@0.8.0
|
|
24
|
+
|
|
25
|
+
## 0.7.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- Release minor version after improving runtime command header messaging and UX consistency.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Updated dependencies
|
|
34
|
+
- @eslint-config-snapshot/api@0.7.0
|
|
35
|
+
|
|
3
36
|
## 0.6.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,7 @@ var import_commander = require("commander");
|
|
|
41
41
|
var import_fast_glob = __toESM(require("fast-glob"), 1);
|
|
42
42
|
var import_node_fs = require("fs");
|
|
43
43
|
var import_promises = require("fs/promises");
|
|
44
|
+
var import_node_module = require("module");
|
|
44
45
|
var import_node_path = __toESM(require("path"), 1);
|
|
45
46
|
var import_node_readline = require("readline");
|
|
46
47
|
var SNAPSHOT_DIR = ".eslint-config-snapshot";
|
|
@@ -218,13 +219,13 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
|
|
|
218
219
|
if (storedSnapshots.size === 0) {
|
|
219
220
|
const summary = summarizeSnapshots(currentSnapshots);
|
|
220
221
|
process.stdout.write(
|
|
221
|
-
`
|
|
222
|
+
`Rules found in this analysis: ${summary.groups} groups, ${summary.rules} rules (severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off).
|
|
222
223
|
`
|
|
223
224
|
);
|
|
224
225
|
const canPromptBaseline = defaultInvocation || format === "summary";
|
|
225
226
|
if (canPromptBaseline && process.stdin.isTTY && process.stdout.isTTY) {
|
|
226
227
|
const shouldCreateBaseline = await askYesNo(
|
|
227
|
-
"No baseline yet.
|
|
228
|
+
"No baseline yet. Do you want to save this analyzed rule state as your baseline now? [Y/n] ",
|
|
228
229
|
true
|
|
229
230
|
);
|
|
230
231
|
if (shouldCreateBaseline) {
|
|
@@ -329,7 +330,7 @@ async function executeConfig(cwd, format) {
|
|
|
329
330
|
const storedSnapshots = await loadStoredSnapshots(cwd);
|
|
330
331
|
writeRunContextHeader(cwd, `config:${format}`, foundConfig?.path, storedSnapshots);
|
|
331
332
|
if (shouldShowRunLogs()) {
|
|
332
|
-
writeSubtleInfo("\
|
|
333
|
+
writeSubtleInfo("\u2699\uFE0F Resolving effective runtime configuration...\n");
|
|
333
334
|
}
|
|
334
335
|
const config = await (0, import_api.loadConfig)(cwd);
|
|
335
336
|
const resolved = await resolveWorkspaceAssignments(cwd, config);
|
|
@@ -780,7 +781,6 @@ function printWhatChanged(changes, currentSnapshots, eslintVersionsByGroup) {
|
|
|
780
781
|
- workspace membership changes: ${changeSummary.workspace}
|
|
781
782
|
- current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
|
|
782
783
|
- current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
|
|
783
|
-
|
|
784
784
|
`
|
|
785
785
|
);
|
|
786
786
|
writeEslintVersionSummary(eslintVersionsByGroup);
|
|
@@ -890,13 +890,12 @@ function endRunTimer(exitCode) {
|
|
|
890
890
|
activeRunTimer.pauseStartedAtMs = void 0;
|
|
891
891
|
}
|
|
892
892
|
const elapsedMs = Math.max(0, Date.now() - activeRunTimer.startedAtMs - activeRunTimer.pausedMs);
|
|
893
|
-
const color = createColorizer();
|
|
894
893
|
const seconds = (elapsedMs / 1e3).toFixed(2);
|
|
895
894
|
if (exitCode === 0) {
|
|
896
|
-
writeSubtleInfo(
|
|
895
|
+
writeSubtleInfo(`Finished in ${seconds}s
|
|
897
896
|
`);
|
|
898
897
|
} else {
|
|
899
|
-
writeSubtleInfo(
|
|
898
|
+
writeSubtleInfo(`Finished with errors in ${seconds}s
|
|
900
899
|
`);
|
|
901
900
|
}
|
|
902
901
|
activeRunTimer = void 0;
|
|
@@ -926,22 +925,45 @@ function readCliVersion() {
|
|
|
926
925
|
if (cachedCliVersion !== void 0) {
|
|
927
926
|
return cachedCliVersion;
|
|
928
927
|
}
|
|
928
|
+
const envPackageName = process.env.npm_package_name;
|
|
929
|
+
const envPackageVersion = process.env.npm_package_version;
|
|
930
|
+
if (isCliPackageName(envPackageName) && typeof envPackageVersion === "string" && envPackageVersion.length > 0) {
|
|
931
|
+
cachedCliVersion = envPackageVersion;
|
|
932
|
+
return cachedCliVersion;
|
|
933
|
+
}
|
|
929
934
|
const scriptPath = process.argv[1];
|
|
930
935
|
if (!scriptPath) {
|
|
931
936
|
cachedCliVersion = "unknown";
|
|
932
937
|
return cachedCliVersion;
|
|
933
938
|
}
|
|
939
|
+
try {
|
|
940
|
+
const req = (0, import_node_module.createRequire)(import_node_path.default.resolve(scriptPath));
|
|
941
|
+
const resolvedCliEntry = req.resolve("@eslint-config-snapshot/cli");
|
|
942
|
+
const resolvedVersion = readVersionFromResolvedEntry(resolvedCliEntry);
|
|
943
|
+
if (resolvedVersion !== void 0) {
|
|
944
|
+
cachedCliVersion = resolvedVersion;
|
|
945
|
+
return cachedCliVersion;
|
|
946
|
+
}
|
|
947
|
+
} catch {
|
|
948
|
+
}
|
|
934
949
|
let current = import_node_path.default.resolve(import_node_path.default.dirname(scriptPath));
|
|
950
|
+
let fallbackVersion;
|
|
935
951
|
while (true) {
|
|
936
952
|
const packageJsonPath = import_node_path.default.join(current, "package.json");
|
|
937
953
|
if ((0, import_node_fs.existsSync)(packageJsonPath)) {
|
|
938
954
|
try {
|
|
939
955
|
const raw = (0, import_node_fs.readFileSync)(packageJsonPath, "utf8");
|
|
940
956
|
const parsed = JSON.parse(raw);
|
|
941
|
-
|
|
942
|
-
|
|
957
|
+
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
958
|
+
if (isCliPackageName(parsed.name)) {
|
|
959
|
+
cachedCliVersion = parsed.version;
|
|
960
|
+
return cachedCliVersion;
|
|
961
|
+
}
|
|
962
|
+
if (fallbackVersion === void 0) {
|
|
963
|
+
fallbackVersion = parsed.version;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
943
966
|
} catch {
|
|
944
|
-
break;
|
|
945
967
|
}
|
|
946
968
|
}
|
|
947
969
|
const parent = import_node_path.default.dirname(current);
|
|
@@ -950,25 +972,86 @@ function readCliVersion() {
|
|
|
950
972
|
}
|
|
951
973
|
current = parent;
|
|
952
974
|
}
|
|
953
|
-
cachedCliVersion = "unknown";
|
|
975
|
+
cachedCliVersion = fallbackVersion ?? "unknown";
|
|
954
976
|
return cachedCliVersion;
|
|
955
977
|
}
|
|
978
|
+
function isCliPackageName(value) {
|
|
979
|
+
return value === "@eslint-config-snapshot/cli" || value === "eslint-config-snapshot";
|
|
980
|
+
}
|
|
981
|
+
function readVersionFromResolvedEntry(entryAbs) {
|
|
982
|
+
let current = import_node_path.default.resolve(import_node_path.default.dirname(entryAbs));
|
|
983
|
+
while (true) {
|
|
984
|
+
const packageJsonPath = import_node_path.default.join(current, "package.json");
|
|
985
|
+
if ((0, import_node_fs.existsSync)(packageJsonPath)) {
|
|
986
|
+
try {
|
|
987
|
+
const raw = (0, import_node_fs.readFileSync)(packageJsonPath, "utf8");
|
|
988
|
+
const parsed = JSON.parse(raw);
|
|
989
|
+
if (isCliPackageName(parsed.name) && typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
990
|
+
return parsed.version;
|
|
991
|
+
}
|
|
992
|
+
} catch {
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
const parent = import_node_path.default.dirname(current);
|
|
996
|
+
if (parent === current) {
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
current = parent;
|
|
1000
|
+
}
|
|
1001
|
+
return void 0;
|
|
1002
|
+
}
|
|
956
1003
|
function writeRunContextHeader(cwd, commandLabel, configPath, storedSnapshots) {
|
|
957
1004
|
if (!shouldShowRunLogs()) {
|
|
958
1005
|
return;
|
|
959
1006
|
}
|
|
960
1007
|
const color = createColorizer();
|
|
961
|
-
process.stdout.write(color.bold(
|
|
1008
|
+
process.stdout.write(color.bold(`eslint-config-snapshot v${readCliVersion()} \u2022 ${formatCommandDisplayLabel(commandLabel)}
|
|
962
1009
|
`));
|
|
963
|
-
process.stdout.write(`\u{1F9ED} Command: ${commandLabel}
|
|
964
|
-
`);
|
|
965
1010
|
process.stdout.write(`\u{1F4C1} Repository: ${cwd}
|
|
966
1011
|
`);
|
|
967
|
-
process.stdout.write(`\
|
|
1012
|
+
process.stdout.write(`\u{1F4C1} Baseline: ${formatStoredSnapshotSummary(storedSnapshots)}
|
|
968
1013
|
`);
|
|
969
|
-
process.stdout.write(`\
|
|
970
|
-
|
|
1014
|
+
process.stdout.write(`\u2699\uFE0F Config source: ${formatConfigSource(cwd, configPath)}
|
|
971
1015
|
`);
|
|
1016
|
+
process.stdout.write("\n");
|
|
1017
|
+
}
|
|
1018
|
+
function formatCommandDisplayLabel(commandLabel) {
|
|
1019
|
+
switch (commandLabel) {
|
|
1020
|
+
case "check":
|
|
1021
|
+
case "check:summary": {
|
|
1022
|
+
return "Check drift against baseline (summary)";
|
|
1023
|
+
}
|
|
1024
|
+
case "check:diff": {
|
|
1025
|
+
return "Check drift against baseline (detailed diff)";
|
|
1026
|
+
}
|
|
1027
|
+
case "check:status": {
|
|
1028
|
+
return "Check drift against baseline (status only)";
|
|
1029
|
+
}
|
|
1030
|
+
case "update": {
|
|
1031
|
+
return "Update baseline snapshot";
|
|
1032
|
+
}
|
|
1033
|
+
case "print:json": {
|
|
1034
|
+
return "Print aggregated rules (JSON)";
|
|
1035
|
+
}
|
|
1036
|
+
case "print:short": {
|
|
1037
|
+
return "Print aggregated rules (short view)";
|
|
1038
|
+
}
|
|
1039
|
+
case "config:json": {
|
|
1040
|
+
return "Show effective runtime config (JSON)";
|
|
1041
|
+
}
|
|
1042
|
+
case "config:short": {
|
|
1043
|
+
return "Show effective runtime config (short view)";
|
|
1044
|
+
}
|
|
1045
|
+
case "init": {
|
|
1046
|
+
return "Initialize local configuration";
|
|
1047
|
+
}
|
|
1048
|
+
case "help": {
|
|
1049
|
+
return "Show CLI help";
|
|
1050
|
+
}
|
|
1051
|
+
default: {
|
|
1052
|
+
return commandLabel;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
972
1055
|
}
|
|
973
1056
|
function formatConfigSource(cwd, configPath) {
|
|
974
1057
|
if (!configPath) {
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
|
22
22
|
import fg from "fast-glob";
|
|
23
23
|
import { existsSync, readFileSync } from "fs";
|
|
24
24
|
import { access, mkdir, readFile, writeFile } from "fs/promises";
|
|
25
|
+
import { createRequire } from "module";
|
|
25
26
|
import path from "path";
|
|
26
27
|
import { createInterface } from "readline";
|
|
27
28
|
var SNAPSHOT_DIR = ".eslint-config-snapshot";
|
|
@@ -199,13 +200,13 @@ async function executeCheck(cwd, format, defaultInvocation = false) {
|
|
|
199
200
|
if (storedSnapshots.size === 0) {
|
|
200
201
|
const summary = summarizeSnapshots(currentSnapshots);
|
|
201
202
|
process.stdout.write(
|
|
202
|
-
`
|
|
203
|
+
`Rules found in this analysis: ${summary.groups} groups, ${summary.rules} rules (severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off).
|
|
203
204
|
`
|
|
204
205
|
);
|
|
205
206
|
const canPromptBaseline = defaultInvocation || format === "summary";
|
|
206
207
|
if (canPromptBaseline && process.stdin.isTTY && process.stdout.isTTY) {
|
|
207
208
|
const shouldCreateBaseline = await askYesNo(
|
|
208
|
-
"No baseline yet.
|
|
209
|
+
"No baseline yet. Do you want to save this analyzed rule state as your baseline now? [Y/n] ",
|
|
209
210
|
true
|
|
210
211
|
);
|
|
211
212
|
if (shouldCreateBaseline) {
|
|
@@ -310,7 +311,7 @@ async function executeConfig(cwd, format) {
|
|
|
310
311
|
const storedSnapshots = await loadStoredSnapshots(cwd);
|
|
311
312
|
writeRunContextHeader(cwd, `config:${format}`, foundConfig?.path, storedSnapshots);
|
|
312
313
|
if (shouldShowRunLogs()) {
|
|
313
|
-
writeSubtleInfo("\
|
|
314
|
+
writeSubtleInfo("\u2699\uFE0F Resolving effective runtime configuration...\n");
|
|
314
315
|
}
|
|
315
316
|
const config = await loadConfig(cwd);
|
|
316
317
|
const resolved = await resolveWorkspaceAssignments(cwd, config);
|
|
@@ -761,7 +762,6 @@ function printWhatChanged(changes, currentSnapshots, eslintVersionsByGroup) {
|
|
|
761
762
|
- workspace membership changes: ${changeSummary.workspace}
|
|
762
763
|
- current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
|
|
763
764
|
- current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
|
|
764
|
-
|
|
765
765
|
`
|
|
766
766
|
);
|
|
767
767
|
writeEslintVersionSummary(eslintVersionsByGroup);
|
|
@@ -871,13 +871,12 @@ function endRunTimer(exitCode) {
|
|
|
871
871
|
activeRunTimer.pauseStartedAtMs = void 0;
|
|
872
872
|
}
|
|
873
873
|
const elapsedMs = Math.max(0, Date.now() - activeRunTimer.startedAtMs - activeRunTimer.pausedMs);
|
|
874
|
-
const color = createColorizer();
|
|
875
874
|
const seconds = (elapsedMs / 1e3).toFixed(2);
|
|
876
875
|
if (exitCode === 0) {
|
|
877
|
-
writeSubtleInfo(
|
|
876
|
+
writeSubtleInfo(`Finished in ${seconds}s
|
|
878
877
|
`);
|
|
879
878
|
} else {
|
|
880
|
-
writeSubtleInfo(
|
|
879
|
+
writeSubtleInfo(`Finished with errors in ${seconds}s
|
|
881
880
|
`);
|
|
882
881
|
}
|
|
883
882
|
activeRunTimer = void 0;
|
|
@@ -907,22 +906,45 @@ function readCliVersion() {
|
|
|
907
906
|
if (cachedCliVersion !== void 0) {
|
|
908
907
|
return cachedCliVersion;
|
|
909
908
|
}
|
|
909
|
+
const envPackageName = process.env.npm_package_name;
|
|
910
|
+
const envPackageVersion = process.env.npm_package_version;
|
|
911
|
+
if (isCliPackageName(envPackageName) && typeof envPackageVersion === "string" && envPackageVersion.length > 0) {
|
|
912
|
+
cachedCliVersion = envPackageVersion;
|
|
913
|
+
return cachedCliVersion;
|
|
914
|
+
}
|
|
910
915
|
const scriptPath = process.argv[1];
|
|
911
916
|
if (!scriptPath) {
|
|
912
917
|
cachedCliVersion = "unknown";
|
|
913
918
|
return cachedCliVersion;
|
|
914
919
|
}
|
|
920
|
+
try {
|
|
921
|
+
const req = createRequire(path.resolve(scriptPath));
|
|
922
|
+
const resolvedCliEntry = req.resolve("@eslint-config-snapshot/cli");
|
|
923
|
+
const resolvedVersion = readVersionFromResolvedEntry(resolvedCliEntry);
|
|
924
|
+
if (resolvedVersion !== void 0) {
|
|
925
|
+
cachedCliVersion = resolvedVersion;
|
|
926
|
+
return cachedCliVersion;
|
|
927
|
+
}
|
|
928
|
+
} catch {
|
|
929
|
+
}
|
|
915
930
|
let current = path.resolve(path.dirname(scriptPath));
|
|
931
|
+
let fallbackVersion;
|
|
916
932
|
while (true) {
|
|
917
933
|
const packageJsonPath = path.join(current, "package.json");
|
|
918
934
|
if (existsSync(packageJsonPath)) {
|
|
919
935
|
try {
|
|
920
936
|
const raw = readFileSync(packageJsonPath, "utf8");
|
|
921
937
|
const parsed = JSON.parse(raw);
|
|
922
|
-
|
|
923
|
-
|
|
938
|
+
if (typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
939
|
+
if (isCliPackageName(parsed.name)) {
|
|
940
|
+
cachedCliVersion = parsed.version;
|
|
941
|
+
return cachedCliVersion;
|
|
942
|
+
}
|
|
943
|
+
if (fallbackVersion === void 0) {
|
|
944
|
+
fallbackVersion = parsed.version;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
924
947
|
} catch {
|
|
925
|
-
break;
|
|
926
948
|
}
|
|
927
949
|
}
|
|
928
950
|
const parent = path.dirname(current);
|
|
@@ -931,25 +953,86 @@ function readCliVersion() {
|
|
|
931
953
|
}
|
|
932
954
|
current = parent;
|
|
933
955
|
}
|
|
934
|
-
cachedCliVersion = "unknown";
|
|
956
|
+
cachedCliVersion = fallbackVersion ?? "unknown";
|
|
935
957
|
return cachedCliVersion;
|
|
936
958
|
}
|
|
959
|
+
function isCliPackageName(value) {
|
|
960
|
+
return value === "@eslint-config-snapshot/cli" || value === "eslint-config-snapshot";
|
|
961
|
+
}
|
|
962
|
+
function readVersionFromResolvedEntry(entryAbs) {
|
|
963
|
+
let current = path.resolve(path.dirname(entryAbs));
|
|
964
|
+
while (true) {
|
|
965
|
+
const packageJsonPath = path.join(current, "package.json");
|
|
966
|
+
if (existsSync(packageJsonPath)) {
|
|
967
|
+
try {
|
|
968
|
+
const raw = readFileSync(packageJsonPath, "utf8");
|
|
969
|
+
const parsed = JSON.parse(raw);
|
|
970
|
+
if (isCliPackageName(parsed.name) && typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
971
|
+
return parsed.version;
|
|
972
|
+
}
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
const parent = path.dirname(current);
|
|
977
|
+
if (parent === current) {
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
current = parent;
|
|
981
|
+
}
|
|
982
|
+
return void 0;
|
|
983
|
+
}
|
|
937
984
|
function writeRunContextHeader(cwd, commandLabel, configPath, storedSnapshots) {
|
|
938
985
|
if (!shouldShowRunLogs()) {
|
|
939
986
|
return;
|
|
940
987
|
}
|
|
941
988
|
const color = createColorizer();
|
|
942
|
-
process.stdout.write(color.bold(
|
|
989
|
+
process.stdout.write(color.bold(`eslint-config-snapshot v${readCliVersion()} \u2022 ${formatCommandDisplayLabel(commandLabel)}
|
|
943
990
|
`));
|
|
944
|
-
process.stdout.write(`\u{1F9ED} Command: ${commandLabel}
|
|
945
|
-
`);
|
|
946
991
|
process.stdout.write(`\u{1F4C1} Repository: ${cwd}
|
|
947
992
|
`);
|
|
948
|
-
process.stdout.write(`\
|
|
993
|
+
process.stdout.write(`\u{1F4C1} Baseline: ${formatStoredSnapshotSummary(storedSnapshots)}
|
|
949
994
|
`);
|
|
950
|
-
process.stdout.write(`\
|
|
951
|
-
|
|
995
|
+
process.stdout.write(`\u2699\uFE0F Config source: ${formatConfigSource(cwd, configPath)}
|
|
952
996
|
`);
|
|
997
|
+
process.stdout.write("\n");
|
|
998
|
+
}
|
|
999
|
+
function formatCommandDisplayLabel(commandLabel) {
|
|
1000
|
+
switch (commandLabel) {
|
|
1001
|
+
case "check":
|
|
1002
|
+
case "check:summary": {
|
|
1003
|
+
return "Check drift against baseline (summary)";
|
|
1004
|
+
}
|
|
1005
|
+
case "check:diff": {
|
|
1006
|
+
return "Check drift against baseline (detailed diff)";
|
|
1007
|
+
}
|
|
1008
|
+
case "check:status": {
|
|
1009
|
+
return "Check drift against baseline (status only)";
|
|
1010
|
+
}
|
|
1011
|
+
case "update": {
|
|
1012
|
+
return "Update baseline snapshot";
|
|
1013
|
+
}
|
|
1014
|
+
case "print:json": {
|
|
1015
|
+
return "Print aggregated rules (JSON)";
|
|
1016
|
+
}
|
|
1017
|
+
case "print:short": {
|
|
1018
|
+
return "Print aggregated rules (short view)";
|
|
1019
|
+
}
|
|
1020
|
+
case "config:json": {
|
|
1021
|
+
return "Show effective runtime config (JSON)";
|
|
1022
|
+
}
|
|
1023
|
+
case "config:short": {
|
|
1024
|
+
return "Show effective runtime config (short view)";
|
|
1025
|
+
}
|
|
1026
|
+
case "init": {
|
|
1027
|
+
return "Initialize local configuration";
|
|
1028
|
+
}
|
|
1029
|
+
case "help": {
|
|
1030
|
+
return "Show CLI help";
|
|
1031
|
+
}
|
|
1032
|
+
default: {
|
|
1033
|
+
return commandLabel;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
953
1036
|
}
|
|
954
1037
|
function formatConfigSource(cwd, configPath) {
|
|
955
1038
|
if (!configPath) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-config-snapshot/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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.
|
|
33
|
+
"@eslint-config-snapshot/api": "0.9.0"
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { Command, CommanderError, InvalidArgumentError } from 'commander'
|
|
|
20
20
|
import fg from 'fast-glob'
|
|
21
21
|
import { existsSync, readFileSync } from 'node:fs'
|
|
22
22
|
import { access, mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
23
|
+
import { createRequire } from 'node:module'
|
|
23
24
|
import path from 'node:path'
|
|
24
25
|
import { createInterface } from 'node:readline'
|
|
25
26
|
|
|
@@ -293,18 +294,18 @@ async function executeCheck(cwd: string, format: CheckFormat, defaultInvocation
|
|
|
293
294
|
|
|
294
295
|
throw error
|
|
295
296
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
const canPromptBaseline = defaultInvocation || format === 'summary'
|
|
303
|
-
if (canPromptBaseline && process.stdin.isTTY && process.stdout.isTTY) {
|
|
304
|
-
const shouldCreateBaseline = await askYesNo(
|
|
305
|
-
'No baseline yet. Use current rule state as your baseline now? [Y/n] ',
|
|
306
|
-
true
|
|
297
|
+
if (storedSnapshots.size === 0) {
|
|
298
|
+
const summary = summarizeSnapshots(currentSnapshots)
|
|
299
|
+
process.stdout.write(
|
|
300
|
+
`Rules found in this analysis: ${summary.groups} groups, ${summary.rules} rules (severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off).\n`
|
|
307
301
|
)
|
|
302
|
+
|
|
303
|
+
const canPromptBaseline = defaultInvocation || format === 'summary'
|
|
304
|
+
if (canPromptBaseline && process.stdin.isTTY && process.stdout.isTTY) {
|
|
305
|
+
const shouldCreateBaseline = await askYesNo(
|
|
306
|
+
'No baseline yet. Do you want to save this analyzed rule state as your baseline now? [Y/n] ',
|
|
307
|
+
true
|
|
308
|
+
)
|
|
308
309
|
if (shouldCreateBaseline) {
|
|
309
310
|
await writeSnapshots(cwd, currentSnapshots)
|
|
310
311
|
const summary = summarizeSnapshots(currentSnapshots)
|
|
@@ -420,7 +421,7 @@ async function executeConfig(cwd: string, format: PrintFormat): Promise<void> {
|
|
|
420
421
|
const storedSnapshots = await loadStoredSnapshots(cwd)
|
|
421
422
|
writeRunContextHeader(cwd, `config:${format}`, foundConfig?.path, storedSnapshots)
|
|
422
423
|
if (shouldShowRunLogs()) {
|
|
423
|
-
writeSubtleInfo('
|
|
424
|
+
writeSubtleInfo('⚙️ Resolving effective runtime configuration...\n')
|
|
424
425
|
}
|
|
425
426
|
const config = await loadConfig(cwd)
|
|
426
427
|
const resolved = await resolveWorkspaceAssignments(cwd, config)
|
|
@@ -969,7 +970,7 @@ function printWhatChanged(
|
|
|
969
970
|
process.stdout.write(color.red('Heads up: snapshot drift detected.\n'))
|
|
970
971
|
writeSectionTitle('Summary', color)
|
|
971
972
|
process.stdout.write(
|
|
972
|
-
`- changed groups: ${changes.length}\n- introduced rules: ${changeSummary.introduced}\n- removed rules: ${changeSummary.removed}\n- severity changes: ${changeSummary.severity}\n- options changes: ${changeSummary.options}\n- workspace membership changes: ${changeSummary.workspace}\n- current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules\n- current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off\n
|
|
973
|
+
`- changed groups: ${changes.length}\n- introduced rules: ${changeSummary.introduced}\n- removed rules: ${changeSummary.removed}\n- severity changes: ${changeSummary.severity}\n- options changes: ${changeSummary.options}\n- workspace membership changes: ${changeSummary.workspace}\n- current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules\n- current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off\n`
|
|
973
974
|
)
|
|
974
975
|
writeEslintVersionSummary(eslintVersionsByGroup)
|
|
975
976
|
process.stdout.write('\n')
|
|
@@ -1090,12 +1091,11 @@ function endRunTimer(exitCode: number): void {
|
|
|
1090
1091
|
}
|
|
1091
1092
|
|
|
1092
1093
|
const elapsedMs = Math.max(0, Date.now() - activeRunTimer.startedAtMs - activeRunTimer.pausedMs)
|
|
1093
|
-
const color = createColorizer()
|
|
1094
1094
|
const seconds = (elapsedMs / 1000).toFixed(2)
|
|
1095
1095
|
if (exitCode === 0) {
|
|
1096
|
-
writeSubtleInfo(
|
|
1096
|
+
writeSubtleInfo(`Finished in ${seconds}s\n`)
|
|
1097
1097
|
} else {
|
|
1098
|
-
writeSubtleInfo(
|
|
1098
|
+
writeSubtleInfo(`Finished with errors in ${seconds}s\n`)
|
|
1099
1099
|
}
|
|
1100
1100
|
activeRunTimer = undefined
|
|
1101
1101
|
}
|
|
@@ -1130,23 +1130,51 @@ function readCliVersion(): string {
|
|
|
1130
1130
|
return cachedCliVersion
|
|
1131
1131
|
}
|
|
1132
1132
|
|
|
1133
|
+
const envPackageName = process.env.npm_package_name
|
|
1134
|
+
const envPackageVersion = process.env.npm_package_version
|
|
1135
|
+
if (isCliPackageName(envPackageName) && typeof envPackageVersion === 'string' && envPackageVersion.length > 0) {
|
|
1136
|
+
cachedCliVersion = envPackageVersion
|
|
1137
|
+
return cachedCliVersion
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1133
1140
|
const scriptPath = process.argv[1]
|
|
1134
1141
|
if (!scriptPath) {
|
|
1135
1142
|
cachedCliVersion = 'unknown'
|
|
1136
1143
|
return cachedCliVersion
|
|
1137
1144
|
}
|
|
1138
1145
|
|
|
1146
|
+
try {
|
|
1147
|
+
const req = createRequire(path.resolve(scriptPath))
|
|
1148
|
+
const resolvedCliEntry = req.resolve('@eslint-config-snapshot/cli')
|
|
1149
|
+
const resolvedVersion = readVersionFromResolvedEntry(resolvedCliEntry)
|
|
1150
|
+
if (resolvedVersion !== undefined) {
|
|
1151
|
+
cachedCliVersion = resolvedVersion
|
|
1152
|
+
return cachedCliVersion
|
|
1153
|
+
}
|
|
1154
|
+
} catch {
|
|
1155
|
+
// continue to path-walk fallback
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1139
1158
|
let current = path.resolve(path.dirname(scriptPath))
|
|
1159
|
+
let fallbackVersion: string | undefined
|
|
1140
1160
|
while (true) {
|
|
1141
1161
|
const packageJsonPath = path.join(current, 'package.json')
|
|
1142
1162
|
if (existsSync(packageJsonPath)) {
|
|
1143
1163
|
try {
|
|
1144
1164
|
const raw = readFileSync(packageJsonPath, 'utf8')
|
|
1145
|
-
const parsed = JSON.parse(raw) as { version?: string }
|
|
1146
|
-
|
|
1147
|
-
|
|
1165
|
+
const parsed = JSON.parse(raw) as { name?: string; version?: string }
|
|
1166
|
+
if (typeof parsed.version === 'string' && parsed.version.length > 0) {
|
|
1167
|
+
if (isCliPackageName(parsed.name)) {
|
|
1168
|
+
cachedCliVersion = parsed.version
|
|
1169
|
+
return cachedCliVersion
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (fallbackVersion === undefined) {
|
|
1173
|
+
fallbackVersion = parsed.version
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1148
1176
|
} catch {
|
|
1149
|
-
|
|
1177
|
+
// continue walking up
|
|
1150
1178
|
}
|
|
1151
1179
|
}
|
|
1152
1180
|
|
|
@@ -1157,10 +1185,41 @@ function readCliVersion(): string {
|
|
|
1157
1185
|
current = parent
|
|
1158
1186
|
}
|
|
1159
1187
|
|
|
1160
|
-
cachedCliVersion = 'unknown'
|
|
1188
|
+
cachedCliVersion = fallbackVersion ?? 'unknown'
|
|
1161
1189
|
return cachedCliVersion
|
|
1162
1190
|
}
|
|
1163
1191
|
|
|
1192
|
+
function isCliPackageName(value: string | undefined): boolean {
|
|
1193
|
+
return value === '@eslint-config-snapshot/cli' || value === 'eslint-config-snapshot'
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
function readVersionFromResolvedEntry(entryAbs: string): string | undefined {
|
|
1197
|
+
let current = path.resolve(path.dirname(entryAbs))
|
|
1198
|
+
|
|
1199
|
+
while (true) {
|
|
1200
|
+
const packageJsonPath = path.join(current, 'package.json')
|
|
1201
|
+
if (existsSync(packageJsonPath)) {
|
|
1202
|
+
try {
|
|
1203
|
+
const raw = readFileSync(packageJsonPath, 'utf8')
|
|
1204
|
+
const parsed = JSON.parse(raw) as { name?: string; version?: string }
|
|
1205
|
+
if (isCliPackageName(parsed.name) && typeof parsed.version === 'string' && parsed.version.length > 0) {
|
|
1206
|
+
return parsed.version
|
|
1207
|
+
}
|
|
1208
|
+
} catch {
|
|
1209
|
+
// continue walking up
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const parent = path.dirname(current)
|
|
1214
|
+
if (parent === current) {
|
|
1215
|
+
break
|
|
1216
|
+
}
|
|
1217
|
+
current = parent
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
return undefined
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1164
1223
|
function writeRunContextHeader(
|
|
1165
1224
|
cwd: string,
|
|
1166
1225
|
commandLabel: string,
|
|
@@ -1172,11 +1231,50 @@ function writeRunContextHeader(
|
|
|
1172
1231
|
}
|
|
1173
1232
|
|
|
1174
1233
|
const color = createColorizer()
|
|
1175
|
-
process.stdout.write(color.bold(
|
|
1176
|
-
process.stdout.write(`🧭 Command: ${commandLabel}\n`)
|
|
1234
|
+
process.stdout.write(color.bold(`eslint-config-snapshot v${readCliVersion()} • ${formatCommandDisplayLabel(commandLabel)}\n`))
|
|
1177
1235
|
process.stdout.write(`📁 Repository: ${cwd}\n`)
|
|
1236
|
+
process.stdout.write(`📁 Baseline: ${formatStoredSnapshotSummary(storedSnapshots)}\n`)
|
|
1178
1237
|
process.stdout.write(`⚙️ Config source: ${formatConfigSource(cwd, configPath)}\n`)
|
|
1179
|
-
process.stdout.write(
|
|
1238
|
+
process.stdout.write('\n')
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function formatCommandDisplayLabel(commandLabel: string): string {
|
|
1242
|
+
switch (commandLabel) {
|
|
1243
|
+
case 'check':
|
|
1244
|
+
case 'check:summary': {
|
|
1245
|
+
return 'Check drift against baseline (summary)'
|
|
1246
|
+
}
|
|
1247
|
+
case 'check:diff': {
|
|
1248
|
+
return 'Check drift against baseline (detailed diff)'
|
|
1249
|
+
}
|
|
1250
|
+
case 'check:status': {
|
|
1251
|
+
return 'Check drift against baseline (status only)'
|
|
1252
|
+
}
|
|
1253
|
+
case 'update': {
|
|
1254
|
+
return 'Update baseline snapshot'
|
|
1255
|
+
}
|
|
1256
|
+
case 'print:json': {
|
|
1257
|
+
return 'Print aggregated rules (JSON)'
|
|
1258
|
+
}
|
|
1259
|
+
case 'print:short': {
|
|
1260
|
+
return 'Print aggregated rules (short view)'
|
|
1261
|
+
}
|
|
1262
|
+
case 'config:json': {
|
|
1263
|
+
return 'Show effective runtime config (JSON)'
|
|
1264
|
+
}
|
|
1265
|
+
case 'config:short': {
|
|
1266
|
+
return 'Show effective runtime config (short view)'
|
|
1267
|
+
}
|
|
1268
|
+
case 'init': {
|
|
1269
|
+
return 'Initialize local configuration'
|
|
1270
|
+
}
|
|
1271
|
+
case 'help': {
|
|
1272
|
+
return 'Show CLI help'
|
|
1273
|
+
}
|
|
1274
|
+
default: {
|
|
1275
|
+
return commandLabel
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1180
1278
|
}
|
|
1181
1279
|
|
|
1182
1280
|
function formatConfigSource(cwd: string, configPath: string | undefined): string {
|
|
@@ -101,7 +101,7 @@ describe('cli terminal invocation', () => {
|
|
|
101
101
|
const result = run([])
|
|
102
102
|
expect(result.status).toBe(1)
|
|
103
103
|
expect(result.stdout).toBe(
|
|
104
|
-
'
|
|
104
|
+
'Rules found in this analysis: 1 groups, 3 rules (severity mix: 2 errors, 0 warnings, 1 off).\nYou are almost set: no baseline snapshot found yet.\nRun `eslint-config-snapshot --update` to create your first baseline.\n'
|
|
105
105
|
)
|
|
106
106
|
})
|
|
107
107
|
|