@utoo/lint 0.2.1 → 0.2.3

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/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
 
@@ -470,7 +487,7 @@ export class UtooLint {
470
487
  }
471
488
 
472
489
  async lintFiles(patterns, options = {}) {
473
- const mergedOptions = mergeLintOptions(this.options, options);
490
+ const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
474
491
  const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
475
492
  const customRuleFilterMap = customRuleMapForConfig(customRuleConfig, new Map());
476
493
  const nativeOptions = lintOptionsWithoutCustomRules(mergedOptions, customRuleFilterMap);
@@ -490,7 +507,7 @@ export class UtooLint {
490
507
  throw new TypeError("code must be a string");
491
508
  }
492
509
 
493
- const mergedOptions = mergeLintOptions(this.options, options);
510
+ const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
494
511
  const filePath = normalizeESLintFilePath(textFilePathForOptions(options, "<text>"), mergedOptions.cwd);
495
512
  const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
496
513
  const customRuleMap = customRuleMapForConfig(customRuleConfig, new Map(), filePath, mergedOptions.cwd);
@@ -514,7 +531,7 @@ export class UtooLint {
514
531
  }
515
532
 
516
533
  async calculateConfigForFile(filePath) {
517
- return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
534
+ return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
518
535
  }
519
536
 
520
537
  async findConfigFile(filePath) {
@@ -2429,7 +2446,7 @@ export class CLIEngine {
2429
2446
  }
2430
2447
 
2431
2448
  executeOnFiles(patterns) {
2432
- const mergedOptions = eslintConstructorOptions(this.options);
2449
+ const mergedOptions = withConfigCache(eslintConstructorOptions(this.options));
2433
2450
  const report = lintFiles(patterns, mergedOptions);
2434
2451
  throwOnUnmatchedPatternDiagnostics(report, mergedOptions);
2435
2452
  const results = maybeFilterQuietResults(reportToESLintResults(report, {
@@ -2449,10 +2466,10 @@ export class CLIEngine {
2449
2466
  typeof filePathOrOptions === "object" && filePathOrOptions !== null
2450
2467
  ? filePathOrOptions.filePath ?? filePathOrOptions.filename ?? "input.js"
2451
2468
  : filePathOrOptions;
2452
- const mergedOptions = {
2469
+ const mergedOptions = withConfigCache({
2453
2470
  ...eslintConstructorOptions(this.options),
2454
2471
  ...textOptions
2455
- };
2472
+ });
2456
2473
  const report = lintText(code, {
2457
2474
  ...mergedOptions,
2458
2475
  filePath
@@ -2489,7 +2506,7 @@ export class CLIEngine {
2489
2506
  }
2490
2507
 
2491
2508
  getConfigForFile(filePath) {
2492
- return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
2509
+ return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
2493
2510
  }
2494
2511
  }
2495
2512
 
@@ -2631,26 +2648,42 @@ export function runCli(args = [], options = {}) {
2631
2648
  return run(parsed.passthroughArgs, options);
2632
2649
  }
2633
2650
 
2634
- const cliOptions = {
2651
+ const cliOptions = withConfigCache({
2635
2652
  ...options,
2636
- ...parsed.options
2637
- };
2653
+ ...parsed.options,
2654
+ extraArgs: parsed.passthroughArgs,
2655
+ preserveNativeDefaults: Boolean(parsed.options.noConfig)
2656
+ });
2638
2657
  const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
2639
- const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, cliOptions);
2640
- const lintPaths = filteredLintPaths(targets, cliOptions);
2641
- if (lintPaths.length === 0) {
2642
- return completedRunResult(ignoredDiagnostics.length > 0 ? 1 : 0, "", "", options);
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
- return withTemporaryConfig(cliOptions, (resolvedOptions) => {
2646
- const cliArgs = buildRunCliArgs(lintPaths, resolvedOptions, parsed.passthroughArgs);
2647
- return run(cliArgs, {
2648
- ...resolvedOptions,
2649
- stdio: options.stdio,
2650
- encoding: options.encoding,
2651
- input: options.input
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 = {}) {
@@ -2866,15 +2899,35 @@ function booleanFlagValue(arg) {
2866
2899
  }
2867
2900
 
2868
2901
  export function lintFiles(paths, options = {}) {
2902
+ options = withConfigCache(options);
2869
2903
  const targets = lintTargetsForInput(paths, options);
2870
2904
  const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
2871
2905
  const lintPaths = filteredLintPaths(targets, options);
2872
2906
  if (lintPaths.length === 0) {
2873
- return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: ignoredDiagnostics.length > 0 ? 1 : 0 };
2907
+ return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
2908
+ }
2909
+
2910
+ const configRuns = nativeConfigRunsForFiles(expandNativeConfigRunPaths(lintPaths, options), options);
2911
+ if (configRuns) {
2912
+ const report = mergeNativeReports(
2913
+ configRuns.map(({ paths: runPaths, options: runOptions }) => runNativeLintReport(runPaths, runOptions))
2914
+ );
2915
+ return finalizeLintReport(report, ignoredDiagnostics, options);
2874
2916
  }
2875
2917
 
2918
+ const report = runNativeLintReport(lintPaths, options);
2919
+ return finalizeLintReport(report, ignoredDiagnostics, options);
2920
+ }
2921
+
2922
+ function expandNativeConfigRunPaths(paths, options) {
2923
+ const cwd = options.cwd ?? process.cwd();
2924
+ const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
2925
+ return paths.flatMap((path) => expandLintTarget(path, cwd, patterns) ?? [path]);
2926
+ }
2927
+
2928
+ function runNativeLintReport(paths, options) {
2876
2929
  return withTemporaryConfig(options, (resolvedOptions) => {
2877
- const cliArgs = buildLintArgs(lintPaths, resolvedOptions);
2930
+ const cliArgs = buildLintArgs(paths, resolvedOptions);
2878
2931
  const result = run(cliArgs, { ...resolvedOptions, stdio: undefined, encoding: "utf8" });
2879
2932
 
2880
2933
  if (result.error) {
@@ -2897,17 +2950,6 @@ export function lintFiles(paths, options = {}) {
2897
2950
  }
2898
2951
 
2899
2952
  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
2953
  if (stderr) {
2912
2954
  Object.defineProperty(report, "stderr", {
2913
2955
  value: stderr,
@@ -2919,7 +2961,124 @@ export function lintFiles(paths, options = {}) {
2919
2961
  });
2920
2962
  }
2921
2963
 
2964
+ function finalizeLintReport(report, ignoredDiagnostics, options) {
2965
+ report.diagnostics = [
2966
+ ...(report.diagnostics ?? []),
2967
+ ...ignoredDiagnostics
2968
+ ];
2969
+ if (!options.deferDiagnosticConfigFiltering) {
2970
+ report.filePaths = normalizeReportFilePaths(report.filePaths, options);
2971
+ report.diagnostics = normalizeDiagnosticFilePaths(report.diagnostics, options);
2972
+ report.outputs = normalizeReportOutputs(report.outputs, options);
2973
+ report.diagnostics = normalizeReportDiagnostics(report.diagnostics, options);
2974
+ report.exitCode = exitCodeForDiagnostics(report.diagnostics);
2975
+ }
2976
+ return report;
2977
+ }
2978
+
2979
+ function nativeConfigRunsForFiles(paths, options) {
2980
+ if (options.noConfig && !options.baseConfig && !options.overrideConfig) {
2981
+ return undefined;
2982
+ }
2983
+
2984
+ const groups = new Map();
2985
+ let hasConfigSource = Boolean(options.baseConfig || options.overrideConfig);
2986
+ for (const filePath of paths) {
2987
+ const matchPath = paths.length === 1
2988
+ ? options.filePath ?? options.filename ?? filePath
2989
+ : filePath;
2990
+ const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
2991
+ const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
2992
+ hasConfigSource ||= configured;
2993
+ let rules = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath).rules : undefined;
2994
+ if (configured && options.rules) {
2995
+ rules = selectedRulesWithConfigOptions(rules, options.rules);
2996
+ }
2997
+ const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
2998
+ if (!groups.has(signature)) {
2999
+ groups.set(signature, { paths: [], rules, configured });
3000
+ }
3001
+ groups.get(signature).paths.push(filePath);
3002
+ }
3003
+
3004
+ if (!hasConfigSource) {
3005
+ return undefined;
3006
+ }
3007
+
3008
+ return [...groups.values()].map((group) => {
3009
+ if (!group.configured) {
3010
+ return {
3011
+ paths: group.paths,
3012
+ options: {
3013
+ ...options,
3014
+ config: undefined,
3015
+ noConfig: true,
3016
+ baseConfig: undefined,
3017
+ overrideConfig: undefined
3018
+ }
3019
+ };
3020
+ }
3021
+
3022
+ return {
3023
+ paths: group.paths,
3024
+ options: {
3025
+ ...options,
3026
+ config: undefined,
3027
+ noConfig: true,
3028
+ baseConfig: undefined,
3029
+ overrideConfig: { rules: allDisabledNativeRules(group.rules) },
3030
+ forceMaterializedConfig: true
3031
+ }
3032
+ };
3033
+ });
3034
+ }
3035
+
3036
+ function allDisabledNativeRules(rules) {
3037
+ return {
3038
+ ...Object.fromEntries(NATIVE_RULE_IDS.map((rule) => [rule, "off"])),
3039
+ ...rules
3040
+ };
3041
+ }
3042
+
3043
+ function selectedRulesWithConfigOptions(configRules, selectedRules) {
3044
+ const selected = rulesFromNativeRuleList(selectedRules);
3045
+ return Object.fromEntries(
3046
+ Object.keys(selected).map((rule) => [
3047
+ rule,
3048
+ configRules?.[rule] != null && ruleConfigSeverity(configRules[rule]) > 0
3049
+ ? configRules[rule]
3050
+ : selected[rule]
3051
+ ])
3052
+ );
3053
+ }
3054
+
3055
+ function stableConfigSignature(value) {
3056
+ if (Array.isArray(value)) {
3057
+ return `[${value.map((item) => stableConfigSignature(item)).join(",")}]`;
3058
+ }
3059
+ if (value && typeof value === "object") {
3060
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableConfigSignature(value[key])}`).join(",")}}`;
3061
+ }
3062
+ return JSON.stringify(value);
3063
+ }
3064
+
3065
+ function mergeNativeReports(reports) {
3066
+ const report = {
3067
+ files: reports.reduce((count, item) => count + (item.files ?? 0), 0),
3068
+ filePaths: reports.flatMap((item) => item.filePaths ?? []),
3069
+ diagnostics: reports.flatMap((item) => item.diagnostics ?? []),
3070
+ outputs: reports.flatMap((item) => item.outputs ?? []),
3071
+ exitCode: reports.some((item) => item.exitCode) ? 1 : 0
3072
+ };
3073
+ const stderr = reports.map((item) => item.stderr ?? "").join("");
3074
+ if (stderr) {
3075
+ Object.defineProperty(report, "stderr", { value: stderr, enumerable: false });
3076
+ }
3077
+ return report;
3078
+ }
3079
+
2922
3080
  export function lintText(code, options = {}) {
3081
+ options = withConfigCache(options);
2923
3082
  if (typeof code !== "string") {
2924
3083
  throw new TypeError("code must be a string");
2925
3084
  }
@@ -2940,7 +3099,7 @@ export function lintText(code, options = {}) {
2940
3099
  files: 0,
2941
3100
  filePaths: [],
2942
3101
  diagnostics,
2943
- exitCode: diagnostics.length > 0 ? 1 : 0
3102
+ exitCode: exitCodeForDiagnostics(diagnostics)
2944
3103
  };
2945
3104
  }
2946
3105
 
@@ -2977,22 +3136,28 @@ function buildLintArgs(paths, options) {
2977
3136
  });
2978
3137
  }
2979
3138
 
2980
- function buildRunCliArgs(paths, options, passthroughArgs) {
2981
- return buildNativeLintArgs(paths, {
2982
- ...options,
2983
- extraArgs: passthroughArgs
2984
- });
2985
- }
2986
-
2987
3139
  function buildNativeLintArgs(paths, options) {
2988
3140
  const cliArgs = [];
2989
3141
 
2990
- if (options.config) {
2991
- cliArgs.push(`--config=${options.config}`);
2992
- }
2993
3142
  if (options.noConfig) {
2994
3143
  cliArgs.push("--no-config");
2995
3144
  }
3145
+ if (!options.rules && !options.forceMaterializedConfig && (options.config || options.baseConfig || options.overrideConfig)) {
3146
+ const configForRuleSelection = options.noConfig
3147
+ ? undefined
3148
+ : options.config ? readConfig(options.config, options.cwd, options.configCache) : undefined;
3149
+ const enabledRules = enabledRuleNamesFromConfigs(
3150
+ configForRuleSelection,
3151
+ options.baseConfig,
3152
+ options.overrideConfig
3153
+ );
3154
+ if (enabledRules.length > 0) {
3155
+ cliArgs.push(`--rules=${enabledRules.join(",")}`);
3156
+ }
3157
+ }
3158
+ if (options.config) {
3159
+ cliArgs.push(`--config=${options.config}`);
3160
+ }
2996
3161
  if (options.rules) {
2997
3162
  const rules = Array.isArray(options.rules) ? options.rules.join(",") : options.rules;
2998
3163
  if (rules) {
@@ -3026,6 +3191,12 @@ function mergeLintOptions(base, override) {
3026
3191
  };
3027
3192
  }
3028
3193
 
3194
+ function withConfigCache(options) {
3195
+ // Executable configs are otherwise re-evaluated at every per-file lookup.
3196
+ // Keep the cache on the options object so one public invocation shares it.
3197
+ return options.configCache ? options : { ...options, configCache: new Map() };
3198
+ }
3199
+
3029
3200
  function eslintConstructorOptions(options) {
3030
3201
  const mapped = {
3031
3202
  cwd: options.cwd,
@@ -3078,10 +3249,10 @@ function calculatedConfig(options = {}, filePath) {
3078
3249
  return {
3079
3250
  ...configData,
3080
3251
  rules: {
3081
- ...rulesFromNativeRuleList(options.rules),
3082
3252
  ...rulesFromConfig(options.baseConfig, filePath, options.cwd),
3083
3253
  ...rulesFromFileConfig(options, filePath),
3084
- ...rulesFromConfig(options.overrideConfig, filePath, options.cwd)
3254
+ ...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
3255
+ ...rulesFromNativeRuleList(options.rules)
3085
3256
  }
3086
3257
  };
3087
3258
  }
@@ -3198,8 +3369,8 @@ function rulesFromFileConfig(options, filePath) {
3198
3369
  return {};
3199
3370
  }
3200
3371
 
3201
- const config = readConfig(configPath, options.cwd);
3202
- return rulesFromConfig(config, filePath, options.cwd);
3372
+ const config = readConfig(configPath, options.cwd, options.configCache);
3373
+ return rulesFromConfig(config, filePath, dirname(configPath));
3203
3374
  }
3204
3375
 
3205
3376
  function configDataFromFileConfig(options, filePath) {
@@ -3212,8 +3383,8 @@ function configDataFromFileConfig(options, filePath) {
3212
3383
  return {};
3213
3384
  }
3214
3385
 
3215
- const config = readConfig(configPath, options.cwd);
3216
- return configDataFromConfig(config, filePath, options.cwd);
3386
+ const config = readConfig(configPath, options.cwd, options.configCache);
3387
+ return configDataFromConfig(config, filePath, dirname(configPath));
3217
3388
  }
3218
3389
 
3219
3390
  function lintTargetsForInput(paths, options = {}) {
@@ -3233,8 +3404,17 @@ function defaultLintTargets(options = {}) {
3233
3404
  return ["."];
3234
3405
  }
3235
3406
 
3236
- const targets = filePatternsFromConfig(readConfig(configPath, options.cwd));
3237
- return targets.length > 0 ? targets : ["."];
3407
+ const targets = filePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache));
3408
+ return targets.length > 0
3409
+ ? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
3410
+ : ["."];
3411
+ }
3412
+
3413
+ function resolveConfigPattern(pattern, configDirectory) {
3414
+ if (isAbsolute(pattern)) {
3415
+ return pattern;
3416
+ }
3417
+ return resolvePath(configDirectory, pattern.replace(/^[/\\]/, ""));
3238
3418
  }
3239
3419
 
3240
3420
  function filePatternsFromConfig(config) {
@@ -3288,6 +3468,9 @@ function configPathForOptions(options) {
3288
3468
  }
3289
3469
 
3290
3470
  function configPathForFile(options, filePath) {
3471
+ if (options.noConfig) {
3472
+ return undefined;
3473
+ }
3291
3474
  if (options.config) {
3292
3475
  return resolvePath(options.cwd ?? process.cwd(), options.config);
3293
3476
  }
@@ -3307,66 +3490,20 @@ function configSearchDirectoryForFile(filePath, cwd) {
3307
3490
  }
3308
3491
 
3309
3492
  function configPathFromDirectory(directory) {
3310
- let current = resolvePath(directory);
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));
3493
+ return findConfigPathFromDirectory(directory);
3358
3494
  }
3359
3495
 
3360
3496
  function withTemporaryConfig(options, callback) {
3361
3497
  const fileConfigPath = !options.noConfig ? configPathForOptions(options) : undefined;
3362
- const shouldMaterializeFileConfig = fileConfigPath && isJavaScriptConfigPath(fileConfigPath);
3363
- const fileConfig = shouldMaterializeFileConfig ? readConfig(fileConfigPath, options.cwd) : undefined;
3364
- const inlineRules = {
3365
- ...rulesFromConfig(fileConfig),
3366
- ...rulesFromConfig(options.baseConfig),
3367
- ...rulesFromConfig(options.overrideConfig)
3368
- };
3369
- if (Object.keys(inlineRules).length === 0) {
3498
+ const shouldMaterializeFileConfig = Boolean(fileConfigPath);
3499
+ const fileConfig = shouldMaterializeFileConfig
3500
+ ? readConfig(fileConfigPath, options.cwd, options.configCache)
3501
+ : undefined;
3502
+ const configs = [options.baseConfig, fileConfig, options.overrideConfig];
3503
+ const rules = shouldMaterializeFileConfig
3504
+ ? materializedRulesFromConfigs(...configs)
3505
+ : runtimeRulesFromConfigs(...configs);
3506
+ if (Object.keys(rules).length === 0) {
3370
3507
  if (shouldMaterializeFileConfig) {
3371
3508
  return callback({
3372
3509
  ...options,
@@ -3376,10 +3513,9 @@ function withTemporaryConfig(options, callback) {
3376
3513
  }
3377
3514
  return callback(options);
3378
3515
  }
3379
-
3380
- const rules = runtimeRulesFromConfigs(fileConfig, options.baseConfig, options.overrideConfig);
3381
- const enabledRules = enabledRuleNamesFromConfigs(fileConfig, options.baseConfig, options.overrideConfig);
3382
- if (!hasRuleOptions(rules)) {
3516
+ const enabledRules = enabledRuleNamesFromConfigs(...configs);
3517
+ const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
3518
+ if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
3383
3519
  return callback({
3384
3520
  ...options,
3385
3521
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -3389,13 +3525,14 @@ function withTemporaryConfig(options, callback) {
3389
3525
  }
3390
3526
 
3391
3527
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
3392
- const configPath = join(tmp, "utoo.json");
3528
+ const configPath = join(tmp, "utlint.config.json");
3393
3529
  try {
3394
3530
  writeFileSync(configPath, JSON.stringify({ rules }));
3395
3531
  return callback({
3396
3532
  ...options,
3397
3533
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
3398
- noConfig: false
3534
+ noConfig: false,
3535
+ rules: options.forceMaterializedConfig ? undefined : options.rules
3399
3536
  });
3400
3537
  } finally {
3401
3538
  rmSync(tmp, { recursive: true, force: true });
@@ -3416,6 +3553,10 @@ function runtimeRulesFromConfigs(...configs) {
3416
3553
  return rules;
3417
3554
  }
3418
3555
 
3556
+ function materializedRulesFromConfigs(...configs) {
3557
+ return runtimeRulesFromConfigs(...configs);
3558
+ }
3559
+
3419
3560
  function runtimeRulesFromConfig(config) {
3420
3561
  if (!config) {
3421
3562
  return {};
@@ -3429,9 +3570,7 @@ function runtimeRulesFromConfig(config) {
3429
3570
 
3430
3571
  const rules = {};
3431
3572
  for (const [rule, value] of Object.entries(rulesFromConfig(config))) {
3432
- if (ruleConfigSeverity(value) > 0) {
3433
- rules[rule] = value;
3434
- }
3573
+ rules[rule] = value;
3435
3574
  }
3436
3575
  return rules;
3437
3576
  }
@@ -3512,19 +3651,19 @@ function reportToESLintResults(report, textOptions = {}) {
3512
3651
  }
3513
3652
 
3514
3653
  function normalizeReportDiagnostics(diagnostics, options = {}) {
3515
- const filterUnconfiguredRules = hasRuleConfigSource(options);
3516
3654
  return (diagnostics ?? []).flatMap((diagnostic) => {
3517
3655
  if (!diagnostic?.ruleId) {
3518
3656
  return [diagnostic];
3519
3657
  }
3520
- if (diagnostic.ruleId === "io") {
3658
+ if (diagnostic.ruleId === "io" || diagnostic.ruleId === "parse") {
3521
3659
  return [diagnostic];
3522
3660
  }
3523
3661
 
3524
3662
  const filePath = normalizeESLintFilePath(diagnostic.filePath, options.cwd);
3663
+ const filterUnconfiguredRules = hasRuleConfigSource(options, filePath);
3525
3664
  const ruleSeverities = ruleSeverityMapForOptions(options, filePath);
3526
3665
  const severity = ruleSeverities?.get(diagnostic.ruleId);
3527
- if (filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
3666
+ if (!options.preserveNativeDefaults && filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
3528
3667
  return [];
3529
3668
  }
3530
3669
  if (severity === 0) {
@@ -3555,15 +3694,17 @@ function normalizeDiagnosticFilePaths(diagnostics, options = {}) {
3555
3694
  }));
3556
3695
  }
3557
3696
 
3558
- function hasRuleConfigSource(options = {}) {
3559
- if (options.noConfig || options.baseConfig || options.overrideConfig || options.rules) {
3697
+ function hasRuleConfigSource(options = {}, filePath) {
3698
+ if (options.baseConfig || options.overrideConfig || options.rules) {
3560
3699
  return true;
3561
3700
  }
3562
- return !options.noConfig && Boolean(configPathForOptions(options));
3701
+ return !options.noConfig && Boolean(
3702
+ filePath ? configPathForFile(options, filePath) : configPathForOptions(options)
3703
+ );
3563
3704
  }
3564
3705
 
3565
3706
  function exitCodeForDiagnostics(diagnostics) {
3566
- return (diagnostics?.length ?? 0) > 0 ? 1 : 0;
3707
+ return diagnostics?.some((diagnostic) => diagnostic?.severity === "error" || diagnostic?.severity === 2) ? 1 : 0;
3567
3708
  }
3568
3709
 
3569
3710
  function throwOnUnmatchedPatternDiagnostics(report, options = {}) {
@@ -3883,7 +4024,21 @@ function ignorePatternsFromFileConfig(options, filePath) {
3883
4024
  return [];
3884
4025
  }
3885
4026
 
3886
- return ignorePatternsFromConfig(readConfig(configPath, options.cwd));
4027
+ const cwd = options.cwd ?? process.cwd();
4028
+ return ignorePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache)).map((pattern) =>
4029
+ rebaseConfigPattern(pattern, dirname(configPath), cwd)
4030
+ );
4031
+ }
4032
+
4033
+ function rebaseConfigPattern(pattern, configDirectory, cwd) {
4034
+ const negated = pattern.startsWith("!");
4035
+ const value = negated ? pattern.slice(1) : pattern;
4036
+ const absolute = resolveConfigPattern(value, configDirectory);
4037
+ const relativePattern = normalizePath(relative(resolvePath(cwd), absolute));
4038
+ const rebased = relativePattern === ".." || relativePattern.startsWith("../")
4039
+ ? normalizePath(absolute)
4040
+ : relativePattern;
4041
+ return negated ? `!${rebased}` : rebased;
3887
4042
  }
3888
4043
 
3889
4044
  function ignorePatternsFromConfig(config) {