@eslint-config-snapshot/cli 0.3.0 → 0.4.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 +27 -0
- package/dist/index.cjs +46 -23
- package/dist/index.js +47 -24
- package/package.json +2 -2
- package/src/index.ts +46 -26
- package/test/cli.terminal.integration.test.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @eslint-config-snapshot/cli
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Minor release with improved CLI output consistency and faster workspace extraction using ESLint API fallback strategy.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @eslint-config-snapshot/api@0.4.0
|
|
13
|
+
|
|
14
|
+
## 0.3.2
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Fix deterministic aggregation when same-severity ESLint rule options differ across sampled files, preventing update crashes.
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- @eslint-config-snapshot/api@0.3.2
|
|
21
|
+
|
|
22
|
+
## 0.3.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Release patch bump after init UX clarity improvements for default catch-all group messaging.
|
|
27
|
+
- Updated dependencies
|
|
28
|
+
- @eslint-config-snapshot/api@0.3.1
|
|
29
|
+
|
|
3
30
|
## 0.3.0
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -124,7 +124,7 @@ function createProgram(cwd, onActionExit) {
|
|
|
124
124
|
Examples:
|
|
125
125
|
$ eslint-config-snapshot init
|
|
126
126
|
Runs interactive select prompts for target/preset.
|
|
127
|
-
Recommended preset
|
|
127
|
+
Recommended preset keeps a dynamic catch-all default group ("*") and asks only for static exception groups.
|
|
128
128
|
|
|
129
129
|
$ eslint-config-snapshot init --yes --target package-json --preset recommended --show-effective
|
|
130
130
|
Non-interactive recommended setup in package.json, with effective preview.
|
|
@@ -266,8 +266,13 @@ async function executeUpdate(cwd, printSummary) {
|
|
|
266
266
|
await writeSnapshots(cwd, currentSnapshots);
|
|
267
267
|
if (printSummary) {
|
|
268
268
|
const summary = summarizeSnapshots(currentSnapshots);
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
const color = createColorizer();
|
|
270
|
+
writeSectionTitle("Summary", color);
|
|
271
|
+
process.stdout.write(
|
|
272
|
+
`Baseline updated: ${summary.groups} groups, ${summary.rules} rules.
|
|
273
|
+
Severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off.
|
|
274
|
+
`
|
|
275
|
+
);
|
|
271
276
|
}
|
|
272
277
|
return 0;
|
|
273
278
|
}
|
|
@@ -317,19 +322,20 @@ async function computeCurrentSnapshots(cwd) {
|
|
|
317
322
|
const sampled = await (0, import_api.sampleWorkspaceFiles)(workspaceAbs, config.sampling);
|
|
318
323
|
let extractedCount = 0;
|
|
319
324
|
let lastExtractionError;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
325
|
+
const sampledAbs = sampled.map((sampledRel) => import_node_path.default.resolve(workspaceAbs, sampledRel));
|
|
326
|
+
const results = await (0, import_api.extractRulesForWorkspaceSamples)(workspaceAbs, sampledAbs);
|
|
327
|
+
for (const result of results) {
|
|
328
|
+
if (result.rules) {
|
|
329
|
+
extractedForGroup.push(result.rules);
|
|
324
330
|
extractedCount += 1;
|
|
325
|
-
|
|
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;
|
|
331
|
+
continue;
|
|
332
332
|
}
|
|
333
|
+
const message = result.error instanceof Error ? result.error.message : String(result.error);
|
|
334
|
+
if (isRecoverableExtractionError(message)) {
|
|
335
|
+
lastExtractionError = message;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
throw result.error ?? new Error(message);
|
|
333
339
|
}
|
|
334
340
|
if (extractedCount === 0) {
|
|
335
341
|
const context = lastExtractionError ? ` Last error: ${lastExtractionError}` : "";
|
|
@@ -343,6 +349,9 @@ async function computeCurrentSnapshots(cwd) {
|
|
|
343
349
|
}
|
|
344
350
|
return snapshots;
|
|
345
351
|
}
|
|
352
|
+
function isRecoverableExtractionError(message) {
|
|
353
|
+
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");
|
|
354
|
+
}
|
|
346
355
|
async function resolveWorkspaceAssignments(cwd, config) {
|
|
347
356
|
const discovery = await (0, import_api.discoverWorkspaces)({ cwd, workspaceInput: config.workspaceInput });
|
|
348
357
|
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: ["**/*"] }]);
|
|
@@ -484,7 +493,7 @@ async function askInitPreset(selectPrompt) {
|
|
|
484
493
|
return selectPrompt({
|
|
485
494
|
message: "Select preset",
|
|
486
495
|
choices: [
|
|
487
|
-
{ name: 'recommended (
|
|
496
|
+
{ name: 'recommended (dynamic catch-all "*" + optional static exceptions)', value: "recommended" },
|
|
488
497
|
{ name: "minimal", value: "minimal" },
|
|
489
498
|
{ name: "full", value: "full" }
|
|
490
499
|
]
|
|
@@ -634,9 +643,12 @@ function trimTrailingSlashes(value) {
|
|
|
634
643
|
}
|
|
635
644
|
async function askRecommendedGroupAssignments(workspaces) {
|
|
636
645
|
const { checkbox, select } = await import("@inquirer/prompts");
|
|
637
|
-
process.stdout.write(
|
|
646
|
+
process.stdout.write(
|
|
647
|
+
'Recommended setup: default group "*" is a dynamic catch-all for every discovered workspace.\n'
|
|
648
|
+
);
|
|
649
|
+
process.stdout.write("Select only workspaces that should move to explicit static groups.\n");
|
|
638
650
|
const overrides = await checkbox({
|
|
639
|
-
message:
|
|
651
|
+
message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
|
|
640
652
|
choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
|
|
641
653
|
pageSize: Math.min(12, Math.max(4, workspaces.length))
|
|
642
654
|
});
|
|
@@ -702,22 +714,29 @@ function printWhatChanged(changes, currentSnapshots) {
|
|
|
702
714
|
const changeSummary = summarizeChanges(changes);
|
|
703
715
|
if (changes.length === 0) {
|
|
704
716
|
process.stdout.write(color.green("Great news: no snapshot drift detected.\n"));
|
|
717
|
+
writeSectionTitle("Summary", color);
|
|
705
718
|
process.stdout.write(
|
|
706
|
-
|
|
719
|
+
`- baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
|
|
720
|
+
- severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
|
|
707
721
|
`
|
|
708
722
|
);
|
|
709
723
|
return 0;
|
|
710
724
|
}
|
|
711
725
|
process.stdout.write(color.red("Heads up: snapshot drift detected.\n"));
|
|
726
|
+
writeSectionTitle("Summary", color);
|
|
712
727
|
process.stdout.write(
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
728
|
+
`- changed groups: ${changes.length}
|
|
729
|
+
- introduced rules: ${changeSummary.introduced}
|
|
730
|
+
- removed rules: ${changeSummary.removed}
|
|
731
|
+
- severity changes: ${changeSummary.severity}
|
|
732
|
+
- options changes: ${changeSummary.options}
|
|
733
|
+
- workspace membership changes: ${changeSummary.workspace}
|
|
734
|
+
- current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
|
|
735
|
+
- current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
|
|
718
736
|
|
|
719
737
|
`
|
|
720
738
|
);
|
|
739
|
+
writeSectionTitle("Changes", color);
|
|
721
740
|
for (const change of changes) {
|
|
722
741
|
process.stdout.write(color.bold(`group ${change.groupId}
|
|
723
742
|
`));
|
|
@@ -732,6 +751,10 @@ function printWhatChanged(changes, currentSnapshots) {
|
|
|
732
751
|
writeSubtleInfo(UPDATE_HINT);
|
|
733
752
|
return 1;
|
|
734
753
|
}
|
|
754
|
+
function writeSectionTitle(title, color) {
|
|
755
|
+
process.stdout.write(`${color.bold(title)}
|
|
756
|
+
`);
|
|
757
|
+
}
|
|
735
758
|
function summarizeChanges(changes) {
|
|
736
759
|
let introduced = 0;
|
|
737
760
|
let removed = 0;
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
buildSnapshot,
|
|
8
8
|
diffSnapshots,
|
|
9
9
|
discoverWorkspaces,
|
|
10
|
-
|
|
10
|
+
extractRulesForWorkspaceSamples,
|
|
11
11
|
findConfigPath,
|
|
12
12
|
getConfigScaffold,
|
|
13
13
|
hasDiff,
|
|
@@ -104,7 +104,7 @@ function createProgram(cwd, onActionExit) {
|
|
|
104
104
|
Examples:
|
|
105
105
|
$ eslint-config-snapshot init
|
|
106
106
|
Runs interactive select prompts for target/preset.
|
|
107
|
-
Recommended preset
|
|
107
|
+
Recommended preset keeps a dynamic catch-all default group ("*") and asks only for static exception groups.
|
|
108
108
|
|
|
109
109
|
$ eslint-config-snapshot init --yes --target package-json --preset recommended --show-effective
|
|
110
110
|
Non-interactive recommended setup in package.json, with effective preview.
|
|
@@ -246,8 +246,13 @@ async function executeUpdate(cwd, printSummary) {
|
|
|
246
246
|
await writeSnapshots(cwd, currentSnapshots);
|
|
247
247
|
if (printSummary) {
|
|
248
248
|
const summary = summarizeSnapshots(currentSnapshots);
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
const color = createColorizer();
|
|
250
|
+
writeSectionTitle("Summary", color);
|
|
251
|
+
process.stdout.write(
|
|
252
|
+
`Baseline updated: ${summary.groups} groups, ${summary.rules} rules.
|
|
253
|
+
Severity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off.
|
|
254
|
+
`
|
|
255
|
+
);
|
|
251
256
|
}
|
|
252
257
|
return 0;
|
|
253
258
|
}
|
|
@@ -297,19 +302,20 @@ async function computeCurrentSnapshots(cwd) {
|
|
|
297
302
|
const sampled = await sampleWorkspaceFiles(workspaceAbs, config.sampling);
|
|
298
303
|
let extractedCount = 0;
|
|
299
304
|
let lastExtractionError;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
305
|
+
const sampledAbs = sampled.map((sampledRel) => path.resolve(workspaceAbs, sampledRel));
|
|
306
|
+
const results = await extractRulesForWorkspaceSamples(workspaceAbs, sampledAbs);
|
|
307
|
+
for (const result of results) {
|
|
308
|
+
if (result.rules) {
|
|
309
|
+
extractedForGroup.push(result.rules);
|
|
304
310
|
extractedCount += 1;
|
|
305
|
-
|
|
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;
|
|
311
|
+
continue;
|
|
312
312
|
}
|
|
313
|
+
const message = result.error instanceof Error ? result.error.message : String(result.error);
|
|
314
|
+
if (isRecoverableExtractionError(message)) {
|
|
315
|
+
lastExtractionError = message;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
throw result.error ?? new Error(message);
|
|
313
319
|
}
|
|
314
320
|
if (extractedCount === 0) {
|
|
315
321
|
const context = lastExtractionError ? ` Last error: ${lastExtractionError}` : "";
|
|
@@ -323,6 +329,9 @@ async function computeCurrentSnapshots(cwd) {
|
|
|
323
329
|
}
|
|
324
330
|
return snapshots;
|
|
325
331
|
}
|
|
332
|
+
function isRecoverableExtractionError(message) {
|
|
333
|
+
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");
|
|
334
|
+
}
|
|
326
335
|
async function resolveWorkspaceAssignments(cwd, config) {
|
|
327
336
|
const discovery = await discoverWorkspaces({ cwd, workspaceInput: config.workspaceInput });
|
|
328
337
|
const assignments = config.grouping.mode === "standalone" ? discovery.workspacesRel.map((workspace) => ({ name: workspace, workspaces: [workspace] })) : assignGroupsByMatch(discovery.workspacesRel, config.grouping.groups ?? [{ name: "default", match: ["**/*"] }]);
|
|
@@ -464,7 +473,7 @@ async function askInitPreset(selectPrompt) {
|
|
|
464
473
|
return selectPrompt({
|
|
465
474
|
message: "Select preset",
|
|
466
475
|
choices: [
|
|
467
|
-
{ name: 'recommended (
|
|
476
|
+
{ name: 'recommended (dynamic catch-all "*" + optional static exceptions)', value: "recommended" },
|
|
468
477
|
{ name: "minimal", value: "minimal" },
|
|
469
478
|
{ name: "full", value: "full" }
|
|
470
479
|
]
|
|
@@ -614,9 +623,12 @@ function trimTrailingSlashes(value) {
|
|
|
614
623
|
}
|
|
615
624
|
async function askRecommendedGroupAssignments(workspaces) {
|
|
616
625
|
const { checkbox, select } = await import("@inquirer/prompts");
|
|
617
|
-
process.stdout.write(
|
|
626
|
+
process.stdout.write(
|
|
627
|
+
'Recommended setup: default group "*" is a dynamic catch-all for every discovered workspace.\n'
|
|
628
|
+
);
|
|
629
|
+
process.stdout.write("Select only workspaces that should move to explicit static groups.\n");
|
|
618
630
|
const overrides = await checkbox({
|
|
619
|
-
message:
|
|
631
|
+
message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
|
|
620
632
|
choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
|
|
621
633
|
pageSize: Math.min(12, Math.max(4, workspaces.length))
|
|
622
634
|
});
|
|
@@ -682,22 +694,29 @@ function printWhatChanged(changes, currentSnapshots) {
|
|
|
682
694
|
const changeSummary = summarizeChanges(changes);
|
|
683
695
|
if (changes.length === 0) {
|
|
684
696
|
process.stdout.write(color.green("Great news: no snapshot drift detected.\n"));
|
|
697
|
+
writeSectionTitle("Summary", color);
|
|
685
698
|
process.stdout.write(
|
|
686
|
-
|
|
699
|
+
`- baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
|
|
700
|
+
- severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
|
|
687
701
|
`
|
|
688
702
|
);
|
|
689
703
|
return 0;
|
|
690
704
|
}
|
|
691
705
|
process.stdout.write(color.red("Heads up: snapshot drift detected.\n"));
|
|
706
|
+
writeSectionTitle("Summary", color);
|
|
692
707
|
process.stdout.write(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
708
|
+
`- changed groups: ${changes.length}
|
|
709
|
+
- introduced rules: ${changeSummary.introduced}
|
|
710
|
+
- removed rules: ${changeSummary.removed}
|
|
711
|
+
- severity changes: ${changeSummary.severity}
|
|
712
|
+
- options changes: ${changeSummary.options}
|
|
713
|
+
- workspace membership changes: ${changeSummary.workspace}
|
|
714
|
+
- current baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules
|
|
715
|
+
- current severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off
|
|
698
716
|
|
|
699
717
|
`
|
|
700
718
|
);
|
|
719
|
+
writeSectionTitle("Changes", color);
|
|
701
720
|
for (const change of changes) {
|
|
702
721
|
process.stdout.write(color.bold(`group ${change.groupId}
|
|
703
722
|
`));
|
|
@@ -712,6 +731,10 @@ function printWhatChanged(changes, currentSnapshots) {
|
|
|
712
731
|
writeSubtleInfo(UPDATE_HINT);
|
|
713
732
|
return 1;
|
|
714
733
|
}
|
|
734
|
+
function writeSectionTitle(title, color) {
|
|
735
|
+
process.stdout.write(`${color.bold(title)}
|
|
736
|
+
`);
|
|
737
|
+
}
|
|
715
738
|
function summarizeChanges(changes) {
|
|
716
739
|
let introduced = 0;
|
|
717
740
|
let removed = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eslint-config-snapshot/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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.4.0"
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
buildSnapshot,
|
|
6
6
|
diffSnapshots,
|
|
7
7
|
discoverWorkspaces,
|
|
8
|
-
|
|
8
|
+
extractRulesForWorkspaceSamples,
|
|
9
9
|
findConfigPath,
|
|
10
10
|
getConfigScaffold,
|
|
11
11
|
hasDiff,
|
|
@@ -164,7 +164,7 @@ function createProgram(cwd: string, onActionExit: (code: number) => void): Comma
|
|
|
164
164
|
Examples:
|
|
165
165
|
$ eslint-config-snapshot init
|
|
166
166
|
Runs interactive select prompts for target/preset.
|
|
167
|
-
Recommended preset
|
|
167
|
+
Recommended preset keeps a dynamic catch-all default group ("*") and asks only for static exception groups.
|
|
168
168
|
|
|
169
169
|
$ eslint-config-snapshot init --yes --target package-json --preset recommended --show-effective
|
|
170
170
|
Non-interactive recommended setup in package.json, with effective preview.
|
|
@@ -340,7 +340,11 @@ async function executeUpdate(cwd: string, printSummary: boolean): Promise<number
|
|
|
340
340
|
|
|
341
341
|
if (printSummary) {
|
|
342
342
|
const summary = summarizeSnapshots(currentSnapshots)
|
|
343
|
-
|
|
343
|
+
const color = createColorizer()
|
|
344
|
+
writeSectionTitle('Summary', color)
|
|
345
|
+
process.stdout.write(
|
|
346
|
+
`Baseline updated: ${summary.groups} groups, ${summary.rules} rules.\nSeverity mix: ${summary.error} errors, ${summary.warn} warnings, ${summary.off} off.\n`
|
|
347
|
+
)
|
|
344
348
|
}
|
|
345
349
|
|
|
346
350
|
return 0
|
|
@@ -400,23 +404,23 @@ async function computeCurrentSnapshots(cwd: string): Promise<Map<string, BuiltSn
|
|
|
400
404
|
let extractedCount = 0
|
|
401
405
|
let lastExtractionError: string | undefined
|
|
402
406
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
+
const sampledAbs = sampled.map((sampledRel) => path.resolve(workspaceAbs, sampledRel))
|
|
408
|
+
const results = await extractRulesForWorkspaceSamples(workspaceAbs, sampledAbs)
|
|
409
|
+
|
|
410
|
+
for (const result of results) {
|
|
411
|
+
if (result.rules) {
|
|
412
|
+
extractedForGroup.push(result.rules)
|
|
407
413
|
extractedCount += 1
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
continue
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
throw error
|
|
414
|
+
continue
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const message = result.error instanceof Error ? result.error.message : String(result.error)
|
|
418
|
+
if (isRecoverableExtractionError(message)) {
|
|
419
|
+
lastExtractionError = message
|
|
420
|
+
continue
|
|
419
421
|
}
|
|
422
|
+
|
|
423
|
+
throw result.error ?? new Error(message)
|
|
420
424
|
}
|
|
421
425
|
|
|
422
426
|
if (extractedCount === 0) {
|
|
@@ -434,6 +438,15 @@ async function computeCurrentSnapshots(cwd: string): Promise<Map<string, BuiltSn
|
|
|
434
438
|
return snapshots
|
|
435
439
|
}
|
|
436
440
|
|
|
441
|
+
function isRecoverableExtractionError(message: string): boolean {
|
|
442
|
+
return (
|
|
443
|
+
message.startsWith('Invalid JSON from eslint --print-config') ||
|
|
444
|
+
message.startsWith('Empty ESLint print-config output') ||
|
|
445
|
+
message.includes('File ignored because of a matching ignore pattern') ||
|
|
446
|
+
message.includes('File ignored by default')
|
|
447
|
+
)
|
|
448
|
+
}
|
|
449
|
+
|
|
437
450
|
async function resolveWorkspaceAssignments(cwd: string, config: Awaited<ReturnType<typeof loadConfig>>) {
|
|
438
451
|
const discovery = await discoverWorkspaces({ cwd, workspaceInput: config.workspaceInput })
|
|
439
452
|
|
|
@@ -617,7 +630,7 @@ async function askInitPreset(
|
|
|
617
630
|
return selectPrompt({
|
|
618
631
|
message: 'Select preset',
|
|
619
632
|
choices: [
|
|
620
|
-
{ name: 'recommended (
|
|
633
|
+
{ name: 'recommended (dynamic catch-all "*" + optional static exceptions)', value: 'recommended' },
|
|
621
634
|
{ name: 'minimal', value: 'minimal' },
|
|
622
635
|
{ name: 'full', value: 'full' }
|
|
623
636
|
]
|
|
@@ -798,9 +811,12 @@ function trimTrailingSlashes(value: string): string {
|
|
|
798
811
|
|
|
799
812
|
async function askRecommendedGroupAssignments(workspaces: string[]): Promise<Map<string, number>> {
|
|
800
813
|
const { checkbox, select } = await import('@inquirer/prompts')
|
|
801
|
-
process.stdout.write(
|
|
814
|
+
process.stdout.write(
|
|
815
|
+
'Recommended setup: default group "*" is a dynamic catch-all for every discovered workspace.\n'
|
|
816
|
+
)
|
|
817
|
+
process.stdout.write('Select only workspaces that should move to explicit static groups.\n')
|
|
802
818
|
const overrides = await checkbox<string>({
|
|
803
|
-
message: '
|
|
819
|
+
message: 'Choose exception workspaces (leave empty to keep all in default "*"):',
|
|
804
820
|
choices: workspaces.map((workspace) => ({ name: workspace, value: workspace })),
|
|
805
821
|
pageSize: Math.min(12, Math.max(4, workspaces.length))
|
|
806
822
|
})
|
|
@@ -877,20 +893,20 @@ function printWhatChanged(changes: Array<{ groupId: string; diff: SnapshotDiff }
|
|
|
877
893
|
|
|
878
894
|
if (changes.length === 0) {
|
|
879
895
|
process.stdout.write(color.green('Great news: no snapshot drift detected.\n'))
|
|
896
|
+
writeSectionTitle('Summary', color)
|
|
880
897
|
process.stdout.write(
|
|
881
|
-
|
|
898
|
+
`- baseline: ${currentSummary.groups} groups, ${currentSummary.rules} rules\n- severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off\n`
|
|
882
899
|
)
|
|
883
900
|
return 0
|
|
884
901
|
}
|
|
885
902
|
|
|
886
903
|
process.stdout.write(color.red('Heads up: snapshot drift detected.\n'))
|
|
904
|
+
writeSectionTitle('Summary', color)
|
|
887
905
|
process.stdout.write(
|
|
888
|
-
|
|
889
|
-
)
|
|
890
|
-
process.stdout.write(
|
|
891
|
-
`Current rules: ${currentSummary.rules} (severity mix: ${currentSummary.error} errors, ${currentSummary.warn} warnings, ${currentSummary.off} off)\n\n`
|
|
906
|
+
`- 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\n`
|
|
892
907
|
)
|
|
893
908
|
|
|
909
|
+
writeSectionTitle('Changes', color)
|
|
894
910
|
for (const change of changes) {
|
|
895
911
|
process.stdout.write(color.bold(`group ${change.groupId}\n`))
|
|
896
912
|
const lines = formatDiff(change.groupId, change.diff).split('\n').slice(1)
|
|
@@ -905,6 +921,10 @@ function printWhatChanged(changes: Array<{ groupId: string; diff: SnapshotDiff }
|
|
|
905
921
|
return 1
|
|
906
922
|
}
|
|
907
923
|
|
|
924
|
+
function writeSectionTitle(title: string, color: ReturnType<typeof createColorizer>): void {
|
|
925
|
+
process.stdout.write(`${color.bold(title)}\n`)
|
|
926
|
+
}
|
|
927
|
+
|
|
908
928
|
function summarizeChanges(changes: Array<{ groupId: string; diff: SnapshotDiff }>) {
|
|
909
929
|
let introduced = 0
|
|
910
930
|
let removed = 0
|
|
@@ -389,7 +389,7 @@ no-debugger: off
|
|
|
389
389
|
expect(result.stdout).toContain('Initialize config (file or package.json)')
|
|
390
390
|
expect(result.stdout).toContain('-f, --force')
|
|
391
391
|
expect(result.stdout).toContain('Runs interactive select prompts for target/preset.')
|
|
392
|
-
expect(result.stdout).toContain('Recommended preset
|
|
392
|
+
expect(result.stdout).toContain('Recommended preset keeps a dynamic catch-all default group ("*")')
|
|
393
393
|
expect(result.stdout).toContain('--show-effective')
|
|
394
394
|
expect(result.stdout).toContain('--yes --force --target file --preset full')
|
|
395
395
|
expect(result.stderr).toBe('')
|