@utoo/lint 0.2.2 → 0.2.4
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/README.md +20 -0
- package/index.cjs +103 -21
- package/index.d.ts +1 -0
- package/index.js +103 -21
- package/lib/config-loader.cjs +15 -7
- package/lib/config-loader.js +15 -7
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -41,6 +41,11 @@ Use machine-readable output:
|
|
|
41
41
|
pnpm exec utoo-lint --format=json src
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
The default terminal output groups diagnostics by file, aligns their location
|
|
45
|
+
and severity, and summarizes errors and warnings separately. Color is detected
|
|
46
|
+
automatically; pass `--color` to force it or `--no-color` to disable it. JSON
|
|
47
|
+
output remains stable for editor and CI integrations.
|
|
48
|
+
|
|
44
49
|
## Autofix
|
|
45
50
|
|
|
46
51
|
Apply safe fixes from all enabled rules that support autofix:
|
|
@@ -64,6 +69,21 @@ lists autofix coverage by rule.
|
|
|
64
69
|
|
|
65
70
|
## Configuration
|
|
66
71
|
|
|
72
|
+
`utoo-lint` supports two canonical project config formats:
|
|
73
|
+
|
|
74
|
+
| Config | Use it when | Raw native binary |
|
|
75
|
+
| --- | --- | --- |
|
|
76
|
+
| `utlint.config.ts` | You want typed authoring, imports, or computed values. | No |
|
|
77
|
+
| `utlint.config.json` | You want a static, runtime-independent config. | Yes |
|
|
78
|
+
|
|
79
|
+
The npm CLI discovers either format automatically. It can also select one
|
|
80
|
+
explicitly:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pnpm exec utoo-lint --config=utlint.config.ts src
|
|
84
|
+
pnpm exec utoo-lint --config=utlint.config.json src
|
|
85
|
+
```
|
|
86
|
+
|
|
67
87
|
For a static config readable by both the npm CLI and raw native binary, create
|
|
68
88
|
`utlint.config.json`:
|
|
69
89
|
|
package/index.cjs
CHANGED
|
@@ -484,7 +484,7 @@ class UtooLint {
|
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
async lintFiles(patterns, options = {}) {
|
|
487
|
-
const mergedOptions = mergeLintOptions(this.options, options);
|
|
487
|
+
const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
|
|
488
488
|
const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
|
|
489
489
|
const customRuleFilterMap = customRuleMapForConfig(customRuleConfig, new Map());
|
|
490
490
|
const nativeOptions = lintOptionsWithoutCustomRules(mergedOptions, customRuleFilterMap);
|
|
@@ -504,7 +504,7 @@ class UtooLint {
|
|
|
504
504
|
throw new TypeError("code must be a string");
|
|
505
505
|
}
|
|
506
506
|
|
|
507
|
-
const mergedOptions = mergeLintOptions(this.options, options);
|
|
507
|
+
const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
|
|
508
508
|
const filePath = normalizeESLintFilePath(textFilePathForOptions(options, "<text>"), mergedOptions.cwd);
|
|
509
509
|
const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
|
|
510
510
|
const customRuleMap = customRuleMapForConfig(customRuleConfig, new Map(), filePath, mergedOptions.cwd);
|
|
@@ -528,7 +528,7 @@ class UtooLint {
|
|
|
528
528
|
}
|
|
529
529
|
|
|
530
530
|
async calculateConfigForFile(filePath) {
|
|
531
|
-
return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
|
|
531
|
+
return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
|
|
532
532
|
}
|
|
533
533
|
|
|
534
534
|
async findConfigFile(filePath) {
|
|
@@ -610,7 +610,7 @@ class CLIEngine {
|
|
|
610
610
|
}
|
|
611
611
|
|
|
612
612
|
executeOnFiles(patterns) {
|
|
613
|
-
const mergedOptions = eslintConstructorOptions(this.options);
|
|
613
|
+
const mergedOptions = withConfigCache(eslintConstructorOptions(this.options));
|
|
614
614
|
const report = lintFiles(patterns, mergedOptions);
|
|
615
615
|
throwOnUnmatchedPatternDiagnostics(report, mergedOptions);
|
|
616
616
|
const results = maybeFilterQuietResults(reportToESLintResults(report, {
|
|
@@ -630,10 +630,10 @@ class CLIEngine {
|
|
|
630
630
|
typeof filePathOrOptions === "object" && filePathOrOptions !== null
|
|
631
631
|
? filePathOrOptions.filePath ?? filePathOrOptions.filename ?? "input.js"
|
|
632
632
|
: filePathOrOptions;
|
|
633
|
-
const mergedOptions = {
|
|
633
|
+
const mergedOptions = withConfigCache({
|
|
634
634
|
...eslintConstructorOptions(this.options),
|
|
635
635
|
...textOptions
|
|
636
|
-
};
|
|
636
|
+
});
|
|
637
637
|
const report = lintText(code, {
|
|
638
638
|
...mergedOptions,
|
|
639
639
|
filePath
|
|
@@ -670,7 +670,7 @@ class CLIEngine {
|
|
|
670
670
|
}
|
|
671
671
|
|
|
672
672
|
getConfigForFile(filePath) {
|
|
673
|
-
return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
|
|
673
|
+
return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
|
|
674
674
|
}
|
|
675
675
|
}
|
|
676
676
|
|
|
@@ -747,6 +747,14 @@ function parseUtooLintCliArgs(args = []) {
|
|
|
747
747
|
passthroughArgs.push(arg);
|
|
748
748
|
continue;
|
|
749
749
|
}
|
|
750
|
+
if (arg === "--color") {
|
|
751
|
+
options.color = true;
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
if (arg === "--no-color") {
|
|
755
|
+
options.color = false;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
750
758
|
if (arg.startsWith("-")) {
|
|
751
759
|
passthroughArgs.push(arg);
|
|
752
760
|
continue;
|
|
@@ -812,12 +820,12 @@ function runCli(args = [], options = {}) {
|
|
|
812
820
|
return run(parsed.passthroughArgs, options);
|
|
813
821
|
}
|
|
814
822
|
|
|
815
|
-
const cliOptions = {
|
|
823
|
+
const cliOptions = withConfigCache({
|
|
816
824
|
...options,
|
|
817
825
|
...parsed.options,
|
|
818
826
|
extraArgs: parsed.passthroughArgs,
|
|
819
827
|
preserveNativeDefaults: Boolean(parsed.options.noConfig)
|
|
820
|
-
};
|
|
828
|
+
});
|
|
821
829
|
const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
|
|
822
830
|
const report = lintFiles(targets, cliOptions);
|
|
823
831
|
report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
|
|
@@ -826,7 +834,7 @@ function runCli(args = [], options = {}) {
|
|
|
826
834
|
if (cliOptions.format === "json" || parsed.passthroughArgs.includes("--json")) {
|
|
827
835
|
return completedRunResult(status, `${JSON.stringify(publicCliReport(report))}\n`, report.stderr ?? "", options);
|
|
828
836
|
}
|
|
829
|
-
return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report)}`, options);
|
|
837
|
+
return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report, cliOptions)}`, options);
|
|
830
838
|
}
|
|
831
839
|
|
|
832
840
|
function publicCliReport(report) {
|
|
@@ -838,18 +846,82 @@ function publicCliReport(report) {
|
|
|
838
846
|
};
|
|
839
847
|
}
|
|
840
848
|
|
|
841
|
-
function formatNativeTextReport(report) {
|
|
849
|
+
function formatNativeTextReport(report, options = {}) {
|
|
850
|
+
const diagnostics = report.diagnostics ?? [];
|
|
851
|
+
const files = report.files ?? 0;
|
|
852
|
+
const color = shouldUseColor(options);
|
|
853
|
+
|
|
854
|
+
if (diagnostics.length === 0) {
|
|
855
|
+
return `${paint(color, "\u001b[32m", "✓")} ${pluralize(files, "file")} checked, no problems found\n`;
|
|
856
|
+
}
|
|
857
|
+
|
|
842
858
|
const lines = [];
|
|
843
|
-
|
|
844
|
-
|
|
859
|
+
const locationWidth = diagnostics.reduce(
|
|
860
|
+
(width, diagnostic) => Math.max(width, diagnosticLocation(diagnostic).length),
|
|
861
|
+
0
|
|
862
|
+
);
|
|
863
|
+
let currentFile;
|
|
864
|
+
|
|
865
|
+
for (const diagnostic of diagnostics) {
|
|
866
|
+
const filePath = diagnostic.filePath ?? "<unknown>";
|
|
867
|
+
if (filePath !== currentFile) {
|
|
868
|
+
if (lines.length > 0) lines.push("");
|
|
869
|
+
lines.push(paint(color, "\u001b[1m", filePath));
|
|
870
|
+
currentFile = filePath;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const location = diagnosticLocation(diagnostic).padStart(locationWidth);
|
|
874
|
+
const severity = diagnostic.severity === "warning" || diagnostic.severity === 1 ? "warning" : "error";
|
|
875
|
+
const severityColor = severity === "error" ? "\u001b[31m" : "\u001b[33m";
|
|
876
|
+
const message = String(diagnostic.message ?? "").replace(/\s*[\r\n]+\s*/g, " ");
|
|
877
|
+
const rule = diagnostic.ruleId ? ` ${paint(color, "\u001b[2m", diagnostic.ruleId)}` : "";
|
|
845
878
|
lines.push(
|
|
846
|
-
|
|
879
|
+
` ${paint(color, "\u001b[2m", location)} ${paint(color, severityColor, severity.padEnd(7))} ${message}${rule}`
|
|
847
880
|
);
|
|
848
881
|
}
|
|
849
|
-
|
|
882
|
+
|
|
883
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error" || diagnostic.severity === 2).length;
|
|
884
|
+
const warnings = diagnostics.length - errors;
|
|
885
|
+
const markerColor = errors > 0 ? "\u001b[31m" : "\u001b[33m";
|
|
886
|
+
lines.push("");
|
|
887
|
+
lines.push(
|
|
888
|
+
`${paint(color, markerColor, "✖")} ${pluralize(diagnostics.length, "problem")} (${pluralize(errors, "error")}, ${pluralize(warnings, "warning")})`
|
|
889
|
+
);
|
|
890
|
+
|
|
891
|
+
const fixable = diagnostics.filter(diagnosticIsFixable).length;
|
|
892
|
+
const fixRequested = options.fix || options.extraArgs?.includes("--fix") || options.extraArgs?.includes("--fix-dry-run");
|
|
893
|
+
if (fixable > 0 && !fixRequested) {
|
|
894
|
+
lines.push(` ${pluralize(fixable, "problem")} potentially fixable with the \`--fix\` option.`);
|
|
895
|
+
}
|
|
896
|
+
lines.push(` ${pluralize(files, "file")} checked`);
|
|
850
897
|
return `${lines.join("\n")}\n`;
|
|
851
898
|
}
|
|
852
899
|
|
|
900
|
+
function diagnosticLocation(diagnostic) {
|
|
901
|
+
return `${diagnostic.line ?? 0}:${diagnostic.column ?? 0}`;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
function diagnosticIsFixable(diagnostic) {
|
|
905
|
+
return Boolean(diagnostic.fix) || (Array.isArray(diagnostic.fixes) && diagnostic.fixes.length > 0);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function pluralize(count, noun) {
|
|
909
|
+
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function paint(enabled, code, text) {
|
|
913
|
+
return enabled ? `${code}${text}\u001b[0m` : text;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
function shouldUseColor(options = {}) {
|
|
917
|
+
if (options.color === true) return true;
|
|
918
|
+
if (options.color === false) return false;
|
|
919
|
+
const env = options.env ? { ...process.env, ...options.env } : process.env;
|
|
920
|
+
if (env.NO_COLOR !== undefined || env.NODE_DISABLE_COLORS !== undefined) return false;
|
|
921
|
+
if ((env.FORCE_COLOR && env.FORCE_COLOR !== "0") || (env.CLICOLOR_FORCE && env.CLICOLOR_FORCE !== "0")) return true;
|
|
922
|
+
return Boolean(process.stderr.isTTY);
|
|
923
|
+
}
|
|
924
|
+
|
|
853
925
|
function runFishlint(args = [], options = {}) {
|
|
854
926
|
const cliArgs = normalizeStringArray(args, "args");
|
|
855
927
|
const env = options.env ? { ...process.env, ...options.env } : { ...process.env };
|
|
@@ -1063,6 +1135,7 @@ function booleanFlagValue(arg) {
|
|
|
1063
1135
|
}
|
|
1064
1136
|
|
|
1065
1137
|
function lintFiles(paths, options = {}) {
|
|
1138
|
+
options = withConfigCache(options);
|
|
1066
1139
|
const targets = lintTargetsForInput(paths, options);
|
|
1067
1140
|
const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
|
|
1068
1141
|
const lintPaths = filteredLintPaths(targets, options);
|
|
@@ -1241,6 +1314,7 @@ function mergeNativeReports(reports) {
|
|
|
1241
1314
|
}
|
|
1242
1315
|
|
|
1243
1316
|
function lintText(code, options = {}) {
|
|
1317
|
+
options = withConfigCache(options);
|
|
1244
1318
|
if (typeof code !== "string") {
|
|
1245
1319
|
throw new TypeError("code must be a string");
|
|
1246
1320
|
}
|
|
@@ -1307,7 +1381,7 @@ function buildNativeLintArgs(paths, options) {
|
|
|
1307
1381
|
if (!options.rules && !options.forceMaterializedConfig && (options.config || options.baseConfig || options.overrideConfig)) {
|
|
1308
1382
|
const configForRuleSelection = options.noConfig
|
|
1309
1383
|
? undefined
|
|
1310
|
-
: options.config ? readConfig(options.config, options.cwd) : undefined;
|
|
1384
|
+
: options.config ? readConfig(options.config, options.cwd, options.configCache) : undefined;
|
|
1311
1385
|
const enabledRules = enabledRuleNamesFromConfigs(
|
|
1312
1386
|
configForRuleSelection,
|
|
1313
1387
|
options.baseConfig,
|
|
@@ -1353,6 +1427,12 @@ function mergeLintOptions(base, override) {
|
|
|
1353
1427
|
};
|
|
1354
1428
|
}
|
|
1355
1429
|
|
|
1430
|
+
function withConfigCache(options) {
|
|
1431
|
+
// Executable configs are otherwise re-evaluated at every per-file lookup.
|
|
1432
|
+
// Keep the cache on the options object so one public invocation shares it.
|
|
1433
|
+
return options.configCache ? options : { ...options, configCache: new Map() };
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1356
1436
|
function eslintConstructorOptions(options) {
|
|
1357
1437
|
const mapped = {
|
|
1358
1438
|
cwd: options.cwd,
|
|
@@ -1525,7 +1605,7 @@ function rulesFromFileConfig(options, filePath) {
|
|
|
1525
1605
|
return {};
|
|
1526
1606
|
}
|
|
1527
1607
|
|
|
1528
|
-
const config = readConfig(configPath, options.cwd);
|
|
1608
|
+
const config = readConfig(configPath, options.cwd, options.configCache);
|
|
1529
1609
|
return rulesFromConfig(config, filePath, dirname(configPath));
|
|
1530
1610
|
}
|
|
1531
1611
|
|
|
@@ -1539,7 +1619,7 @@ function configDataFromFileConfig(options, filePath) {
|
|
|
1539
1619
|
return {};
|
|
1540
1620
|
}
|
|
1541
1621
|
|
|
1542
|
-
const config = readConfig(configPath, options.cwd);
|
|
1622
|
+
const config = readConfig(configPath, options.cwd, options.configCache);
|
|
1543
1623
|
return configDataFromConfig(config, filePath, dirname(configPath));
|
|
1544
1624
|
}
|
|
1545
1625
|
|
|
@@ -1560,7 +1640,7 @@ function defaultLintTargets(options = {}) {
|
|
|
1560
1640
|
return ["."];
|
|
1561
1641
|
}
|
|
1562
1642
|
|
|
1563
|
-
const targets = filePatternsFromConfig(readConfig(configPath, options.cwd));
|
|
1643
|
+
const targets = filePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache));
|
|
1564
1644
|
return targets.length > 0
|
|
1565
1645
|
? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
|
|
1566
1646
|
: ["."];
|
|
@@ -1652,7 +1732,9 @@ function configPathFromDirectory(directory) {
|
|
|
1652
1732
|
function withTemporaryConfig(options, callback) {
|
|
1653
1733
|
const fileConfigPath = !options.noConfig ? configPathForOptions(options) : undefined;
|
|
1654
1734
|
const shouldMaterializeFileConfig = Boolean(fileConfigPath);
|
|
1655
|
-
const fileConfig = shouldMaterializeFileConfig
|
|
1735
|
+
const fileConfig = shouldMaterializeFileConfig
|
|
1736
|
+
? readConfig(fileConfigPath, options.cwd, options.configCache)
|
|
1737
|
+
: undefined;
|
|
1656
1738
|
const configs = [options.baseConfig, fileConfig, options.overrideConfig];
|
|
1657
1739
|
const rules = shouldMaterializeFileConfig
|
|
1658
1740
|
? materializedRulesFromConfigs(...configs)
|
|
@@ -4012,7 +4094,7 @@ function ignorePatternsFromFileConfig(options, filePath) {
|
|
|
4012
4094
|
}
|
|
4013
4095
|
|
|
4014
4096
|
const cwd = options.cwd ?? process.cwd();
|
|
4015
|
-
return ignorePatternsFromConfig(readConfig(configPath, options.cwd)).map((pattern) =>
|
|
4097
|
+
return ignorePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache)).map((pattern) =>
|
|
4016
4098
|
rebaseConfigPattern(pattern, dirname(configPath), cwd)
|
|
4017
4099
|
);
|
|
4018
4100
|
}
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -487,7 +487,7 @@ export class UtooLint {
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
async lintFiles(patterns, options = {}) {
|
|
490
|
-
const mergedOptions = mergeLintOptions(this.options, options);
|
|
490
|
+
const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
|
|
491
491
|
const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
|
|
492
492
|
const customRuleFilterMap = customRuleMapForConfig(customRuleConfig, new Map());
|
|
493
493
|
const nativeOptions = lintOptionsWithoutCustomRules(mergedOptions, customRuleFilterMap);
|
|
@@ -507,7 +507,7 @@ export class UtooLint {
|
|
|
507
507
|
throw new TypeError("code must be a string");
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
const mergedOptions = mergeLintOptions(this.options, options);
|
|
510
|
+
const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
|
|
511
511
|
const filePath = normalizeESLintFilePath(textFilePathForOptions(options, "<text>"), mergedOptions.cwd);
|
|
512
512
|
const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
|
|
513
513
|
const customRuleMap = customRuleMapForConfig(customRuleConfig, new Map(), filePath, mergedOptions.cwd);
|
|
@@ -531,7 +531,7 @@ export class UtooLint {
|
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
async calculateConfigForFile(filePath) {
|
|
534
|
-
return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
|
|
534
|
+
return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
async findConfigFile(filePath) {
|
|
@@ -2446,7 +2446,7 @@ export class CLIEngine {
|
|
|
2446
2446
|
}
|
|
2447
2447
|
|
|
2448
2448
|
executeOnFiles(patterns) {
|
|
2449
|
-
const mergedOptions = eslintConstructorOptions(this.options);
|
|
2449
|
+
const mergedOptions = withConfigCache(eslintConstructorOptions(this.options));
|
|
2450
2450
|
const report = lintFiles(patterns, mergedOptions);
|
|
2451
2451
|
throwOnUnmatchedPatternDiagnostics(report, mergedOptions);
|
|
2452
2452
|
const results = maybeFilterQuietResults(reportToESLintResults(report, {
|
|
@@ -2466,10 +2466,10 @@ export class CLIEngine {
|
|
|
2466
2466
|
typeof filePathOrOptions === "object" && filePathOrOptions !== null
|
|
2467
2467
|
? filePathOrOptions.filePath ?? filePathOrOptions.filename ?? "input.js"
|
|
2468
2468
|
: filePathOrOptions;
|
|
2469
|
-
const mergedOptions = {
|
|
2469
|
+
const mergedOptions = withConfigCache({
|
|
2470
2470
|
...eslintConstructorOptions(this.options),
|
|
2471
2471
|
...textOptions
|
|
2472
|
-
};
|
|
2472
|
+
});
|
|
2473
2473
|
const report = lintText(code, {
|
|
2474
2474
|
...mergedOptions,
|
|
2475
2475
|
filePath
|
|
@@ -2506,7 +2506,7 @@ export class CLIEngine {
|
|
|
2506
2506
|
}
|
|
2507
2507
|
|
|
2508
2508
|
getConfigForFile(filePath) {
|
|
2509
|
-
return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
|
|
2509
|
+
return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
|
|
2510
2510
|
}
|
|
2511
2511
|
}
|
|
2512
2512
|
|
|
@@ -2583,6 +2583,14 @@ function parseUtooLintCliArgs(args = []) {
|
|
|
2583
2583
|
passthroughArgs.push(arg);
|
|
2584
2584
|
continue;
|
|
2585
2585
|
}
|
|
2586
|
+
if (arg === "--color") {
|
|
2587
|
+
options.color = true;
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
if (arg === "--no-color") {
|
|
2591
|
+
options.color = false;
|
|
2592
|
+
continue;
|
|
2593
|
+
}
|
|
2586
2594
|
if (arg.startsWith("-")) {
|
|
2587
2595
|
passthroughArgs.push(arg);
|
|
2588
2596
|
continue;
|
|
@@ -2648,12 +2656,12 @@ export function runCli(args = [], options = {}) {
|
|
|
2648
2656
|
return run(parsed.passthroughArgs, options);
|
|
2649
2657
|
}
|
|
2650
2658
|
|
|
2651
|
-
const cliOptions = {
|
|
2659
|
+
const cliOptions = withConfigCache({
|
|
2652
2660
|
...options,
|
|
2653
2661
|
...parsed.options,
|
|
2654
2662
|
extraArgs: parsed.passthroughArgs,
|
|
2655
2663
|
preserveNativeDefaults: Boolean(parsed.options.noConfig)
|
|
2656
|
-
};
|
|
2664
|
+
});
|
|
2657
2665
|
const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
|
|
2658
2666
|
const report = lintFiles(targets, cliOptions);
|
|
2659
2667
|
report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
|
|
@@ -2662,7 +2670,7 @@ export function runCli(args = [], options = {}) {
|
|
|
2662
2670
|
if (cliOptions.format === "json" || parsed.passthroughArgs.includes("--json")) {
|
|
2663
2671
|
return completedRunResult(status, `${JSON.stringify(publicCliReport(report))}\n`, report.stderr ?? "", options);
|
|
2664
2672
|
}
|
|
2665
|
-
return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report)}`, options);
|
|
2673
|
+
return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report, cliOptions)}`, options);
|
|
2666
2674
|
}
|
|
2667
2675
|
|
|
2668
2676
|
function publicCliReport(report) {
|
|
@@ -2674,18 +2682,82 @@ function publicCliReport(report) {
|
|
|
2674
2682
|
};
|
|
2675
2683
|
}
|
|
2676
2684
|
|
|
2677
|
-
function formatNativeTextReport(report) {
|
|
2685
|
+
function formatNativeTextReport(report, options = {}) {
|
|
2686
|
+
const diagnostics = report.diagnostics ?? [];
|
|
2687
|
+
const files = report.files ?? 0;
|
|
2688
|
+
const color = shouldUseColor(options);
|
|
2689
|
+
|
|
2690
|
+
if (diagnostics.length === 0) {
|
|
2691
|
+
return `${paint(color, "\u001b[32m", "✓")} ${pluralize(files, "file")} checked, no problems found\n`;
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2678
2694
|
const lines = [];
|
|
2679
|
-
|
|
2680
|
-
|
|
2695
|
+
const locationWidth = diagnostics.reduce(
|
|
2696
|
+
(width, diagnostic) => Math.max(width, diagnosticLocation(diagnostic).length),
|
|
2697
|
+
0
|
|
2698
|
+
);
|
|
2699
|
+
let currentFile;
|
|
2700
|
+
|
|
2701
|
+
for (const diagnostic of diagnostics) {
|
|
2702
|
+
const filePath = diagnostic.filePath ?? "<unknown>";
|
|
2703
|
+
if (filePath !== currentFile) {
|
|
2704
|
+
if (lines.length > 0) lines.push("");
|
|
2705
|
+
lines.push(paint(color, "\u001b[1m", filePath));
|
|
2706
|
+
currentFile = filePath;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
const location = diagnosticLocation(diagnostic).padStart(locationWidth);
|
|
2710
|
+
const severity = diagnostic.severity === "warning" || diagnostic.severity === 1 ? "warning" : "error";
|
|
2711
|
+
const severityColor = severity === "error" ? "\u001b[31m" : "\u001b[33m";
|
|
2712
|
+
const message = String(diagnostic.message ?? "").replace(/\s*[\r\n]+\s*/g, " ");
|
|
2713
|
+
const rule = diagnostic.ruleId ? ` ${paint(color, "\u001b[2m", diagnostic.ruleId)}` : "";
|
|
2681
2714
|
lines.push(
|
|
2682
|
-
|
|
2715
|
+
` ${paint(color, "\u001b[2m", location)} ${paint(color, severityColor, severity.padEnd(7))} ${message}${rule}`
|
|
2683
2716
|
);
|
|
2684
2717
|
}
|
|
2685
|
-
|
|
2718
|
+
|
|
2719
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error" || diagnostic.severity === 2).length;
|
|
2720
|
+
const warnings = diagnostics.length - errors;
|
|
2721
|
+
const markerColor = errors > 0 ? "\u001b[31m" : "\u001b[33m";
|
|
2722
|
+
lines.push("");
|
|
2723
|
+
lines.push(
|
|
2724
|
+
`${paint(color, markerColor, "✖")} ${pluralize(diagnostics.length, "problem")} (${pluralize(errors, "error")}, ${pluralize(warnings, "warning")})`
|
|
2725
|
+
);
|
|
2726
|
+
|
|
2727
|
+
const fixable = diagnostics.filter(diagnosticIsFixable).length;
|
|
2728
|
+
const fixRequested = options.fix || options.extraArgs?.includes("--fix") || options.extraArgs?.includes("--fix-dry-run");
|
|
2729
|
+
if (fixable > 0 && !fixRequested) {
|
|
2730
|
+
lines.push(` ${pluralize(fixable, "problem")} potentially fixable with the \`--fix\` option.`);
|
|
2731
|
+
}
|
|
2732
|
+
lines.push(` ${pluralize(files, "file")} checked`);
|
|
2686
2733
|
return `${lines.join("\n")}\n`;
|
|
2687
2734
|
}
|
|
2688
2735
|
|
|
2736
|
+
function diagnosticLocation(diagnostic) {
|
|
2737
|
+
return `${diagnostic.line ?? 0}:${diagnostic.column ?? 0}`;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
function diagnosticIsFixable(diagnostic) {
|
|
2741
|
+
return Boolean(diagnostic.fix) || (Array.isArray(diagnostic.fixes) && diagnostic.fixes.length > 0);
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
function pluralize(count, noun) {
|
|
2745
|
+
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
function paint(enabled, code, text) {
|
|
2749
|
+
return enabled ? `${code}${text}\u001b[0m` : text;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
function shouldUseColor(options = {}) {
|
|
2753
|
+
if (options.color === true) return true;
|
|
2754
|
+
if (options.color === false) return false;
|
|
2755
|
+
const env = options.env ? { ...process.env, ...options.env } : process.env;
|
|
2756
|
+
if (env.NO_COLOR !== undefined || env.NODE_DISABLE_COLORS !== undefined) return false;
|
|
2757
|
+
if ((env.FORCE_COLOR && env.FORCE_COLOR !== "0") || (env.CLICOLOR_FORCE && env.CLICOLOR_FORCE !== "0")) return true;
|
|
2758
|
+
return Boolean(process.stderr.isTTY);
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2689
2761
|
export function runFishlint(args = [], options = {}) {
|
|
2690
2762
|
const cliArgs = normalizeStringArray(args, "args");
|
|
2691
2763
|
const env = options.env ? { ...process.env, ...options.env } : { ...process.env };
|
|
@@ -2899,6 +2971,7 @@ function booleanFlagValue(arg) {
|
|
|
2899
2971
|
}
|
|
2900
2972
|
|
|
2901
2973
|
export function lintFiles(paths, options = {}) {
|
|
2974
|
+
options = withConfigCache(options);
|
|
2902
2975
|
const targets = lintTargetsForInput(paths, options);
|
|
2903
2976
|
const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
|
|
2904
2977
|
const lintPaths = filteredLintPaths(targets, options);
|
|
@@ -3077,6 +3150,7 @@ function mergeNativeReports(reports) {
|
|
|
3077
3150
|
}
|
|
3078
3151
|
|
|
3079
3152
|
export function lintText(code, options = {}) {
|
|
3153
|
+
options = withConfigCache(options);
|
|
3080
3154
|
if (typeof code !== "string") {
|
|
3081
3155
|
throw new TypeError("code must be a string");
|
|
3082
3156
|
}
|
|
@@ -3143,7 +3217,7 @@ function buildNativeLintArgs(paths, options) {
|
|
|
3143
3217
|
if (!options.rules && !options.forceMaterializedConfig && (options.config || options.baseConfig || options.overrideConfig)) {
|
|
3144
3218
|
const configForRuleSelection = options.noConfig
|
|
3145
3219
|
? undefined
|
|
3146
|
-
: options.config ? readConfig(options.config, options.cwd) : undefined;
|
|
3220
|
+
: options.config ? readConfig(options.config, options.cwd, options.configCache) : undefined;
|
|
3147
3221
|
const enabledRules = enabledRuleNamesFromConfigs(
|
|
3148
3222
|
configForRuleSelection,
|
|
3149
3223
|
options.baseConfig,
|
|
@@ -3189,6 +3263,12 @@ function mergeLintOptions(base, override) {
|
|
|
3189
3263
|
};
|
|
3190
3264
|
}
|
|
3191
3265
|
|
|
3266
|
+
function withConfigCache(options) {
|
|
3267
|
+
// Executable configs are otherwise re-evaluated at every per-file lookup.
|
|
3268
|
+
// Keep the cache on the options object so one public invocation shares it.
|
|
3269
|
+
return options.configCache ? options : { ...options, configCache: new Map() };
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3192
3272
|
function eslintConstructorOptions(options) {
|
|
3193
3273
|
const mapped = {
|
|
3194
3274
|
cwd: options.cwd,
|
|
@@ -3361,7 +3441,7 @@ function rulesFromFileConfig(options, filePath) {
|
|
|
3361
3441
|
return {};
|
|
3362
3442
|
}
|
|
3363
3443
|
|
|
3364
|
-
const config = readConfig(configPath, options.cwd);
|
|
3444
|
+
const config = readConfig(configPath, options.cwd, options.configCache);
|
|
3365
3445
|
return rulesFromConfig(config, filePath, dirname(configPath));
|
|
3366
3446
|
}
|
|
3367
3447
|
|
|
@@ -3375,7 +3455,7 @@ function configDataFromFileConfig(options, filePath) {
|
|
|
3375
3455
|
return {};
|
|
3376
3456
|
}
|
|
3377
3457
|
|
|
3378
|
-
const config = readConfig(configPath, options.cwd);
|
|
3458
|
+
const config = readConfig(configPath, options.cwd, options.configCache);
|
|
3379
3459
|
return configDataFromConfig(config, filePath, dirname(configPath));
|
|
3380
3460
|
}
|
|
3381
3461
|
|
|
@@ -3396,7 +3476,7 @@ function defaultLintTargets(options = {}) {
|
|
|
3396
3476
|
return ["."];
|
|
3397
3477
|
}
|
|
3398
3478
|
|
|
3399
|
-
const targets = filePatternsFromConfig(readConfig(configPath, options.cwd));
|
|
3479
|
+
const targets = filePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache));
|
|
3400
3480
|
return targets.length > 0
|
|
3401
3481
|
? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
|
|
3402
3482
|
: ["."];
|
|
@@ -3488,7 +3568,9 @@ function configPathFromDirectory(directory) {
|
|
|
3488
3568
|
function withTemporaryConfig(options, callback) {
|
|
3489
3569
|
const fileConfigPath = !options.noConfig ? configPathForOptions(options) : undefined;
|
|
3490
3570
|
const shouldMaterializeFileConfig = Boolean(fileConfigPath);
|
|
3491
|
-
const fileConfig = shouldMaterializeFileConfig
|
|
3571
|
+
const fileConfig = shouldMaterializeFileConfig
|
|
3572
|
+
? readConfig(fileConfigPath, options.cwd, options.configCache)
|
|
3573
|
+
: undefined;
|
|
3492
3574
|
const configs = [options.baseConfig, fileConfig, options.overrideConfig];
|
|
3493
3575
|
const rules = shouldMaterializeFileConfig
|
|
3494
3576
|
? materializedRulesFromConfigs(...configs)
|
|
@@ -4015,7 +4097,7 @@ function ignorePatternsFromFileConfig(options, filePath) {
|
|
|
4015
4097
|
}
|
|
4016
4098
|
|
|
4017
4099
|
const cwd = options.cwd ?? process.cwd();
|
|
4018
|
-
return ignorePatternsFromConfig(readConfig(configPath, options.cwd)).map((pattern) =>
|
|
4100
|
+
return ignorePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache)).map((pattern) =>
|
|
4019
4101
|
rebaseConfigPattern(pattern, dirname(configPath), cwd)
|
|
4020
4102
|
);
|
|
4021
4103
|
}
|
package/lib/config-loader.cjs
CHANGED
|
@@ -38,17 +38,25 @@ function isFile(path) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function readConfig(path, cwd) {
|
|
41
|
+
function readConfig(path, cwd, cache) {
|
|
42
42
|
const configPath = resolvePath(cwd ?? process.cwd(), path);
|
|
43
|
-
if (
|
|
44
|
-
return
|
|
43
|
+
if (cache?.has(configPath)) {
|
|
44
|
+
return cache.get(configPath);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
let config;
|
|
48
|
+
if (isExecutableConfigPath(configPath)) {
|
|
49
|
+
config = readExecutableConfig(configPath, cwd);
|
|
50
|
+
} else {
|
|
51
|
+
try {
|
|
52
|
+
config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw configReadError(configPath, error.message);
|
|
55
|
+
}
|
|
51
56
|
}
|
|
57
|
+
|
|
58
|
+
cache?.set(configPath, config);
|
|
59
|
+
return config;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
function isExecutableConfigPath(path) {
|
package/lib/config-loader.js
CHANGED
|
@@ -39,17 +39,25 @@ function isFile(path) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export function readConfig(path, cwd) {
|
|
42
|
+
export function readConfig(path, cwd, cache) {
|
|
43
43
|
const configPath = resolvePath(cwd ?? process.cwd(), path);
|
|
44
|
-
if (
|
|
45
|
-
return
|
|
44
|
+
if (cache?.has(configPath)) {
|
|
45
|
+
return cache.get(configPath);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
let config;
|
|
49
|
+
if (isExecutableConfigPath(configPath)) {
|
|
50
|
+
config = readExecutableConfig(configPath, cwd);
|
|
51
|
+
} else {
|
|
52
|
+
try {
|
|
53
|
+
config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw configReadError(configPath, error.message);
|
|
56
|
+
}
|
|
52
57
|
}
|
|
58
|
+
|
|
59
|
+
cache?.set(configPath, config);
|
|
60
|
+
return config;
|
|
53
61
|
}
|
|
54
62
|
|
|
55
63
|
export function isExecutableConfigPath(path) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utoo/lint",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Experimental JavaScript and TypeScript linter powered by Yuku",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
"use-at-your-own-risk.js"
|
|
74
74
|
],
|
|
75
75
|
"optionalDependencies": {
|
|
76
|
-
"@utoo/lint-darwin-arm64": "0.2.
|
|
77
|
-
"@utoo/lint-darwin-x64": "0.2.
|
|
78
|
-
"@utoo/lint-linux-arm64": "0.2.
|
|
79
|
-
"@utoo/lint-linux-x64": "0.2.
|
|
80
|
-
"@utoo/lint-win32-x64": "0.2.
|
|
76
|
+
"@utoo/lint-darwin-arm64": "0.2.4",
|
|
77
|
+
"@utoo/lint-darwin-x64": "0.2.4",
|
|
78
|
+
"@utoo/lint-linux-arm64": "0.2.4",
|
|
79
|
+
"@utoo/lint-linux-x64": "0.2.4",
|
|
80
|
+
"@utoo/lint-win32-x64": "0.2.4"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
83
|
"jiti": "2.7.0"
|