@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.cjs CHANGED
@@ -2,9 +2,13 @@ const { spawnSync } = require("node:child_process");
2
2
  const { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } = require("node:fs");
3
3
  const { writeFile: writeFileAsync } = require("node:fs/promises");
4
4
  const { tmpdir } = require("node:os");
5
- const { dirname, extname, isAbsolute, join, resolve: resolvePath } = require("node:path");
5
+ const { dirname, extname, isAbsolute, join, relative, resolve: resolvePath } = require("node:path");
6
6
 
7
7
  const { platformPackageName, resolveBinary } = require("./lib/binary.cjs");
8
+ const {
9
+ findConfigPath: findConfigPathFromDirectory,
10
+ readConfig
11
+ } = require("./lib/config-loader.cjs");
8
12
 
9
13
  const version = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8")).version;
10
14
 
@@ -66,20 +70,6 @@ const FISHLINT_DROP_VALUE_FLAGS = new Set([
66
70
  "-E",
67
71
  "-o"
68
72
  ]);
69
- const CONFIG_FILENAMES = ["utoo.json", "utoo-lint.json"];
70
- const JAVASCRIPT_CONFIG_EXTENSIONS = new Set([".js", ".mjs", ".cjs"]);
71
- const JAVASCRIPT_CONFIG_LOADER_SCRIPT = `
72
- import { pathToFileURL } from "node:url";
73
-
74
- const configPath = process.argv[1];
75
- const loaded = await import(pathToFileURL(configPath).href);
76
- const value = loaded.default ?? loaded;
77
- const json = JSON.stringify(value);
78
- if (json === undefined) {
79
- throw new TypeError("config did not export a JSON-serializable value");
80
- }
81
- process.stdout.write(json);
82
- `;
83
73
  const BUILTIN_RULE_IDS = [
84
74
  "accessor-pairs",
85
75
  "array-callback-return",
@@ -417,6 +407,33 @@ const BUILTIN_RULE_IDS = [
417
407
  "@typescript-eslint/prefer-namespace-keyword",
418
408
  "@typescript-eslint/restrict-plus-operands"
419
409
  ];
410
+ const NATIVE_ONLY_RULE_IDS = [
411
+ "@alipay/ant/disallow-typos",
412
+ "@alipay/ant/exhaustive-deps",
413
+ "@alipay/ant/jsx-handler-names",
414
+ "@alipay/ant/no-deprecated-dependence",
415
+ "@alipay/ant/no-deprecated-variable",
416
+ "@alipay/ant/no-import-files-from-pages-in-common",
417
+ "@alipay/ant/no-negative-conditionals",
418
+ "@alipay/ant/no-import-src",
419
+ "@alipay/ant/no-phantom-dependencies",
420
+ "@alipay/ant/no-too-large-file",
421
+ "@alipay/ant/prefer-elseif-end-with-else",
422
+ "@alipay/ant/prefer-catch-unsafe-func-call",
423
+ "@alipay/ant/prefer-click-with-debounce",
424
+ "@alipay/ant/prefer-import-as-required",
425
+ "@alipay/ant/no-spread-params",
426
+ "@alipay/ant/prefer-managed-resource",
427
+ "@alipay/ant/prefer-safe-image-renderer",
428
+ "@alipay/ant/prefer-import-from-stdlib",
429
+ "@alipay/spmLint/use-labeled-spm",
430
+ "@alipay/spmLint/valid-manual-click",
431
+ "@alipay/spmLint/valid-manual-expo",
432
+ "@alipay/spmLint/valid-manual-param",
433
+ "@alipay/spmLint/valid-manual-pv",
434
+ "semantic-errors"
435
+ ];
436
+ const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
420
437
  const FIXABLE_BUILTIN_RULE_IDS = new Set(["no-extra-semi", "@typescript-eslint/no-extra-semi"]);
421
438
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
422
439
 
@@ -467,7 +484,7 @@ class UtooLint {
467
484
  }
468
485
 
469
486
  async lintFiles(patterns, options = {}) {
470
- const mergedOptions = mergeLintOptions(this.options, options);
487
+ const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
471
488
  const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
472
489
  const customRuleFilterMap = customRuleMapForConfig(customRuleConfig, new Map());
473
490
  const nativeOptions = lintOptionsWithoutCustomRules(mergedOptions, customRuleFilterMap);
@@ -487,7 +504,7 @@ class UtooLint {
487
504
  throw new TypeError("code must be a string");
488
505
  }
489
506
 
490
- const mergedOptions = mergeLintOptions(this.options, options);
507
+ const mergedOptions = withConfigCache(mergeLintOptions(this.options, options));
491
508
  const filePath = normalizeESLintFilePath(textFilePathForOptions(options, "<text>"), mergedOptions.cwd);
492
509
  const customRuleConfig = [mergedOptions.baseConfig, mergedOptions.overrideConfig].filter(Boolean);
493
510
  const customRuleMap = customRuleMapForConfig(customRuleConfig, new Map(), filePath, mergedOptions.cwd);
@@ -511,7 +528,7 @@ class UtooLint {
511
528
  }
512
529
 
513
530
  async calculateConfigForFile(filePath) {
514
- return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
531
+ return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
515
532
  }
516
533
 
517
534
  async findConfigFile(filePath) {
@@ -593,7 +610,7 @@ class CLIEngine {
593
610
  }
594
611
 
595
612
  executeOnFiles(patterns) {
596
- const mergedOptions = eslintConstructorOptions(this.options);
613
+ const mergedOptions = withConfigCache(eslintConstructorOptions(this.options));
597
614
  const report = lintFiles(patterns, mergedOptions);
598
615
  throwOnUnmatchedPatternDiagnostics(report, mergedOptions);
599
616
  const results = maybeFilterQuietResults(reportToESLintResults(report, {
@@ -613,10 +630,10 @@ class CLIEngine {
613
630
  typeof filePathOrOptions === "object" && filePathOrOptions !== null
614
631
  ? filePathOrOptions.filePath ?? filePathOrOptions.filename ?? "input.js"
615
632
  : filePathOrOptions;
616
- const mergedOptions = {
633
+ const mergedOptions = withConfigCache({
617
634
  ...eslintConstructorOptions(this.options),
618
635
  ...textOptions
619
- };
636
+ });
620
637
  const report = lintText(code, {
621
638
  ...mergedOptions,
622
639
  filePath
@@ -653,7 +670,7 @@ class CLIEngine {
653
670
  }
654
671
 
655
672
  getConfigForFile(filePath) {
656
- return publicCalculatedConfig(eslintConstructorOptions(this.options), filePath);
673
+ return publicCalculatedConfig(withConfigCache(eslintConstructorOptions(this.options)), filePath);
657
674
  }
658
675
  }
659
676
 
@@ -795,26 +812,42 @@ function runCli(args = [], options = {}) {
795
812
  return run(parsed.passthroughArgs, options);
796
813
  }
797
814
 
798
- const cliOptions = {
815
+ const cliOptions = withConfigCache({
799
816
  ...options,
800
- ...parsed.options
801
- };
817
+ ...parsed.options,
818
+ extraArgs: parsed.passthroughArgs,
819
+ preserveNativeDefaults: Boolean(parsed.options.noConfig)
820
+ });
802
821
  const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
803
- const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, cliOptions);
804
- const lintPaths = filteredLintPaths(targets, cliOptions);
805
- if (lintPaths.length === 0) {
806
- return completedRunResult(ignoredDiagnostics.length > 0 ? 1 : 0, "", "", options);
822
+ const report = lintFiles(targets, cliOptions);
823
+ report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
824
+ report.exitCode = exitCodeForDiagnostics(report.diagnostics);
825
+ const status = report.exitCode ?? exitCodeForDiagnostics(report.diagnostics);
826
+ if (cliOptions.format === "json" || parsed.passthroughArgs.includes("--json")) {
827
+ return completedRunResult(status, `${JSON.stringify(publicCliReport(report))}\n`, report.stderr ?? "", options);
807
828
  }
829
+ return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report)}`, options);
830
+ }
808
831
 
809
- return withTemporaryConfig(cliOptions, (resolvedOptions) => {
810
- const cliArgs = buildRunCliArgs(lintPaths, resolvedOptions, parsed.passthroughArgs);
811
- return run(cliArgs, {
812
- ...resolvedOptions,
813
- stdio: options.stdio,
814
- encoding: options.encoding,
815
- input: options.input
816
- });
817
- });
832
+ function publicCliReport(report) {
833
+ return {
834
+ files: report.files ?? 0,
835
+ filePaths: report.filePaths ?? [],
836
+ diagnostics: report.diagnostics ?? [],
837
+ outputs: report.outputs ?? []
838
+ };
839
+ }
840
+
841
+ function formatNativeTextReport(report) {
842
+ const lines = [];
843
+ for (const diagnostic of report.diagnostics ?? []) {
844
+ const rule = diagnostic.ruleId ? ` [${diagnostic.ruleId}]` : "";
845
+ lines.push(
846
+ `${diagnostic.filePath}:${diagnostic.line ?? 0}:${diagnostic.column ?? 0}: ${diagnostic.severity ?? "error"}: ${diagnostic.message}${rule}`
847
+ );
848
+ }
849
+ lines.push(`${report.files ?? 0} file(s) checked, ${(report.diagnostics ?? []).length} diagnostic(s)`);
850
+ return `${lines.join("\n")}\n`;
818
851
  }
819
852
 
820
853
  function runFishlint(args = [], options = {}) {
@@ -1030,15 +1063,35 @@ function booleanFlagValue(arg) {
1030
1063
  }
1031
1064
 
1032
1065
  function lintFiles(paths, options = {}) {
1066
+ options = withConfigCache(options);
1033
1067
  const targets = lintTargetsForInput(paths, options);
1034
1068
  const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
1035
1069
  const lintPaths = filteredLintPaths(targets, options);
1036
1070
  if (lintPaths.length === 0) {
1037
- return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: ignoredDiagnostics.length > 0 ? 1 : 0 };
1071
+ return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
1072
+ }
1073
+
1074
+ const configRuns = nativeConfigRunsForFiles(expandNativeConfigRunPaths(lintPaths, options), options);
1075
+ if (configRuns) {
1076
+ const report = mergeNativeReports(
1077
+ configRuns.map(({ paths: runPaths, options: runOptions }) => runNativeLintReport(runPaths, runOptions))
1078
+ );
1079
+ return finalizeLintReport(report, ignoredDiagnostics, options);
1038
1080
  }
1039
1081
 
1082
+ const report = runNativeLintReport(lintPaths, options);
1083
+ return finalizeLintReport(report, ignoredDiagnostics, options);
1084
+ }
1085
+
1086
+ function expandNativeConfigRunPaths(paths, options) {
1087
+ const cwd = options.cwd ?? process.cwd();
1088
+ const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
1089
+ return paths.flatMap((path) => expandLintTarget(path, cwd, patterns) ?? [path]);
1090
+ }
1091
+
1092
+ function runNativeLintReport(paths, options) {
1040
1093
  return withTemporaryConfig(options, (resolvedOptions) => {
1041
- const cliArgs = buildLintArgs(lintPaths, resolvedOptions);
1094
+ const cliArgs = buildLintArgs(paths, resolvedOptions);
1042
1095
  const result = run(cliArgs, { ...resolvedOptions, stdio: undefined, encoding: "utf8" });
1043
1096
 
1044
1097
  if (result.error) {
@@ -1061,17 +1114,6 @@ function lintFiles(paths, options = {}) {
1061
1114
  }
1062
1115
 
1063
1116
  report.exitCode = status;
1064
- report.diagnostics = [
1065
- ...(report.diagnostics ?? []),
1066
- ...ignoredDiagnostics
1067
- ];
1068
- if (!resolvedOptions.deferDiagnosticConfigFiltering) {
1069
- report.filePaths = normalizeReportFilePaths(report.filePaths, resolvedOptions);
1070
- report.diagnostics = normalizeDiagnosticFilePaths(report.diagnostics, resolvedOptions);
1071
- report.outputs = normalizeReportOutputs(report.outputs, resolvedOptions);
1072
- report.diagnostics = normalizeReportDiagnostics(report.diagnostics, resolvedOptions);
1073
- report.exitCode = exitCodeForDiagnostics(report.diagnostics);
1074
- }
1075
1117
  if (stderr) {
1076
1118
  Object.defineProperty(report, "stderr", {
1077
1119
  value: stderr,
@@ -1083,7 +1125,124 @@ function lintFiles(paths, options = {}) {
1083
1125
  });
1084
1126
  }
1085
1127
 
1128
+ function finalizeLintReport(report, ignoredDiagnostics, options) {
1129
+ report.diagnostics = [
1130
+ ...(report.diagnostics ?? []),
1131
+ ...ignoredDiagnostics
1132
+ ];
1133
+ if (!options.deferDiagnosticConfigFiltering) {
1134
+ report.filePaths = normalizeReportFilePaths(report.filePaths, options);
1135
+ report.diagnostics = normalizeDiagnosticFilePaths(report.diagnostics, options);
1136
+ report.outputs = normalizeReportOutputs(report.outputs, options);
1137
+ report.diagnostics = normalizeReportDiagnostics(report.diagnostics, options);
1138
+ report.exitCode = exitCodeForDiagnostics(report.diagnostics);
1139
+ }
1140
+ return report;
1141
+ }
1142
+
1143
+ function nativeConfigRunsForFiles(paths, options) {
1144
+ if (options.noConfig && !options.baseConfig && !options.overrideConfig) {
1145
+ return undefined;
1146
+ }
1147
+
1148
+ const groups = new Map();
1149
+ let hasConfigSource = Boolean(options.baseConfig || options.overrideConfig);
1150
+ for (const filePath of paths) {
1151
+ const matchPath = paths.length === 1
1152
+ ? options.filePath ?? options.filename ?? filePath
1153
+ : filePath;
1154
+ const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
1155
+ const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
1156
+ hasConfigSource ||= configured;
1157
+ let rules = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath).rules : undefined;
1158
+ if (configured && options.rules) {
1159
+ rules = selectedRulesWithConfigOptions(rules, options.rules);
1160
+ }
1161
+ const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
1162
+ if (!groups.has(signature)) {
1163
+ groups.set(signature, { paths: [], rules, configured });
1164
+ }
1165
+ groups.get(signature).paths.push(filePath);
1166
+ }
1167
+
1168
+ if (!hasConfigSource) {
1169
+ return undefined;
1170
+ }
1171
+
1172
+ return [...groups.values()].map((group) => {
1173
+ if (!group.configured) {
1174
+ return {
1175
+ paths: group.paths,
1176
+ options: {
1177
+ ...options,
1178
+ config: undefined,
1179
+ noConfig: true,
1180
+ baseConfig: undefined,
1181
+ overrideConfig: undefined
1182
+ }
1183
+ };
1184
+ }
1185
+
1186
+ return {
1187
+ paths: group.paths,
1188
+ options: {
1189
+ ...options,
1190
+ config: undefined,
1191
+ noConfig: true,
1192
+ baseConfig: undefined,
1193
+ overrideConfig: { rules: allDisabledNativeRules(group.rules) },
1194
+ forceMaterializedConfig: true
1195
+ }
1196
+ };
1197
+ });
1198
+ }
1199
+
1200
+ function allDisabledNativeRules(rules) {
1201
+ return {
1202
+ ...Object.fromEntries(NATIVE_RULE_IDS.map((rule) => [rule, "off"])),
1203
+ ...rules
1204
+ };
1205
+ }
1206
+
1207
+ function selectedRulesWithConfigOptions(configRules, selectedRules) {
1208
+ const selected = rulesFromNativeRuleList(selectedRules);
1209
+ return Object.fromEntries(
1210
+ Object.keys(selected).map((rule) => [
1211
+ rule,
1212
+ configRules?.[rule] != null && ruleConfigSeverity(configRules[rule]) > 0
1213
+ ? configRules[rule]
1214
+ : selected[rule]
1215
+ ])
1216
+ );
1217
+ }
1218
+
1219
+ function stableConfigSignature(value) {
1220
+ if (Array.isArray(value)) {
1221
+ return `[${value.map((item) => stableConfigSignature(item)).join(",")}]`;
1222
+ }
1223
+ if (value && typeof value === "object") {
1224
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableConfigSignature(value[key])}`).join(",")}}`;
1225
+ }
1226
+ return JSON.stringify(value);
1227
+ }
1228
+
1229
+ function mergeNativeReports(reports) {
1230
+ const report = {
1231
+ files: reports.reduce((count, item) => count + (item.files ?? 0), 0),
1232
+ filePaths: reports.flatMap((item) => item.filePaths ?? []),
1233
+ diagnostics: reports.flatMap((item) => item.diagnostics ?? []),
1234
+ outputs: reports.flatMap((item) => item.outputs ?? []),
1235
+ exitCode: reports.some((item) => item.exitCode) ? 1 : 0
1236
+ };
1237
+ const stderr = reports.map((item) => item.stderr ?? "").join("");
1238
+ if (stderr) {
1239
+ Object.defineProperty(report, "stderr", { value: stderr, enumerable: false });
1240
+ }
1241
+ return report;
1242
+ }
1243
+
1086
1244
  function lintText(code, options = {}) {
1245
+ options = withConfigCache(options);
1087
1246
  if (typeof code !== "string") {
1088
1247
  throw new TypeError("code must be a string");
1089
1248
  }
@@ -1104,7 +1263,7 @@ function lintText(code, options = {}) {
1104
1263
  files: 0,
1105
1264
  filePaths: [],
1106
1265
  diagnostics,
1107
- exitCode: diagnostics.length > 0 ? 1 : 0
1266
+ exitCode: exitCodeForDiagnostics(diagnostics)
1108
1267
  };
1109
1268
  }
1110
1269
 
@@ -1141,22 +1300,28 @@ function buildLintArgs(paths, options) {
1141
1300
  });
1142
1301
  }
1143
1302
 
1144
- function buildRunCliArgs(paths, options, passthroughArgs) {
1145
- return buildNativeLintArgs(paths, {
1146
- ...options,
1147
- extraArgs: passthroughArgs
1148
- });
1149
- }
1150
-
1151
1303
  function buildNativeLintArgs(paths, options) {
1152
1304
  const cliArgs = [];
1153
1305
 
1154
- if (options.config) {
1155
- cliArgs.push(`--config=${options.config}`);
1156
- }
1157
1306
  if (options.noConfig) {
1158
1307
  cliArgs.push("--no-config");
1159
1308
  }
1309
+ if (!options.rules && !options.forceMaterializedConfig && (options.config || options.baseConfig || options.overrideConfig)) {
1310
+ const configForRuleSelection = options.noConfig
1311
+ ? undefined
1312
+ : options.config ? readConfig(options.config, options.cwd, options.configCache) : undefined;
1313
+ const enabledRules = enabledRuleNamesFromConfigs(
1314
+ configForRuleSelection,
1315
+ options.baseConfig,
1316
+ options.overrideConfig
1317
+ );
1318
+ if (enabledRules.length > 0) {
1319
+ cliArgs.push(`--rules=${enabledRules.join(",")}`);
1320
+ }
1321
+ }
1322
+ if (options.config) {
1323
+ cliArgs.push(`--config=${options.config}`);
1324
+ }
1160
1325
  if (options.rules) {
1161
1326
  const rules = Array.isArray(options.rules) ? options.rules.join(",") : options.rules;
1162
1327
  if (rules) {
@@ -1190,6 +1355,12 @@ function mergeLintOptions(base, override) {
1190
1355
  };
1191
1356
  }
1192
1357
 
1358
+ function withConfigCache(options) {
1359
+ // Executable configs are otherwise re-evaluated at every per-file lookup.
1360
+ // Keep the cache on the options object so one public invocation shares it.
1361
+ return options.configCache ? options : { ...options, configCache: new Map() };
1362
+ }
1363
+
1193
1364
  function eslintConstructorOptions(options) {
1194
1365
  const mapped = {
1195
1366
  cwd: options.cwd,
@@ -1242,10 +1413,10 @@ function calculatedConfig(options = {}, filePath) {
1242
1413
  return {
1243
1414
  ...configData,
1244
1415
  rules: {
1245
- ...rulesFromNativeRuleList(options.rules),
1246
1416
  ...rulesFromConfig(options.baseConfig, filePath, options.cwd),
1247
1417
  ...rulesFromFileConfig(options, filePath),
1248
- ...rulesFromConfig(options.overrideConfig, filePath, options.cwd)
1418
+ ...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
1419
+ ...rulesFromNativeRuleList(options.rules)
1249
1420
  }
1250
1421
  };
1251
1422
  }
@@ -1362,8 +1533,8 @@ function rulesFromFileConfig(options, filePath) {
1362
1533
  return {};
1363
1534
  }
1364
1535
 
1365
- const config = readConfig(configPath, options.cwd);
1366
- return rulesFromConfig(config, filePath, options.cwd);
1536
+ const config = readConfig(configPath, options.cwd, options.configCache);
1537
+ return rulesFromConfig(config, filePath, dirname(configPath));
1367
1538
  }
1368
1539
 
1369
1540
  function configDataFromFileConfig(options, filePath) {
@@ -1376,8 +1547,8 @@ function configDataFromFileConfig(options, filePath) {
1376
1547
  return {};
1377
1548
  }
1378
1549
 
1379
- const config = readConfig(configPath, options.cwd);
1380
- return configDataFromConfig(config, filePath, options.cwd);
1550
+ const config = readConfig(configPath, options.cwd, options.configCache);
1551
+ return configDataFromConfig(config, filePath, dirname(configPath));
1381
1552
  }
1382
1553
 
1383
1554
  function lintTargetsForInput(paths, options = {}) {
@@ -1397,8 +1568,17 @@ function defaultLintTargets(options = {}) {
1397
1568
  return ["."];
1398
1569
  }
1399
1570
 
1400
- const targets = filePatternsFromConfig(readConfig(configPath, options.cwd));
1401
- return targets.length > 0 ? targets : ["."];
1571
+ const targets = filePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache));
1572
+ return targets.length > 0
1573
+ ? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
1574
+ : ["."];
1575
+ }
1576
+
1577
+ function resolveConfigPattern(pattern, configDirectory) {
1578
+ if (isAbsolute(pattern)) {
1579
+ return pattern;
1580
+ }
1581
+ return resolvePath(configDirectory, pattern.replace(/^[/\\]/, ""));
1402
1582
  }
1403
1583
 
1404
1584
  function filePatternsFromConfig(config) {
@@ -1452,6 +1632,9 @@ function configPathForOptions(options) {
1452
1632
  }
1453
1633
 
1454
1634
  function configPathForFile(options, filePath) {
1635
+ if (options.noConfig) {
1636
+ return undefined;
1637
+ }
1455
1638
  if (options.config) {
1456
1639
  return resolvePath(options.cwd ?? process.cwd(), options.config);
1457
1640
  }
@@ -1471,66 +1654,20 @@ function configSearchDirectoryForFile(filePath, cwd) {
1471
1654
  }
1472
1655
 
1473
1656
  function configPathFromDirectory(directory) {
1474
- let current = resolvePath(directory);
1475
- while (true) {
1476
- for (const candidate of CONFIG_FILENAMES) {
1477
- const path = resolvePath(current, candidate);
1478
- if (existsSync(path)) {
1479
- return path;
1480
- }
1481
- }
1482
- const parent = dirname(current);
1483
- if (parent === current) {
1484
- return undefined;
1485
- }
1486
- current = parent;
1487
- }
1488
- }
1489
-
1490
- function readConfig(path, cwd) {
1491
- if (isJavaScriptConfigPath(path)) {
1492
- return readJavaScriptConfig(path, cwd);
1493
- }
1494
-
1495
- try {
1496
- return JSON.parse(readFileSync(path, "utf8"));
1497
- } catch (error) {
1498
- throw new Error(`utoo-lint unable to read config ${path}: ${error.message}`);
1499
- }
1500
- }
1501
-
1502
- function readJavaScriptConfig(path, cwd) {
1503
- const result = spawnSync(process.execPath, ["--input-type=module", "--eval", JAVASCRIPT_CONFIG_LOADER_SCRIPT, path], {
1504
- cwd: cwd ?? process.cwd(),
1505
- encoding: "utf8"
1506
- });
1507
- if (result.error) {
1508
- throw result.error;
1509
- }
1510
- if (result.status !== 0) {
1511
- throw new Error(`utoo-lint unable to read config ${path}: ${(result.stderr ?? "").trim() || "JavaScript config loader failed"}`);
1512
- }
1513
- try {
1514
- return JSON.parse(result.stdout);
1515
- } catch (error) {
1516
- throw new Error(`utoo-lint unable to read config ${path}: ${error.message}`);
1517
- }
1518
- }
1519
-
1520
- function isJavaScriptConfigPath(path) {
1521
- return JAVASCRIPT_CONFIG_EXTENSIONS.has(extname(path));
1657
+ return findConfigPathFromDirectory(directory);
1522
1658
  }
1523
1659
 
1524
1660
  function withTemporaryConfig(options, callback) {
1525
1661
  const fileConfigPath = !options.noConfig ? configPathForOptions(options) : undefined;
1526
- const shouldMaterializeFileConfig = fileConfigPath && isJavaScriptConfigPath(fileConfigPath);
1527
- const fileConfig = shouldMaterializeFileConfig ? readConfig(fileConfigPath, options.cwd) : undefined;
1528
- const inlineRules = {
1529
- ...rulesFromConfig(fileConfig),
1530
- ...rulesFromConfig(options.baseConfig),
1531
- ...rulesFromConfig(options.overrideConfig)
1532
- };
1533
- if (Object.keys(inlineRules).length === 0) {
1662
+ const shouldMaterializeFileConfig = Boolean(fileConfigPath);
1663
+ const fileConfig = shouldMaterializeFileConfig
1664
+ ? readConfig(fileConfigPath, options.cwd, options.configCache)
1665
+ : undefined;
1666
+ const configs = [options.baseConfig, fileConfig, options.overrideConfig];
1667
+ const rules = shouldMaterializeFileConfig
1668
+ ? materializedRulesFromConfigs(...configs)
1669
+ : runtimeRulesFromConfigs(...configs);
1670
+ if (Object.keys(rules).length === 0) {
1534
1671
  if (shouldMaterializeFileConfig) {
1535
1672
  return callback({
1536
1673
  ...options,
@@ -1540,10 +1677,9 @@ function withTemporaryConfig(options, callback) {
1540
1677
  }
1541
1678
  return callback(options);
1542
1679
  }
1543
-
1544
- const rules = runtimeRulesFromConfigs(fileConfig, options.baseConfig, options.overrideConfig);
1545
- const enabledRules = enabledRuleNamesFromConfigs(fileConfig, options.baseConfig, options.overrideConfig);
1546
- if (!hasRuleOptions(rules)) {
1680
+ const enabledRules = enabledRuleNamesFromConfigs(...configs);
1681
+ const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
1682
+ if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
1547
1683
  return callback({
1548
1684
  ...options,
1549
1685
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -1553,13 +1689,14 @@ function withTemporaryConfig(options, callback) {
1553
1689
  }
1554
1690
 
1555
1691
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
1556
- const configPath = join(tmp, "utoo.json");
1692
+ const configPath = join(tmp, "utlint.config.json");
1557
1693
  try {
1558
1694
  writeFileSync(configPath, JSON.stringify({ rules }));
1559
1695
  return callback({
1560
1696
  ...options,
1561
1697
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
1562
- noConfig: false
1698
+ noConfig: false,
1699
+ rules: options.forceMaterializedConfig ? undefined : options.rules
1563
1700
  });
1564
1701
  } finally {
1565
1702
  rmSync(tmp, { recursive: true, force: true });
@@ -1580,6 +1717,10 @@ function runtimeRulesFromConfigs(...configs) {
1580
1717
  return rules;
1581
1718
  }
1582
1719
 
1720
+ function materializedRulesFromConfigs(...configs) {
1721
+ return runtimeRulesFromConfigs(...configs);
1722
+ }
1723
+
1583
1724
  function runtimeRulesFromConfig(config) {
1584
1725
  if (!config) {
1585
1726
  return {};
@@ -1593,9 +1734,7 @@ function runtimeRulesFromConfig(config) {
1593
1734
 
1594
1735
  const rules = {};
1595
1736
  for (const [rule, value] of Object.entries(rulesFromConfig(config))) {
1596
- if (ruleConfigSeverity(value) > 0) {
1597
- rules[rule] = value;
1598
- }
1737
+ rules[rule] = value;
1599
1738
  }
1600
1739
  return rules;
1601
1740
  }
@@ -3509,19 +3648,19 @@ function reportToESLintResults(report, textOptions = {}) {
3509
3648
  }
3510
3649
 
3511
3650
  function normalizeReportDiagnostics(diagnostics, options = {}) {
3512
- const filterUnconfiguredRules = hasRuleConfigSource(options);
3513
3651
  return (diagnostics ?? []).flatMap((diagnostic) => {
3514
3652
  if (!diagnostic?.ruleId) {
3515
3653
  return [diagnostic];
3516
3654
  }
3517
- if (diagnostic.ruleId === "io") {
3655
+ if (diagnostic.ruleId === "io" || diagnostic.ruleId === "parse") {
3518
3656
  return [diagnostic];
3519
3657
  }
3520
3658
 
3521
3659
  const filePath = normalizeESLintFilePath(diagnostic.filePath, options.cwd);
3660
+ const filterUnconfiguredRules = hasRuleConfigSource(options, filePath);
3522
3661
  const ruleSeverities = ruleSeverityMapForOptions(options, filePath);
3523
3662
  const severity = ruleSeverities?.get(diagnostic.ruleId);
3524
- if (filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
3663
+ if (!options.preserveNativeDefaults && filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
3525
3664
  return [];
3526
3665
  }
3527
3666
  if (severity === 0) {
@@ -3552,15 +3691,17 @@ function normalizeDiagnosticFilePaths(diagnostics, options = {}) {
3552
3691
  }));
3553
3692
  }
3554
3693
 
3555
- function hasRuleConfigSource(options = {}) {
3556
- if (options.noConfig || options.baseConfig || options.overrideConfig || options.rules) {
3694
+ function hasRuleConfigSource(options = {}, filePath) {
3695
+ if (options.baseConfig || options.overrideConfig || options.rules) {
3557
3696
  return true;
3558
3697
  }
3559
- return !options.noConfig && Boolean(configPathForOptions(options));
3698
+ return !options.noConfig && Boolean(
3699
+ filePath ? configPathForFile(options, filePath) : configPathForOptions(options)
3700
+ );
3560
3701
  }
3561
3702
 
3562
3703
  function exitCodeForDiagnostics(diagnostics) {
3563
- return (diagnostics?.length ?? 0) > 0 ? 1 : 0;
3704
+ return diagnostics?.some((diagnostic) => diagnostic?.severity === "error" || diagnostic?.severity === 2) ? 1 : 0;
3564
3705
  }
3565
3706
 
3566
3707
  function throwOnUnmatchedPatternDiagnostics(report, options = {}) {
@@ -3880,7 +4021,21 @@ function ignorePatternsFromFileConfig(options, filePath) {
3880
4021
  return [];
3881
4022
  }
3882
4023
 
3883
- return ignorePatternsFromConfig(readConfig(configPath, options.cwd));
4024
+ const cwd = options.cwd ?? process.cwd();
4025
+ return ignorePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache)).map((pattern) =>
4026
+ rebaseConfigPattern(pattern, dirname(configPath), cwd)
4027
+ );
4028
+ }
4029
+
4030
+ function rebaseConfigPattern(pattern, configDirectory, cwd) {
4031
+ const negated = pattern.startsWith("!");
4032
+ const value = negated ? pattern.slice(1) : pattern;
4033
+ const absolute = resolveConfigPattern(value, configDirectory);
4034
+ const relativePattern = normalizePath(relative(resolvePath(cwd), absolute));
4035
+ const rebased = relativePattern === ".." || relativePattern.startsWith("../")
4036
+ ? normalizePath(absolute)
4037
+ : relativePattern;
4038
+ return negated ? `!${rebased}` : rebased;
3884
4039
  }
3885
4040
 
3886
4041
  function ignorePatternsFromConfig(config) {