@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/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
 
@@ -797,24 +814,40 @@ function runCli(args = [], options = {}) {
797
814
 
798
815
  const cliOptions = {
799
816
  ...options,
800
- ...parsed.options
817
+ ...parsed.options,
818
+ extraArgs: parsed.passthroughArgs,
819
+ preserveNativeDefaults: Boolean(parsed.options.noConfig)
801
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 = {}) {
@@ -1034,11 +1067,30 @@ function lintFiles(paths, options = {}) {
1034
1067
  const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
1035
1068
  const lintPaths = filteredLintPaths(targets, options);
1036
1069
  if (lintPaths.length === 0) {
1037
- return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: ignoredDiagnostics.length > 0 ? 1 : 0 };
1070
+ return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
1071
+ }
1072
+
1073
+ const configRuns = nativeConfigRunsForFiles(expandNativeConfigRunPaths(lintPaths, options), options);
1074
+ if (configRuns) {
1075
+ const report = mergeNativeReports(
1076
+ configRuns.map(({ paths: runPaths, options: runOptions }) => runNativeLintReport(runPaths, runOptions))
1077
+ );
1078
+ return finalizeLintReport(report, ignoredDiagnostics, options);
1038
1079
  }
1039
1080
 
1081
+ const report = runNativeLintReport(lintPaths, options);
1082
+ return finalizeLintReport(report, ignoredDiagnostics, options);
1083
+ }
1084
+
1085
+ function expandNativeConfigRunPaths(paths, options) {
1086
+ const cwd = options.cwd ?? process.cwd();
1087
+ const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
1088
+ return paths.flatMap((path) => expandLintTarget(path, cwd, patterns) ?? [path]);
1089
+ }
1090
+
1091
+ function runNativeLintReport(paths, options) {
1040
1092
  return withTemporaryConfig(options, (resolvedOptions) => {
1041
- const cliArgs = buildLintArgs(lintPaths, resolvedOptions);
1093
+ const cliArgs = buildLintArgs(paths, resolvedOptions);
1042
1094
  const result = run(cliArgs, { ...resolvedOptions, stdio: undefined, encoding: "utf8" });
1043
1095
 
1044
1096
  if (result.error) {
@@ -1061,17 +1113,6 @@ function lintFiles(paths, options = {}) {
1061
1113
  }
1062
1114
 
1063
1115
  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
1116
  if (stderr) {
1076
1117
  Object.defineProperty(report, "stderr", {
1077
1118
  value: stderr,
@@ -1083,6 +1124,122 @@ function lintFiles(paths, options = {}) {
1083
1124
  });
1084
1125
  }
1085
1126
 
1127
+ function finalizeLintReport(report, ignoredDiagnostics, options) {
1128
+ report.diagnostics = [
1129
+ ...(report.diagnostics ?? []),
1130
+ ...ignoredDiagnostics
1131
+ ];
1132
+ if (!options.deferDiagnosticConfigFiltering) {
1133
+ report.filePaths = normalizeReportFilePaths(report.filePaths, options);
1134
+ report.diagnostics = normalizeDiagnosticFilePaths(report.diagnostics, options);
1135
+ report.outputs = normalizeReportOutputs(report.outputs, options);
1136
+ report.diagnostics = normalizeReportDiagnostics(report.diagnostics, options);
1137
+ report.exitCode = exitCodeForDiagnostics(report.diagnostics);
1138
+ }
1139
+ return report;
1140
+ }
1141
+
1142
+ function nativeConfigRunsForFiles(paths, options) {
1143
+ if (options.noConfig && !options.baseConfig && !options.overrideConfig) {
1144
+ return undefined;
1145
+ }
1146
+
1147
+ const groups = new Map();
1148
+ let hasConfigSource = Boolean(options.baseConfig || options.overrideConfig);
1149
+ for (const filePath of paths) {
1150
+ const matchPath = paths.length === 1
1151
+ ? options.filePath ?? options.filename ?? filePath
1152
+ : filePath;
1153
+ const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
1154
+ const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
1155
+ hasConfigSource ||= configured;
1156
+ let rules = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath).rules : undefined;
1157
+ if (configured && options.rules) {
1158
+ rules = selectedRulesWithConfigOptions(rules, options.rules);
1159
+ }
1160
+ const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
1161
+ if (!groups.has(signature)) {
1162
+ groups.set(signature, { paths: [], rules, configured });
1163
+ }
1164
+ groups.get(signature).paths.push(filePath);
1165
+ }
1166
+
1167
+ if (!hasConfigSource) {
1168
+ return undefined;
1169
+ }
1170
+
1171
+ return [...groups.values()].map((group) => {
1172
+ if (!group.configured) {
1173
+ return {
1174
+ paths: group.paths,
1175
+ options: {
1176
+ ...options,
1177
+ config: undefined,
1178
+ noConfig: true,
1179
+ baseConfig: undefined,
1180
+ overrideConfig: undefined
1181
+ }
1182
+ };
1183
+ }
1184
+
1185
+ return {
1186
+ paths: group.paths,
1187
+ options: {
1188
+ ...options,
1189
+ config: undefined,
1190
+ noConfig: true,
1191
+ baseConfig: undefined,
1192
+ overrideConfig: { rules: allDisabledNativeRules(group.rules) },
1193
+ forceMaterializedConfig: true
1194
+ }
1195
+ };
1196
+ });
1197
+ }
1198
+
1199
+ function allDisabledNativeRules(rules) {
1200
+ return {
1201
+ ...Object.fromEntries(NATIVE_RULE_IDS.map((rule) => [rule, "off"])),
1202
+ ...rules
1203
+ };
1204
+ }
1205
+
1206
+ function selectedRulesWithConfigOptions(configRules, selectedRules) {
1207
+ const selected = rulesFromNativeRuleList(selectedRules);
1208
+ return Object.fromEntries(
1209
+ Object.keys(selected).map((rule) => [
1210
+ rule,
1211
+ configRules?.[rule] != null && ruleConfigSeverity(configRules[rule]) > 0
1212
+ ? configRules[rule]
1213
+ : selected[rule]
1214
+ ])
1215
+ );
1216
+ }
1217
+
1218
+ function stableConfigSignature(value) {
1219
+ if (Array.isArray(value)) {
1220
+ return `[${value.map((item) => stableConfigSignature(item)).join(",")}]`;
1221
+ }
1222
+ if (value && typeof value === "object") {
1223
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableConfigSignature(value[key])}`).join(",")}}`;
1224
+ }
1225
+ return JSON.stringify(value);
1226
+ }
1227
+
1228
+ function mergeNativeReports(reports) {
1229
+ const report = {
1230
+ files: reports.reduce((count, item) => count + (item.files ?? 0), 0),
1231
+ filePaths: reports.flatMap((item) => item.filePaths ?? []),
1232
+ diagnostics: reports.flatMap((item) => item.diagnostics ?? []),
1233
+ outputs: reports.flatMap((item) => item.outputs ?? []),
1234
+ exitCode: reports.some((item) => item.exitCode) ? 1 : 0
1235
+ };
1236
+ const stderr = reports.map((item) => item.stderr ?? "").join("");
1237
+ if (stderr) {
1238
+ Object.defineProperty(report, "stderr", { value: stderr, enumerable: false });
1239
+ }
1240
+ return report;
1241
+ }
1242
+
1086
1243
  function lintText(code, options = {}) {
1087
1244
  if (typeof code !== "string") {
1088
1245
  throw new TypeError("code must be a string");
@@ -1104,7 +1261,7 @@ function lintText(code, options = {}) {
1104
1261
  files: 0,
1105
1262
  filePaths: [],
1106
1263
  diagnostics,
1107
- exitCode: diagnostics.length > 0 ? 1 : 0
1264
+ exitCode: exitCodeForDiagnostics(diagnostics)
1108
1265
  };
1109
1266
  }
1110
1267
 
@@ -1141,22 +1298,28 @@ function buildLintArgs(paths, options) {
1141
1298
  });
1142
1299
  }
1143
1300
 
1144
- function buildRunCliArgs(paths, options, passthroughArgs) {
1145
- return buildNativeLintArgs(paths, {
1146
- ...options,
1147
- extraArgs: passthroughArgs
1148
- });
1149
- }
1150
-
1151
1301
  function buildNativeLintArgs(paths, options) {
1152
1302
  const cliArgs = [];
1153
1303
 
1154
- if (options.config) {
1155
- cliArgs.push(`--config=${options.config}`);
1156
- }
1157
1304
  if (options.noConfig) {
1158
1305
  cliArgs.push("--no-config");
1159
1306
  }
1307
+ if (!options.rules && !options.forceMaterializedConfig && (options.config || options.baseConfig || options.overrideConfig)) {
1308
+ const configForRuleSelection = options.noConfig
1309
+ ? undefined
1310
+ : options.config ? readConfig(options.config, options.cwd) : undefined;
1311
+ const enabledRules = enabledRuleNamesFromConfigs(
1312
+ configForRuleSelection,
1313
+ options.baseConfig,
1314
+ options.overrideConfig
1315
+ );
1316
+ if (enabledRules.length > 0) {
1317
+ cliArgs.push(`--rules=${enabledRules.join(",")}`);
1318
+ }
1319
+ }
1320
+ if (options.config) {
1321
+ cliArgs.push(`--config=${options.config}`);
1322
+ }
1160
1323
  if (options.rules) {
1161
1324
  const rules = Array.isArray(options.rules) ? options.rules.join(",") : options.rules;
1162
1325
  if (rules) {
@@ -1242,10 +1405,10 @@ function calculatedConfig(options = {}, filePath) {
1242
1405
  return {
1243
1406
  ...configData,
1244
1407
  rules: {
1245
- ...rulesFromNativeRuleList(options.rules),
1246
1408
  ...rulesFromConfig(options.baseConfig, filePath, options.cwd),
1247
1409
  ...rulesFromFileConfig(options, filePath),
1248
- ...rulesFromConfig(options.overrideConfig, filePath, options.cwd)
1410
+ ...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
1411
+ ...rulesFromNativeRuleList(options.rules)
1249
1412
  }
1250
1413
  };
1251
1414
  }
@@ -1363,7 +1526,7 @@ function rulesFromFileConfig(options, filePath) {
1363
1526
  }
1364
1527
 
1365
1528
  const config = readConfig(configPath, options.cwd);
1366
- return rulesFromConfig(config, filePath, options.cwd);
1529
+ return rulesFromConfig(config, filePath, dirname(configPath));
1367
1530
  }
1368
1531
 
1369
1532
  function configDataFromFileConfig(options, filePath) {
@@ -1377,7 +1540,7 @@ function configDataFromFileConfig(options, filePath) {
1377
1540
  }
1378
1541
 
1379
1542
  const config = readConfig(configPath, options.cwd);
1380
- return configDataFromConfig(config, filePath, options.cwd);
1543
+ return configDataFromConfig(config, filePath, dirname(configPath));
1381
1544
  }
1382
1545
 
1383
1546
  function lintTargetsForInput(paths, options = {}) {
@@ -1398,7 +1561,16 @@ function defaultLintTargets(options = {}) {
1398
1561
  }
1399
1562
 
1400
1563
  const targets = filePatternsFromConfig(readConfig(configPath, options.cwd));
1401
- return targets.length > 0 ? targets : ["."];
1564
+ return targets.length > 0
1565
+ ? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
1566
+ : ["."];
1567
+ }
1568
+
1569
+ function resolveConfigPattern(pattern, configDirectory) {
1570
+ if (isAbsolute(pattern)) {
1571
+ return pattern;
1572
+ }
1573
+ return resolvePath(configDirectory, pattern.replace(/^[/\\]/, ""));
1402
1574
  }
1403
1575
 
1404
1576
  function filePatternsFromConfig(config) {
@@ -1452,6 +1624,9 @@ function configPathForOptions(options) {
1452
1624
  }
1453
1625
 
1454
1626
  function configPathForFile(options, filePath) {
1627
+ if (options.noConfig) {
1628
+ return undefined;
1629
+ }
1455
1630
  if (options.config) {
1456
1631
  return resolvePath(options.cwd ?? process.cwd(), options.config);
1457
1632
  }
@@ -1471,66 +1646,18 @@ function configSearchDirectoryForFile(filePath, cwd) {
1471
1646
  }
1472
1647
 
1473
1648
  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));
1649
+ return findConfigPathFromDirectory(directory);
1522
1650
  }
1523
1651
 
1524
1652
  function withTemporaryConfig(options, callback) {
1525
1653
  const fileConfigPath = !options.noConfig ? configPathForOptions(options) : undefined;
1526
- const shouldMaterializeFileConfig = fileConfigPath && isJavaScriptConfigPath(fileConfigPath);
1654
+ const shouldMaterializeFileConfig = Boolean(fileConfigPath);
1527
1655
  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) {
1656
+ const configs = [options.baseConfig, fileConfig, options.overrideConfig];
1657
+ const rules = shouldMaterializeFileConfig
1658
+ ? materializedRulesFromConfigs(...configs)
1659
+ : runtimeRulesFromConfigs(...configs);
1660
+ if (Object.keys(rules).length === 0) {
1534
1661
  if (shouldMaterializeFileConfig) {
1535
1662
  return callback({
1536
1663
  ...options,
@@ -1540,10 +1667,9 @@ function withTemporaryConfig(options, callback) {
1540
1667
  }
1541
1668
  return callback(options);
1542
1669
  }
1543
-
1544
- const rules = runtimeRulesFromConfigs(fileConfig, options.baseConfig, options.overrideConfig);
1545
- const enabledRules = enabledRuleNamesFromConfigs(fileConfig, options.baseConfig, options.overrideConfig);
1546
- if (!hasRuleOptions(rules)) {
1670
+ const enabledRules = enabledRuleNamesFromConfigs(...configs);
1671
+ const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
1672
+ if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
1547
1673
  return callback({
1548
1674
  ...options,
1549
1675
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -1553,13 +1679,14 @@ function withTemporaryConfig(options, callback) {
1553
1679
  }
1554
1680
 
1555
1681
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
1556
- const configPath = join(tmp, "utoo.json");
1682
+ const configPath = join(tmp, "utlint.config.json");
1557
1683
  try {
1558
1684
  writeFileSync(configPath, JSON.stringify({ rules }));
1559
1685
  return callback({
1560
1686
  ...options,
1561
1687
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
1562
- noConfig: false
1688
+ noConfig: false,
1689
+ rules: options.forceMaterializedConfig ? undefined : options.rules
1563
1690
  });
1564
1691
  } finally {
1565
1692
  rmSync(tmp, { recursive: true, force: true });
@@ -1580,6 +1707,10 @@ function runtimeRulesFromConfigs(...configs) {
1580
1707
  return rules;
1581
1708
  }
1582
1709
 
1710
+ function materializedRulesFromConfigs(...configs) {
1711
+ return runtimeRulesFromConfigs(...configs);
1712
+ }
1713
+
1583
1714
  function runtimeRulesFromConfig(config) {
1584
1715
  if (!config) {
1585
1716
  return {};
@@ -1593,9 +1724,7 @@ function runtimeRulesFromConfig(config) {
1593
1724
 
1594
1725
  const rules = {};
1595
1726
  for (const [rule, value] of Object.entries(rulesFromConfig(config))) {
1596
- if (ruleConfigSeverity(value) > 0) {
1597
- rules[rule] = value;
1598
- }
1727
+ rules[rule] = value;
1599
1728
  }
1600
1729
  return rules;
1601
1730
  }
@@ -3509,19 +3638,19 @@ function reportToESLintResults(report, textOptions = {}) {
3509
3638
  }
3510
3639
 
3511
3640
  function normalizeReportDiagnostics(diagnostics, options = {}) {
3512
- const filterUnconfiguredRules = hasRuleConfigSource(options);
3513
3641
  return (diagnostics ?? []).flatMap((diagnostic) => {
3514
3642
  if (!diagnostic?.ruleId) {
3515
3643
  return [diagnostic];
3516
3644
  }
3517
- if (diagnostic.ruleId === "io") {
3645
+ if (diagnostic.ruleId === "io" || diagnostic.ruleId === "parse") {
3518
3646
  return [diagnostic];
3519
3647
  }
3520
3648
 
3521
3649
  const filePath = normalizeESLintFilePath(diagnostic.filePath, options.cwd);
3650
+ const filterUnconfiguredRules = hasRuleConfigSource(options, filePath);
3522
3651
  const ruleSeverities = ruleSeverityMapForOptions(options, filePath);
3523
3652
  const severity = ruleSeverities?.get(diagnostic.ruleId);
3524
- if (filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
3653
+ if (!options.preserveNativeDefaults && filterUnconfiguredRules && !ruleSeverities?.has(diagnostic.ruleId)) {
3525
3654
  return [];
3526
3655
  }
3527
3656
  if (severity === 0) {
@@ -3552,15 +3681,17 @@ function normalizeDiagnosticFilePaths(diagnostics, options = {}) {
3552
3681
  }));
3553
3682
  }
3554
3683
 
3555
- function hasRuleConfigSource(options = {}) {
3556
- if (options.noConfig || options.baseConfig || options.overrideConfig || options.rules) {
3684
+ function hasRuleConfigSource(options = {}, filePath) {
3685
+ if (options.baseConfig || options.overrideConfig || options.rules) {
3557
3686
  return true;
3558
3687
  }
3559
- return !options.noConfig && Boolean(configPathForOptions(options));
3688
+ return !options.noConfig && Boolean(
3689
+ filePath ? configPathForFile(options, filePath) : configPathForOptions(options)
3690
+ );
3560
3691
  }
3561
3692
 
3562
3693
  function exitCodeForDiagnostics(diagnostics) {
3563
- return (diagnostics?.length ?? 0) > 0 ? 1 : 0;
3694
+ return diagnostics?.some((diagnostic) => diagnostic?.severity === "error" || diagnostic?.severity === 2) ? 1 : 0;
3564
3695
  }
3565
3696
 
3566
3697
  function throwOnUnmatchedPatternDiagnostics(report, options = {}) {
@@ -3880,7 +4011,21 @@ function ignorePatternsFromFileConfig(options, filePath) {
3880
4011
  return [];
3881
4012
  }
3882
4013
 
3883
- return ignorePatternsFromConfig(readConfig(configPath, options.cwd));
4014
+ const cwd = options.cwd ?? process.cwd();
4015
+ return ignorePatternsFromConfig(readConfig(configPath, options.cwd)).map((pattern) =>
4016
+ rebaseConfigPattern(pattern, dirname(configPath), cwd)
4017
+ );
4018
+ }
4019
+
4020
+ function rebaseConfigPattern(pattern, configDirectory, cwd) {
4021
+ const negated = pattern.startsWith("!");
4022
+ const value = negated ? pattern.slice(1) : pattern;
4023
+ const absolute = resolveConfigPattern(value, configDirectory);
4024
+ const relativePattern = normalizePath(relative(resolvePath(cwd), absolute));
4025
+ const rebased = relativePattern === ".." || relativePattern.startsWith("../")
4026
+ ? normalizePath(absolute)
4027
+ : relativePattern;
4028
+ return negated ? `!${rebased}` : rebased;
3884
4029
  }
3885
4030
 
3886
4031
  function ignorePatternsFromConfig(config) {
package/index.d.ts CHANGED
@@ -11,6 +11,10 @@ export interface LintMessage {
11
11
  suggestions?: LintSuggestion[];
12
12
  }
13
13
 
14
+ export interface LintDiagnostic extends Omit<LintMessage, "severity"> {
15
+ severity: "warning" | "error";
16
+ }
17
+
14
18
  export interface LintFix {
15
19
  range: [number, number];
16
20
  text: string;
@@ -37,7 +41,7 @@ export interface LintResult {
37
41
  export interface LintReport {
38
42
  files: number;
39
43
  filePaths: string[];
40
- diagnostics: LintMessage[];
44
+ diagnostics: LintDiagnostic[];
41
45
  outputs?: Array<{ filePath: string; output: string }>;
42
46
  exitCode: number;
43
47
  stderr?: string;