@utoo/lint 0.2.1 → 0.2.2
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 +57 -11
- package/bin/fishlint.js +429 -64
- package/bin/migrate.js +8 -6
- package/index.cjs +275 -130
- package/index.d.ts +5 -1
- package/index.js +275 -130
- package/lib/config-loader.cjs +87 -0
- package/lib/config-loader.js +81 -0
- package/lib/load-config.js +23 -0
- package/package.json +15 -6
- package/schema.json +1 -1
package/index.js
CHANGED
|
@@ -2,10 +2,14 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { dirname, extname, isAbsolute, join, resolve as resolvePath } from "node:path";
|
|
5
|
+
import { dirname, extname, isAbsolute, join, relative, resolve as resolvePath } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
8
|
import { resolveBinary } from "./lib/binary.js";
|
|
9
|
+
import {
|
|
10
|
+
findConfigPath as findConfigPathFromDirectory,
|
|
11
|
+
readConfig
|
|
12
|
+
} from "./lib/config-loader.js";
|
|
9
13
|
|
|
10
14
|
export { platformPackageName, resolveBinary } from "./lib/binary.js";
|
|
11
15
|
|
|
@@ -69,20 +73,6 @@ const FISHLINT_DROP_VALUE_FLAGS = new Set([
|
|
|
69
73
|
"-E",
|
|
70
74
|
"-o"
|
|
71
75
|
]);
|
|
72
|
-
const CONFIG_FILENAMES = ["utoo.json", "utoo-lint.json"];
|
|
73
|
-
const JAVASCRIPT_CONFIG_EXTENSIONS = new Set([".js", ".mjs", ".cjs"]);
|
|
74
|
-
const JAVASCRIPT_CONFIG_LOADER_SCRIPT = `
|
|
75
|
-
import { pathToFileURL } from "node:url";
|
|
76
|
-
|
|
77
|
-
const configPath = process.argv[1];
|
|
78
|
-
const loaded = await import(pathToFileURL(configPath).href);
|
|
79
|
-
const value = loaded.default ?? loaded;
|
|
80
|
-
const json = JSON.stringify(value);
|
|
81
|
-
if (json === undefined) {
|
|
82
|
-
throw new TypeError("config did not export a JSON-serializable value");
|
|
83
|
-
}
|
|
84
|
-
process.stdout.write(json);
|
|
85
|
-
`;
|
|
86
76
|
const BUILTIN_RULE_IDS = [
|
|
87
77
|
"accessor-pairs",
|
|
88
78
|
"array-callback-return",
|
|
@@ -420,6 +410,33 @@ const BUILTIN_RULE_IDS = [
|
|
|
420
410
|
"@typescript-eslint/prefer-namespace-keyword",
|
|
421
411
|
"@typescript-eslint/restrict-plus-operands"
|
|
422
412
|
];
|
|
413
|
+
const NATIVE_ONLY_RULE_IDS = [
|
|
414
|
+
"@alipay/ant/disallow-typos",
|
|
415
|
+
"@alipay/ant/exhaustive-deps",
|
|
416
|
+
"@alipay/ant/jsx-handler-names",
|
|
417
|
+
"@alipay/ant/no-deprecated-dependence",
|
|
418
|
+
"@alipay/ant/no-deprecated-variable",
|
|
419
|
+
"@alipay/ant/no-import-files-from-pages-in-common",
|
|
420
|
+
"@alipay/ant/no-negative-conditionals",
|
|
421
|
+
"@alipay/ant/no-import-src",
|
|
422
|
+
"@alipay/ant/no-phantom-dependencies",
|
|
423
|
+
"@alipay/ant/no-too-large-file",
|
|
424
|
+
"@alipay/ant/prefer-elseif-end-with-else",
|
|
425
|
+
"@alipay/ant/prefer-catch-unsafe-func-call",
|
|
426
|
+
"@alipay/ant/prefer-click-with-debounce",
|
|
427
|
+
"@alipay/ant/prefer-import-as-required",
|
|
428
|
+
"@alipay/ant/no-spread-params",
|
|
429
|
+
"@alipay/ant/prefer-managed-resource",
|
|
430
|
+
"@alipay/ant/prefer-safe-image-renderer",
|
|
431
|
+
"@alipay/ant/prefer-import-from-stdlib",
|
|
432
|
+
"@alipay/spmLint/use-labeled-spm",
|
|
433
|
+
"@alipay/spmLint/valid-manual-click",
|
|
434
|
+
"@alipay/spmLint/valid-manual-expo",
|
|
435
|
+
"@alipay/spmLint/valid-manual-param",
|
|
436
|
+
"@alipay/spmLint/valid-manual-pv",
|
|
437
|
+
"semantic-errors"
|
|
438
|
+
];
|
|
439
|
+
const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
|
|
423
440
|
const FIXABLE_BUILTIN_RULE_IDS = new Set(["no-extra-semi", "@typescript-eslint/no-extra-semi"]);
|
|
424
441
|
const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
|
|
425
442
|
|
|
@@ -2633,24 +2650,40 @@ export function runCli(args = [], options = {}) {
|
|
|
2633
2650
|
|
|
2634
2651
|
const cliOptions = {
|
|
2635
2652
|
...options,
|
|
2636
|
-
...parsed.options
|
|
2653
|
+
...parsed.options,
|
|
2654
|
+
extraArgs: parsed.passthroughArgs,
|
|
2655
|
+
preserveNativeDefaults: Boolean(parsed.options.noConfig)
|
|
2637
2656
|
};
|
|
2638
2657
|
const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
|
|
2639
|
-
const
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2658
|
+
const report = lintFiles(targets, cliOptions);
|
|
2659
|
+
report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
|
|
2660
|
+
report.exitCode = exitCodeForDiagnostics(report.diagnostics);
|
|
2661
|
+
const status = report.exitCode ?? exitCodeForDiagnostics(report.diagnostics);
|
|
2662
|
+
if (cliOptions.format === "json" || parsed.passthroughArgs.includes("--json")) {
|
|
2663
|
+
return completedRunResult(status, `${JSON.stringify(publicCliReport(report))}\n`, report.stderr ?? "", options);
|
|
2643
2664
|
}
|
|
2665
|
+
return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report)}`, options);
|
|
2666
|
+
}
|
|
2644
2667
|
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2668
|
+
function publicCliReport(report) {
|
|
2669
|
+
return {
|
|
2670
|
+
files: report.files ?? 0,
|
|
2671
|
+
filePaths: report.filePaths ?? [],
|
|
2672
|
+
diagnostics: report.diagnostics ?? [],
|
|
2673
|
+
outputs: report.outputs ?? []
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
function formatNativeTextReport(report) {
|
|
2678
|
+
const lines = [];
|
|
2679
|
+
for (const diagnostic of report.diagnostics ?? []) {
|
|
2680
|
+
const rule = diagnostic.ruleId ? ` [${diagnostic.ruleId}]` : "";
|
|
2681
|
+
lines.push(
|
|
2682
|
+
`${diagnostic.filePath}:${diagnostic.line ?? 0}:${diagnostic.column ?? 0}: ${diagnostic.severity ?? "error"}: ${diagnostic.message}${rule}`
|
|
2683
|
+
);
|
|
2684
|
+
}
|
|
2685
|
+
lines.push(`${report.files ?? 0} file(s) checked, ${(report.diagnostics ?? []).length} diagnostic(s)`);
|
|
2686
|
+
return `${lines.join("\n")}\n`;
|
|
2654
2687
|
}
|
|
2655
2688
|
|
|
2656
2689
|
export function runFishlint(args = [], options = {}) {
|
|
@@ -2870,11 +2903,30 @@ export function lintFiles(paths, options = {}) {
|
|
|
2870
2903
|
const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
|
|
2871
2904
|
const lintPaths = filteredLintPaths(targets, options);
|
|
2872
2905
|
if (lintPaths.length === 0) {
|
|
2873
|
-
return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: ignoredDiagnostics
|
|
2906
|
+
return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
const configRuns = nativeConfigRunsForFiles(expandNativeConfigRunPaths(lintPaths, options), options);
|
|
2910
|
+
if (configRuns) {
|
|
2911
|
+
const report = mergeNativeReports(
|
|
2912
|
+
configRuns.map(({ paths: runPaths, options: runOptions }) => runNativeLintReport(runPaths, runOptions))
|
|
2913
|
+
);
|
|
2914
|
+
return finalizeLintReport(report, ignoredDiagnostics, options);
|
|
2874
2915
|
}
|
|
2875
2916
|
|
|
2917
|
+
const report = runNativeLintReport(lintPaths, options);
|
|
2918
|
+
return finalizeLintReport(report, ignoredDiagnostics, options);
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
function expandNativeConfigRunPaths(paths, options) {
|
|
2922
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2923
|
+
const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
|
|
2924
|
+
return paths.flatMap((path) => expandLintTarget(path, cwd, patterns) ?? [path]);
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
function runNativeLintReport(paths, options) {
|
|
2876
2928
|
return withTemporaryConfig(options, (resolvedOptions) => {
|
|
2877
|
-
const cliArgs = buildLintArgs(
|
|
2929
|
+
const cliArgs = buildLintArgs(paths, resolvedOptions);
|
|
2878
2930
|
const result = run(cliArgs, { ...resolvedOptions, stdio: undefined, encoding: "utf8" });
|
|
2879
2931
|
|
|
2880
2932
|
if (result.error) {
|
|
@@ -2897,17 +2949,6 @@ export function lintFiles(paths, options = {}) {
|
|
|
2897
2949
|
}
|
|
2898
2950
|
|
|
2899
2951
|
report.exitCode = status;
|
|
2900
|
-
report.diagnostics = [
|
|
2901
|
-
...(report.diagnostics ?? []),
|
|
2902
|
-
...ignoredDiagnostics
|
|
2903
|
-
];
|
|
2904
|
-
if (!resolvedOptions.deferDiagnosticConfigFiltering) {
|
|
2905
|
-
report.filePaths = normalizeReportFilePaths(report.filePaths, resolvedOptions);
|
|
2906
|
-
report.diagnostics = normalizeDiagnosticFilePaths(report.diagnostics, resolvedOptions);
|
|
2907
|
-
report.outputs = normalizeReportOutputs(report.outputs, resolvedOptions);
|
|
2908
|
-
report.diagnostics = normalizeReportDiagnostics(report.diagnostics, resolvedOptions);
|
|
2909
|
-
report.exitCode = exitCodeForDiagnostics(report.diagnostics);
|
|
2910
|
-
}
|
|
2911
2952
|
if (stderr) {
|
|
2912
2953
|
Object.defineProperty(report, "stderr", {
|
|
2913
2954
|
value: stderr,
|
|
@@ -2919,6 +2960,122 @@ export function lintFiles(paths, options = {}) {
|
|
|
2919
2960
|
});
|
|
2920
2961
|
}
|
|
2921
2962
|
|
|
2963
|
+
function finalizeLintReport(report, ignoredDiagnostics, options) {
|
|
2964
|
+
report.diagnostics = [
|
|
2965
|
+
...(report.diagnostics ?? []),
|
|
2966
|
+
...ignoredDiagnostics
|
|
2967
|
+
];
|
|
2968
|
+
if (!options.deferDiagnosticConfigFiltering) {
|
|
2969
|
+
report.filePaths = normalizeReportFilePaths(report.filePaths, options);
|
|
2970
|
+
report.diagnostics = normalizeDiagnosticFilePaths(report.diagnostics, options);
|
|
2971
|
+
report.outputs = normalizeReportOutputs(report.outputs, options);
|
|
2972
|
+
report.diagnostics = normalizeReportDiagnostics(report.diagnostics, options);
|
|
2973
|
+
report.exitCode = exitCodeForDiagnostics(report.diagnostics);
|
|
2974
|
+
}
|
|
2975
|
+
return report;
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
function nativeConfigRunsForFiles(paths, options) {
|
|
2979
|
+
if (options.noConfig && !options.baseConfig && !options.overrideConfig) {
|
|
2980
|
+
return undefined;
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
const groups = new Map();
|
|
2984
|
+
let hasConfigSource = Boolean(options.baseConfig || options.overrideConfig);
|
|
2985
|
+
for (const filePath of paths) {
|
|
2986
|
+
const matchPath = paths.length === 1
|
|
2987
|
+
? options.filePath ?? options.filename ?? filePath
|
|
2988
|
+
: filePath;
|
|
2989
|
+
const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
|
|
2990
|
+
const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
|
|
2991
|
+
hasConfigSource ||= configured;
|
|
2992
|
+
let rules = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath).rules : undefined;
|
|
2993
|
+
if (configured && options.rules) {
|
|
2994
|
+
rules = selectedRulesWithConfigOptions(rules, options.rules);
|
|
2995
|
+
}
|
|
2996
|
+
const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
|
|
2997
|
+
if (!groups.has(signature)) {
|
|
2998
|
+
groups.set(signature, { paths: [], rules, configured });
|
|
2999
|
+
}
|
|
3000
|
+
groups.get(signature).paths.push(filePath);
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
if (!hasConfigSource) {
|
|
3004
|
+
return undefined;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
return [...groups.values()].map((group) => {
|
|
3008
|
+
if (!group.configured) {
|
|
3009
|
+
return {
|
|
3010
|
+
paths: group.paths,
|
|
3011
|
+
options: {
|
|
3012
|
+
...options,
|
|
3013
|
+
config: undefined,
|
|
3014
|
+
noConfig: true,
|
|
3015
|
+
baseConfig: undefined,
|
|
3016
|
+
overrideConfig: undefined
|
|
3017
|
+
}
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
return {
|
|
3022
|
+
paths: group.paths,
|
|
3023
|
+
options: {
|
|
3024
|
+
...options,
|
|
3025
|
+
config: undefined,
|
|
3026
|
+
noConfig: true,
|
|
3027
|
+
baseConfig: undefined,
|
|
3028
|
+
overrideConfig: { rules: allDisabledNativeRules(group.rules) },
|
|
3029
|
+
forceMaterializedConfig: true
|
|
3030
|
+
}
|
|
3031
|
+
};
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
|
|
3035
|
+
function allDisabledNativeRules(rules) {
|
|
3036
|
+
return {
|
|
3037
|
+
...Object.fromEntries(NATIVE_RULE_IDS.map((rule) => [rule, "off"])),
|
|
3038
|
+
...rules
|
|
3039
|
+
};
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
function selectedRulesWithConfigOptions(configRules, selectedRules) {
|
|
3043
|
+
const selected = rulesFromNativeRuleList(selectedRules);
|
|
3044
|
+
return Object.fromEntries(
|
|
3045
|
+
Object.keys(selected).map((rule) => [
|
|
3046
|
+
rule,
|
|
3047
|
+
configRules?.[rule] != null && ruleConfigSeverity(configRules[rule]) > 0
|
|
3048
|
+
? configRules[rule]
|
|
3049
|
+
: selected[rule]
|
|
3050
|
+
])
|
|
3051
|
+
);
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
function stableConfigSignature(value) {
|
|
3055
|
+
if (Array.isArray(value)) {
|
|
3056
|
+
return `[${value.map((item) => stableConfigSignature(item)).join(",")}]`;
|
|
3057
|
+
}
|
|
3058
|
+
if (value && typeof value === "object") {
|
|
3059
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableConfigSignature(value[key])}`).join(",")}}`;
|
|
3060
|
+
}
|
|
3061
|
+
return JSON.stringify(value);
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
function mergeNativeReports(reports) {
|
|
3065
|
+
const report = {
|
|
3066
|
+
files: reports.reduce((count, item) => count + (item.files ?? 0), 0),
|
|
3067
|
+
filePaths: reports.flatMap((item) => item.filePaths ?? []),
|
|
3068
|
+
diagnostics: reports.flatMap((item) => item.diagnostics ?? []),
|
|
3069
|
+
outputs: reports.flatMap((item) => item.outputs ?? []),
|
|
3070
|
+
exitCode: reports.some((item) => item.exitCode) ? 1 : 0
|
|
3071
|
+
};
|
|
3072
|
+
const stderr = reports.map((item) => item.stderr ?? "").join("");
|
|
3073
|
+
if (stderr) {
|
|
3074
|
+
Object.defineProperty(report, "stderr", { value: stderr, enumerable: false });
|
|
3075
|
+
}
|
|
3076
|
+
return report;
|
|
3077
|
+
}
|
|
3078
|
+
|
|
2922
3079
|
export function lintText(code, options = {}) {
|
|
2923
3080
|
if (typeof code !== "string") {
|
|
2924
3081
|
throw new TypeError("code must be a string");
|
|
@@ -2940,7 +3097,7 @@ export function lintText(code, options = {}) {
|
|
|
2940
3097
|
files: 0,
|
|
2941
3098
|
filePaths: [],
|
|
2942
3099
|
diagnostics,
|
|
2943
|
-
exitCode: diagnostics
|
|
3100
|
+
exitCode: exitCodeForDiagnostics(diagnostics)
|
|
2944
3101
|
};
|
|
2945
3102
|
}
|
|
2946
3103
|
|
|
@@ -2977,22 +3134,28 @@ function buildLintArgs(paths, options) {
|
|
|
2977
3134
|
});
|
|
2978
3135
|
}
|
|
2979
3136
|
|
|
2980
|
-
function buildRunCliArgs(paths, options, passthroughArgs) {
|
|
2981
|
-
return buildNativeLintArgs(paths, {
|
|
2982
|
-
...options,
|
|
2983
|
-
extraArgs: passthroughArgs
|
|
2984
|
-
});
|
|
2985
|
-
}
|
|
2986
|
-
|
|
2987
3137
|
function buildNativeLintArgs(paths, options) {
|
|
2988
3138
|
const cliArgs = [];
|
|
2989
3139
|
|
|
2990
|
-
if (options.config) {
|
|
2991
|
-
cliArgs.push(`--config=${options.config}`);
|
|
2992
|
-
}
|
|
2993
3140
|
if (options.noConfig) {
|
|
2994
3141
|
cliArgs.push("--no-config");
|
|
2995
3142
|
}
|
|
3143
|
+
if (!options.rules && !options.forceMaterializedConfig && (options.config || options.baseConfig || options.overrideConfig)) {
|
|
3144
|
+
const configForRuleSelection = options.noConfig
|
|
3145
|
+
? undefined
|
|
3146
|
+
: options.config ? readConfig(options.config, options.cwd) : undefined;
|
|
3147
|
+
const enabledRules = enabledRuleNamesFromConfigs(
|
|
3148
|
+
configForRuleSelection,
|
|
3149
|
+
options.baseConfig,
|
|
3150
|
+
options.overrideConfig
|
|
3151
|
+
);
|
|
3152
|
+
if (enabledRules.length > 0) {
|
|
3153
|
+
cliArgs.push(`--rules=${enabledRules.join(",")}`);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
if (options.config) {
|
|
3157
|
+
cliArgs.push(`--config=${options.config}`);
|
|
3158
|
+
}
|
|
2996
3159
|
if (options.rules) {
|
|
2997
3160
|
const rules = Array.isArray(options.rules) ? options.rules.join(",") : options.rules;
|
|
2998
3161
|
if (rules) {
|
|
@@ -3078,10 +3241,10 @@ function calculatedConfig(options = {}, filePath) {
|
|
|
3078
3241
|
return {
|
|
3079
3242
|
...configData,
|
|
3080
3243
|
rules: {
|
|
3081
|
-
...rulesFromNativeRuleList(options.rules),
|
|
3082
3244
|
...rulesFromConfig(options.baseConfig, filePath, options.cwd),
|
|
3083
3245
|
...rulesFromFileConfig(options, filePath),
|
|
3084
|
-
...rulesFromConfig(options.overrideConfig, filePath, options.cwd)
|
|
3246
|
+
...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
|
|
3247
|
+
...rulesFromNativeRuleList(options.rules)
|
|
3085
3248
|
}
|
|
3086
3249
|
};
|
|
3087
3250
|
}
|
|
@@ -3199,7 +3362,7 @@ function rulesFromFileConfig(options, filePath) {
|
|
|
3199
3362
|
}
|
|
3200
3363
|
|
|
3201
3364
|
const config = readConfig(configPath, options.cwd);
|
|
3202
|
-
return rulesFromConfig(config, filePath,
|
|
3365
|
+
return rulesFromConfig(config, filePath, dirname(configPath));
|
|
3203
3366
|
}
|
|
3204
3367
|
|
|
3205
3368
|
function configDataFromFileConfig(options, filePath) {
|
|
@@ -3213,7 +3376,7 @@ function configDataFromFileConfig(options, filePath) {
|
|
|
3213
3376
|
}
|
|
3214
3377
|
|
|
3215
3378
|
const config = readConfig(configPath, options.cwd);
|
|
3216
|
-
return configDataFromConfig(config, filePath,
|
|
3379
|
+
return configDataFromConfig(config, filePath, dirname(configPath));
|
|
3217
3380
|
}
|
|
3218
3381
|
|
|
3219
3382
|
function lintTargetsForInput(paths, options = {}) {
|
|
@@ -3234,7 +3397,16 @@ function defaultLintTargets(options = {}) {
|
|
|
3234
3397
|
}
|
|
3235
3398
|
|
|
3236
3399
|
const targets = filePatternsFromConfig(readConfig(configPath, options.cwd));
|
|
3237
|
-
return targets.length > 0
|
|
3400
|
+
return targets.length > 0
|
|
3401
|
+
? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
|
|
3402
|
+
: ["."];
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
function resolveConfigPattern(pattern, configDirectory) {
|
|
3406
|
+
if (isAbsolute(pattern)) {
|
|
3407
|
+
return pattern;
|
|
3408
|
+
}
|
|
3409
|
+
return resolvePath(configDirectory, pattern.replace(/^[/\\]/, ""));
|
|
3238
3410
|
}
|
|
3239
3411
|
|
|
3240
3412
|
function filePatternsFromConfig(config) {
|
|
@@ -3288,6 +3460,9 @@ function configPathForOptions(options) {
|
|
|
3288
3460
|
}
|
|
3289
3461
|
|
|
3290
3462
|
function configPathForFile(options, filePath) {
|
|
3463
|
+
if (options.noConfig) {
|
|
3464
|
+
return undefined;
|
|
3465
|
+
}
|
|
3291
3466
|
if (options.config) {
|
|
3292
3467
|
return resolvePath(options.cwd ?? process.cwd(), options.config);
|
|
3293
3468
|
}
|
|
@@ -3307,66 +3482,18 @@ function configSearchDirectoryForFile(filePath, cwd) {
|
|
|
3307
3482
|
}
|
|
3308
3483
|
|
|
3309
3484
|
function configPathFromDirectory(directory) {
|
|
3310
|
-
|
|
3311
|
-
while (true) {
|
|
3312
|
-
for (const candidate of CONFIG_FILENAMES) {
|
|
3313
|
-
const path = resolvePath(current, candidate);
|
|
3314
|
-
if (existsSync(path)) {
|
|
3315
|
-
return path;
|
|
3316
|
-
}
|
|
3317
|
-
}
|
|
3318
|
-
const parent = dirname(current);
|
|
3319
|
-
if (parent === current) {
|
|
3320
|
-
return undefined;
|
|
3321
|
-
}
|
|
3322
|
-
current = parent;
|
|
3323
|
-
}
|
|
3324
|
-
}
|
|
3325
|
-
|
|
3326
|
-
function readConfig(path, cwd) {
|
|
3327
|
-
if (isJavaScriptConfigPath(path)) {
|
|
3328
|
-
return readJavaScriptConfig(path, cwd);
|
|
3329
|
-
}
|
|
3330
|
-
|
|
3331
|
-
try {
|
|
3332
|
-
return JSON.parse(readFileSync(path, "utf8"));
|
|
3333
|
-
} catch (error) {
|
|
3334
|
-
throw new Error(`utoo-lint unable to read config ${path}: ${error.message}`);
|
|
3335
|
-
}
|
|
3336
|
-
}
|
|
3337
|
-
|
|
3338
|
-
function readJavaScriptConfig(path, cwd) {
|
|
3339
|
-
const result = spawnSync(process.execPath, ["--input-type=module", "--eval", JAVASCRIPT_CONFIG_LOADER_SCRIPT, path], {
|
|
3340
|
-
cwd: cwd ?? process.cwd(),
|
|
3341
|
-
encoding: "utf8"
|
|
3342
|
-
});
|
|
3343
|
-
if (result.error) {
|
|
3344
|
-
throw result.error;
|
|
3345
|
-
}
|
|
3346
|
-
if (result.status !== 0) {
|
|
3347
|
-
throw new Error(`utoo-lint unable to read config ${path}: ${(result.stderr ?? "").trim() || "JavaScript config loader failed"}`);
|
|
3348
|
-
}
|
|
3349
|
-
try {
|
|
3350
|
-
return JSON.parse(result.stdout);
|
|
3351
|
-
} catch (error) {
|
|
3352
|
-
throw new Error(`utoo-lint unable to read config ${path}: ${error.message}`);
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
|
|
3356
|
-
function isJavaScriptConfigPath(path) {
|
|
3357
|
-
return JAVASCRIPT_CONFIG_EXTENSIONS.has(extname(path));
|
|
3485
|
+
return findConfigPathFromDirectory(directory);
|
|
3358
3486
|
}
|
|
3359
3487
|
|
|
3360
3488
|
function withTemporaryConfig(options, callback) {
|
|
3361
3489
|
const fileConfigPath = !options.noConfig ? configPathForOptions(options) : undefined;
|
|
3362
|
-
const shouldMaterializeFileConfig =
|
|
3490
|
+
const shouldMaterializeFileConfig = Boolean(fileConfigPath);
|
|
3363
3491
|
const fileConfig = shouldMaterializeFileConfig ? readConfig(fileConfigPath, options.cwd) : undefined;
|
|
3364
|
-
const
|
|
3365
|
-
|
|
3366
|
-
...
|
|
3367
|
-
...
|
|
3368
|
-
|
|
3369
|
-
if (Object.keys(inlineRules).length === 0) {
|
|
3492
|
+
const configs = [options.baseConfig, fileConfig, options.overrideConfig];
|
|
3493
|
+
const rules = shouldMaterializeFileConfig
|
|
3494
|
+
? materializedRulesFromConfigs(...configs)
|
|
3495
|
+
: runtimeRulesFromConfigs(...configs);
|
|
3496
|
+
if (Object.keys(rules).length === 0) {
|
|
3370
3497
|
if (shouldMaterializeFileConfig) {
|
|
3371
3498
|
return callback({
|
|
3372
3499
|
...options,
|
|
@@ -3376,10 +3503,9 @@ function withTemporaryConfig(options, callback) {
|
|
|
3376
3503
|
}
|
|
3377
3504
|
return callback(options);
|
|
3378
3505
|
}
|
|
3379
|
-
|
|
3380
|
-
const
|
|
3381
|
-
|
|
3382
|
-
if (!hasRuleOptions(rules)) {
|
|
3506
|
+
const enabledRules = enabledRuleNamesFromConfigs(...configs);
|
|
3507
|
+
const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
|
|
3508
|
+
if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
|
|
3383
3509
|
return callback({
|
|
3384
3510
|
...options,
|
|
3385
3511
|
config: shouldMaterializeFileConfig ? undefined : options.config,
|
|
@@ -3389,13 +3515,14 @@ function withTemporaryConfig(options, callback) {
|
|
|
3389
3515
|
}
|
|
3390
3516
|
|
|
3391
3517
|
const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
|
|
3392
|
-
const configPath = join(tmp, "
|
|
3518
|
+
const configPath = join(tmp, "utlint.config.json");
|
|
3393
3519
|
try {
|
|
3394
3520
|
writeFileSync(configPath, JSON.stringify({ rules }));
|
|
3395
3521
|
return callback({
|
|
3396
3522
|
...options,
|
|
3397
3523
|
config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
|
|
3398
|
-
noConfig: false
|
|
3524
|
+
noConfig: false,
|
|
3525
|
+
rules: options.forceMaterializedConfig ? undefined : options.rules
|
|
3399
3526
|
});
|
|
3400
3527
|
} finally {
|
|
3401
3528
|
rmSync(tmp, { recursive: true, force: true });
|
|
@@ -3416,6 +3543,10 @@ function runtimeRulesFromConfigs(...configs) {
|
|
|
3416
3543
|
return rules;
|
|
3417
3544
|
}
|
|
3418
3545
|
|
|
3546
|
+
function materializedRulesFromConfigs(...configs) {
|
|
3547
|
+
return runtimeRulesFromConfigs(...configs);
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3419
3550
|
function runtimeRulesFromConfig(config) {
|
|
3420
3551
|
if (!config) {
|
|
3421
3552
|
return {};
|
|
@@ -3429,9 +3560,7 @@ function runtimeRulesFromConfig(config) {
|
|
|
3429
3560
|
|
|
3430
3561
|
const rules = {};
|
|
3431
3562
|
for (const [rule, value] of Object.entries(rulesFromConfig(config))) {
|
|
3432
|
-
|
|
3433
|
-
rules[rule] = value;
|
|
3434
|
-
}
|
|
3563
|
+
rules[rule] = value;
|
|
3435
3564
|
}
|
|
3436
3565
|
return rules;
|
|
3437
3566
|
}
|
|
@@ -3512,19 +3641,19 @@ function reportToESLintResults(report, textOptions = {}) {
|
|
|
3512
3641
|
}
|
|
3513
3642
|
|
|
3514
3643
|
function normalizeReportDiagnostics(diagnostics, options = {}) {
|
|
3515
|
-
const filterUnconfiguredRules = hasRuleConfigSource(options);
|
|
3516
3644
|
return (diagnostics ?? []).flatMap((diagnostic) => {
|
|
3517
3645
|
if (!diagnostic?.ruleId) {
|
|
3518
3646
|
return [diagnostic];
|
|
3519
3647
|
}
|
|
3520
|
-
if (diagnostic.ruleId === "io") {
|
|
3648
|
+
if (diagnostic.ruleId === "io" || diagnostic.ruleId === "parse") {
|
|
3521
3649
|
return [diagnostic];
|
|
3522
3650
|
}
|
|
3523
3651
|
|
|
3524
3652
|
const filePath = normalizeESLintFilePath(diagnostic.filePath, options.cwd);
|
|
3653
|
+
const filterUnconfiguredRules = hasRuleConfigSource(options, filePath);
|
|
3525
3654
|
const ruleSeverities = ruleSeverityMapForOptions(options, filePath);
|
|
3526
3655
|
const severity = ruleSeverities?.get(diagnostic.ruleId);
|
|
3527
|
-
if (filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
|
|
3656
|
+
if (!options.preserveNativeDefaults && filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
|
|
3528
3657
|
return [];
|
|
3529
3658
|
}
|
|
3530
3659
|
if (severity === 0) {
|
|
@@ -3555,15 +3684,17 @@ function normalizeDiagnosticFilePaths(diagnostics, options = {}) {
|
|
|
3555
3684
|
}));
|
|
3556
3685
|
}
|
|
3557
3686
|
|
|
3558
|
-
function hasRuleConfigSource(options = {}) {
|
|
3559
|
-
if (options.
|
|
3687
|
+
function hasRuleConfigSource(options = {}, filePath) {
|
|
3688
|
+
if (options.baseConfig || options.overrideConfig || options.rules) {
|
|
3560
3689
|
return true;
|
|
3561
3690
|
}
|
|
3562
|
-
return !options.noConfig && Boolean(
|
|
3691
|
+
return !options.noConfig && Boolean(
|
|
3692
|
+
filePath ? configPathForFile(options, filePath) : configPathForOptions(options)
|
|
3693
|
+
);
|
|
3563
3694
|
}
|
|
3564
3695
|
|
|
3565
3696
|
function exitCodeForDiagnostics(diagnostics) {
|
|
3566
|
-
return
|
|
3697
|
+
return diagnostics?.some((diagnostic) => diagnostic?.severity === "error" || diagnostic?.severity === 2) ? 1 : 0;
|
|
3567
3698
|
}
|
|
3568
3699
|
|
|
3569
3700
|
function throwOnUnmatchedPatternDiagnostics(report, options = {}) {
|
|
@@ -3883,7 +4014,21 @@ function ignorePatternsFromFileConfig(options, filePath) {
|
|
|
3883
4014
|
return [];
|
|
3884
4015
|
}
|
|
3885
4016
|
|
|
3886
|
-
|
|
4017
|
+
const cwd = options.cwd ?? process.cwd();
|
|
4018
|
+
return ignorePatternsFromConfig(readConfig(configPath, options.cwd)).map((pattern) =>
|
|
4019
|
+
rebaseConfigPattern(pattern, dirname(configPath), cwd)
|
|
4020
|
+
);
|
|
4021
|
+
}
|
|
4022
|
+
|
|
4023
|
+
function rebaseConfigPattern(pattern, configDirectory, cwd) {
|
|
4024
|
+
const negated = pattern.startsWith("!");
|
|
4025
|
+
const value = negated ? pattern.slice(1) : pattern;
|
|
4026
|
+
const absolute = resolveConfigPattern(value, configDirectory);
|
|
4027
|
+
const relativePattern = normalizePath(relative(resolvePath(cwd), absolute));
|
|
4028
|
+
const rebased = relativePattern === ".." || relativePattern.startsWith("../")
|
|
4029
|
+
? normalizePath(absolute)
|
|
4030
|
+
: relativePattern;
|
|
4031
|
+
return negated ? `!${rebased}` : rebased;
|
|
3887
4032
|
}
|
|
3888
4033
|
|
|
3889
4034
|
function ignorePatternsFromConfig(config) {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const { spawnSync } = require("node:child_process");
|
|
2
|
+
const { readFileSync, statSync } = require("node:fs");
|
|
3
|
+
const { dirname, extname, join, resolve: resolvePath } = require("node:path");
|
|
4
|
+
|
|
5
|
+
const CONFIG_FILENAMES = [
|
|
6
|
+
"utlint.config.ts",
|
|
7
|
+
"utlint.config.json",
|
|
8
|
+
"utoo.json",
|
|
9
|
+
"utoo-lint.json"
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const TYPESCRIPT_CONFIG_EXTENSIONS = new Set([".ts", ".mts", ".cts"]);
|
|
13
|
+
const JAVASCRIPT_CONFIG_EXTENSIONS = new Set([".js", ".mjs", ".cjs"]);
|
|
14
|
+
const loaderPath = join(__dirname, "load-config.js");
|
|
15
|
+
|
|
16
|
+
function findConfigPath(directory) {
|
|
17
|
+
let current = resolvePath(directory);
|
|
18
|
+
while (true) {
|
|
19
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
20
|
+
const candidate = resolvePath(current, filename);
|
|
21
|
+
if (isFile(candidate)) {
|
|
22
|
+
return candidate;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const parent = dirname(current);
|
|
26
|
+
if (parent === current) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
current = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isFile(path) {
|
|
34
|
+
try {
|
|
35
|
+
return statSync(path).isFile();
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readConfig(path, cwd) {
|
|
42
|
+
const configPath = resolvePath(cwd ?? process.cwd(), path);
|
|
43
|
+
if (isExecutableConfigPath(configPath)) {
|
|
44
|
+
return readExecutableConfig(configPath, cwd);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(readFileSync(configPath, "utf8"));
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw configReadError(configPath, error.message);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isExecutableConfigPath(path) {
|
|
55
|
+
const extension = extname(path);
|
|
56
|
+
return TYPESCRIPT_CONFIG_EXTENSIONS.has(extension) || JAVASCRIPT_CONFIG_EXTENSIONS.has(extension);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readExecutableConfig(path, cwd) {
|
|
60
|
+
const result = spawnSync(process.execPath, [loaderPath, path], {
|
|
61
|
+
cwd: cwd ?? process.cwd(),
|
|
62
|
+
encoding: "utf8",
|
|
63
|
+
stdio: ["ignore", "pipe", "pipe", "pipe"]
|
|
64
|
+
});
|
|
65
|
+
if (result.error) {
|
|
66
|
+
throw configReadError(path, result.error.message);
|
|
67
|
+
}
|
|
68
|
+
if (result.status !== 0) {
|
|
69
|
+
throw configReadError(path, (result.stderr ?? "").trim() || "config loader failed");
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(result.output?.[3] ?? "");
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw configReadError(path, error.message);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function configReadError(path, message) {
|
|
79
|
+
return new Error(`utoo-lint unable to read config ${path}: ${message}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
CONFIG_FILENAMES,
|
|
84
|
+
findConfigPath,
|
|
85
|
+
isExecutableConfigPath,
|
|
86
|
+
readConfig
|
|
87
|
+
};
|