@utoo/lint 0.3.6 → 0.3.8

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 CHANGED
@@ -244,10 +244,44 @@ const results = await eslint.lintFiles(["src"]);
244
244
  await ESLint.outputFixes(results);
245
245
  ```
246
246
 
247
+ `await eslint.loadFormatter()` and `cliEngine.getFormatter()` default to an
248
+ ESLint-style `stylish` report: aligned columns, blank lines between files,
249
+ diagnostics sorted by line and column, and a summary of fixable errors and
250
+ warnings. Rule messages (including punctuation), result arrays, and exit codes
251
+ are unchanged. Only actual autofixes count toward the `--fix` hint, not editor
252
+ suggestions; missing JSX keys still need a manually chosen stable, unique key.
253
+
254
+ The stylish formatter detects color support on stdout. Pass `color: true` or
255
+ `color: false` to the `ESLint` or `CLIEngine` constructor to override detection.
256
+ Otherwise, `NO_COLOR` or `NODE_DISABLE_COLORS` disables color; `FORCE_COLOR`
257
+ forces it (`0` disables), followed by `CLICOLOR_FORCE` and stdout TTY detection
258
+ (`TERM=dumb` disables automatic color). Constructor `env` overrides also apply.
259
+ JSON, JSON-with-metadata, compact, unix, and native CLI text formats are unchanged.
260
+
247
261
  Raw reports include active diagnostics, suppressed diagnostics, fixed outputs,
248
262
  and an exit code. The compatibility layer also exports `Linter`, `CLIEngine`,
249
263
  `RuleTester`, and `SourceCode` APIs.
250
264
 
265
+ Native JSON diagnostics and ESLint-compatible messages include one-based `line`,
266
+ `column`, `endLine`, and `endColumn` positions for source spans. Columns count
267
+ UTF-16 code units, and the end position is exclusive. Suppressed diagnostics keep
268
+ the same range. I/O errors without a source span omit the end positions; messages
269
+ from older native binaries may also omit them.
270
+
271
+ `ESLint` and `CLIEngine` also discover classic `.eslintrc.*` files (or
272
+ `eslintConfig` in `package.json`) when no native config applies. Legacy
273
+ `extends`, directory cascades, `root`, `overrides`, and ignore patterns are
274
+ resolved per source filename. Precedence is `baseConfig`, project config,
275
+ then `overrideConfig`; `useEslintrc: false` disables discovery. Native config
276
+ files still take priority. The lower-level `lintFiles`, `lintText`, and native
277
+ CLI keep their native-only discovery behavior.
278
+
279
+ Legacy configuration resolution uses `@eslint/eslintrc`. If the project has
280
+ ESLint 8 installed, its configuration resolver is used instead to support
281
+ shared configs that patch ESLint's module resolution. Neither path uses ESLint
282
+ to parse or lint source files: rules still run in utoo-lint. Enabled rules that
283
+ utoo-lint does not support remain errors, not silently skipped checks.
284
+
251
285
  The Node wrapper captures up to 64 MiB from each native stdout and stderr
252
286
  stream by default. Programmatic `run`, `runCli`, and `runFishlint` calls can set
253
287
  `maxBuffer` to another positive byte count. Set `UTOO_LINT_MAX_BUFFER` to apply
package/index.cjs CHANGED
@@ -5,6 +5,8 @@ const { tmpdir } = require("node:os");
5
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 { createLegacyConfigResolver } = require("./lib/legacy-config.cjs");
9
+ const { formatESLintResults } = require("./lib/stylish-formatter.cjs");
8
10
  const {
9
11
  findConfigPath: findConfigPathFromDirectory,
10
12
  readConfig
@@ -15,6 +17,8 @@ const version = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8")
15
17
  const LINTABLE_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"]);
16
18
  const MAX_AUTOFIX_PASSES = 10;
17
19
  const CONFIG_DISCOVERY_CACHE = Symbol("configDiscoveryCache");
20
+ const LEGACY_CONFIG_ENABLED = Symbol("legacyConfigEnabled");
21
+ const LEGACY_CONFIG_RESOLVER = Symbol("legacyConfigResolver");
18
22
  const JS_KEYWORDS = new Set([
19
23
  "await", "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete",
20
24
  "do", "else", "export", "extends", "finally", "for", "function", "if", "import", "in",
@@ -415,6 +419,7 @@ const BUILTIN_RULE_IDS = [
415
419
  "@typescript-eslint/no-duplicate-enum-values",
416
420
  "@typescript-eslint/no-extra-semi",
417
421
  "@typescript-eslint/no-extra-non-null-assertion",
422
+ "@typescript-eslint/no-explicit-any",
418
423
  "@typescript-eslint/no-inferrable-types",
419
424
  "@typescript-eslint/no-invalid-void-type",
420
425
  "@typescript-eslint/no-loop-func",
@@ -472,6 +477,7 @@ const NATIVE_ONLY_RULE_IDS = [
472
477
  ];
473
478
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
474
479
  const FIXABLE_BUILTIN_RULE_IDS = new Set([
480
+ "@typescript-eslint/no-explicit-any",
475
481
  "jest/no-deprecated-functions",
476
482
  "jest/no-jasmine-globals",
477
483
  "no-extra-semi",
@@ -479,6 +485,7 @@ const FIXABLE_BUILTIN_RULE_IDS = new Set([
479
485
  "unused-imports/no-unused-imports"
480
486
  ]);
481
487
  const SUGGESTION_BUILTIN_RULE_IDS = new Set([
488
+ "@typescript-eslint/no-explicit-any",
482
489
  "jest/no-focused-tests"
483
490
  ]);
484
491
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
@@ -579,6 +586,9 @@ class UtooLint {
579
586
 
580
587
  async findConfigFile(filePath) {
581
588
  const options = withConfigCache(eslintConstructorOptions(this.options));
589
+ if (options.legacyConfigFile) {
590
+ return resolvePath(options.cwd ?? process.cwd(), options.legacyConfigFile);
591
+ }
582
592
  if (options.noConfig) {
583
593
  return undefined;
584
594
  }
@@ -617,7 +627,7 @@ class UtooLint {
617
627
  format(results) {
618
628
  return formatResultsByName(results, name, {
619
629
  rulesMeta: (results) => eslint.getRulesMetaForResults(results)
620
- });
630
+ }, eslint.options);
621
631
  }
622
632
  };
623
633
  }
@@ -695,7 +705,7 @@ class CLIEngine {
695
705
 
696
706
  getFormatter(name = "stylish") {
697
707
  return (results) => {
698
- return formatResultsByName(results, name);
708
+ return formatResultsByName(results, name, {}, this.options);
699
709
  };
700
710
  }
701
711
 
@@ -1207,6 +1217,9 @@ function lintFiles(paths, options = {}) {
1207
1217
  if (usesDefaultTargets) {
1208
1218
  lintPaths = filterDefaultConfigLintPaths(lintPaths, options);
1209
1219
  }
1220
+ if (options[LEGACY_CONFIG_ENABLED] && (!options.noConfig || options.legacyConfigFile) && !options.deferDiagnosticConfigFiltering) {
1221
+ lintPaths = expandNativeConfigRunPaths(lintPaths, options).filter((filePath) => !isPathIgnored(filePath, options));
1222
+ }
1210
1223
  if (lintPaths.length === 0) {
1211
1224
  return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, suppressedDiagnostics: [], exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
1212
1225
  }
@@ -1230,6 +1243,10 @@ function lintFiles(paths, options = {}) {
1230
1243
  }
1231
1244
 
1232
1245
  function nativeFlatConfigOptions(options) {
1246
+ if (defersLegacyIgnores(options)) {
1247
+ // A legacy config may cascade from a nested directory even with baseConfig.
1248
+ return undefined;
1249
+ }
1233
1250
  if (options.rules || options.extraArgs?.some((arg) => arg.startsWith("--") && arg !== "--fix" && arg !== "--fix-dry-run")) {
1234
1251
  return undefined;
1235
1252
  }
@@ -1301,7 +1318,7 @@ function nativeSerializableConfig(config) {
1301
1318
 
1302
1319
  function expandNativeConfigRunPaths(paths, options) {
1303
1320
  const cwd = options.cwd ?? process.cwd();
1304
- const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
1321
+ const patterns = ignoreDisabled(options) || defersLegacyIgnores(options) ? [] : ignorePatternsForOptions(options, cwd);
1305
1322
  return paths.flatMap((path) => expandLintTarget(path, cwd, patterns) ?? [path]);
1306
1323
  }
1307
1324
 
@@ -1359,7 +1376,7 @@ function finalizeLintReport(report, ignoredDiagnostics, options) {
1359
1376
  }
1360
1377
 
1361
1378
  function nativeConfigRunsForFiles(paths, options) {
1362
- if (options.noConfig && !options.baseConfig && !options.overrideConfig) {
1379
+ if (options.noConfig && !options.legacyConfigFile && !options.baseConfig && !options.overrideConfig) {
1363
1380
  return undefined;
1364
1381
  }
1365
1382
 
@@ -1370,7 +1387,7 @@ function nativeConfigRunsForFiles(paths, options) {
1370
1387
  ? options.filePath ?? options.filename ?? filePath
1371
1388
  : filePath;
1372
1389
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
1373
- const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
1390
+ const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath || legacyConfigForFile(options, matchPath));
1374
1391
  hasConfigSource ||= configured;
1375
1392
  const calculated = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath) : {};
1376
1393
  let rules = calculated.rules;
@@ -1473,7 +1490,9 @@ function isPureRuleSeverity(config) {
1473
1490
  function allDisabledNativeRules(rules) {
1474
1491
  return {
1475
1492
  ...Object.fromEntries(NATIVE_RULE_IDS.map((rule) => [rule, "off"])),
1476
- ...rules
1493
+ ...Object.fromEntries(Object.entries(rules ?? {}).filter(
1494
+ ([rule, value]) => NATIVE_RULE_IDS.includes(rule) || ruleConfigSeverity(value) > 0
1495
+ ))
1477
1496
  };
1478
1497
  }
1479
1498
 
@@ -1657,12 +1676,14 @@ function withConfigCache(options) {
1657
1676
 
1658
1677
  function eslintConstructorOptions(options) {
1659
1678
  const mapped = {
1679
+ [LEGACY_CONFIG_ENABLED]: true,
1660
1680
  cwd: options.cwd,
1661
1681
  threads: options.threads,
1662
1682
  binary: options.binary,
1663
1683
  env: options.env,
1664
1684
  extraArgs: options.extraArgs,
1665
- config: options.config ?? options.configFile,
1685
+ config: options.config,
1686
+ legacyConfigFile: options.config == null ? options.configFile : undefined,
1666
1687
  ignorePath: options.ignorePath,
1667
1688
  ignorePatterns: options.ignorePatterns,
1668
1689
  noIgnore: options.noIgnore ?? options.ignore === false,
@@ -1679,14 +1700,14 @@ function eslintConstructorOptions(options) {
1679
1700
  }
1680
1701
  if (typeof options.overrideConfigFile === "string") {
1681
1702
  mapped.config = options.overrideConfigFile;
1703
+ mapped.legacyConfigFile = undefined;
1704
+ }
1705
+ if (mapped.config && /(?:^|[/\\\\])\.eslintrc(?:\.(?:js|cjs|json|yaml|yml))?$/.test(mapped.config)) {
1706
+ mapped.legacyConfigFile = mapped.config;
1707
+ mapped.config = undefined;
1682
1708
  }
1683
1709
  if (options.overrideConfig) {
1684
1710
  mapped.overrideConfig = Array.isArray(options.overrideConfig) ? options.overrideConfig : { ...options.overrideConfig };
1685
- if (!Array.isArray(mapped.overrideConfig) && !mapped.overrideConfig.rules && options.baseConfig?.rules) {
1686
- mapped.overrideConfig.rules = options.baseConfig.rules;
1687
- }
1688
- } else if (options.baseConfig?.rules) {
1689
- mapped.overrideConfig = { rules: options.baseConfig.rules };
1690
1711
  }
1691
1712
  return mapped;
1692
1713
  }
@@ -1818,13 +1839,13 @@ function rulesFromConfig(config, filePath, cwd) {
1818
1839
  }
1819
1840
 
1820
1841
  function rulesFromFileConfig(options, filePath) {
1821
- if (options.noConfig) {
1842
+ if (options.noConfig && !options.legacyConfigFile) {
1822
1843
  return {};
1823
1844
  }
1824
1845
 
1825
1846
  const configPath = filePath ? configPathForFile(options, filePath) : configPathForOptions(options);
1826
1847
  if (!configPath) {
1827
- return {};
1848
+ return legacyConfigForFile(options, filePath)?.rules ?? {};
1828
1849
  }
1829
1850
 
1830
1851
  const config = readConfig(configPath, options.cwd, options.configCache);
@@ -1832,19 +1853,40 @@ function rulesFromFileConfig(options, filePath) {
1832
1853
  }
1833
1854
 
1834
1855
  function configDataFromFileConfig(options, filePath) {
1835
- if (options.noConfig) {
1856
+ if (options.noConfig && !options.legacyConfigFile) {
1836
1857
  return {};
1837
1858
  }
1838
1859
 
1839
1860
  const configPath = filePath ? configPathForFile(options, filePath) : configPathForOptions(options);
1840
1861
  if (!configPath) {
1841
- return {};
1862
+ return configDataFromConfig(legacyConfigForFile(options, filePath), filePath, options.cwd);
1842
1863
  }
1843
1864
 
1844
1865
  const config = readConfig(configPath, options.cwd, options.configCache);
1845
1866
  return configDataFromConfig(config, filePath, dirname(configPath));
1846
1867
  }
1847
1868
 
1869
+ function legacyConfigForFile(options, filePath) {
1870
+ if (!options[LEGACY_CONFIG_ENABLED] || (options.noConfig && !options.legacyConfigFile)) return undefined;
1871
+ if (filePath ? configPathForFile(options, filePath) : configPathForOptions(options)) return undefined;
1872
+ const cwd = resolvePath(options.cwd ?? process.cwd());
1873
+ let resolver = options.configCache?.get(LEGACY_CONFIG_RESOLVER);
1874
+ if (!resolver) {
1875
+ resolver = createLegacyConfigResolver(cwd, {
1876
+ configFile: options.legacyConfigFile,
1877
+ useEslintrc: !options.noConfig,
1878
+ ignorePath: options.ignorePath,
1879
+ ignorePatterns: [
1880
+ ...ignorePatternsFromConfig(options.baseConfig),
1881
+ ...normalizeIgnorePatterns(options.ignorePatterns),
1882
+ ...ignorePatternsFromConfig(options.overrideConfig)
1883
+ ]
1884
+ });
1885
+ options.configCache?.set(LEGACY_CONFIG_RESOLVER, resolver);
1886
+ }
1887
+ return resolver(normalizeESLintFilePath(filePath ?? options.filePath ?? options.filename ?? "__placeholder__.js", cwd));
1888
+ }
1889
+
1848
1890
  function lintTargetsForInput(paths, options = {}) {
1849
1891
  if (paths == null) {
1850
1892
  return defaultLintTargets(options);
@@ -1988,6 +2030,7 @@ function normalizeConfigPatterns(patterns) {
1988
2030
  }
1989
2031
 
1990
2032
  function configPathForOptions(options) {
2033
+ if (options.legacyConfigFile) return undefined;
1991
2034
  if (options.config) {
1992
2035
  return resolvePath(options.cwd ?? process.cwd(), options.config);
1993
2036
  }
@@ -1997,6 +2040,7 @@ function configPathForOptions(options) {
1997
2040
  }
1998
2041
 
1999
2042
  function configPathForFile(options, filePath) {
2043
+ if (options.legacyConfigFile) return undefined;
2000
2044
  if (options.noConfig) {
2001
2045
  return undefined;
2002
2046
  }
@@ -4286,7 +4330,7 @@ function isLintableFilePath(filePath) {
4286
4330
  function filteredLintPaths(paths, options = {}) {
4287
4331
  const values = normalizeStringArray(Array.isArray(paths) ? paths : [paths], "paths");
4288
4332
  const cwd = options.cwd ?? process.cwd();
4289
- const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
4333
+ const patterns = ignoreDisabled(options) || defersLegacyIgnores(options) ? [] : ignorePatternsForOptions(options, cwd);
4290
4334
  if (patterns.length === 0 && options.errorOnUnmatchedPattern !== false && !values.some(hasGlobMagic)) {
4291
4335
  return values;
4292
4336
  }
@@ -4405,7 +4449,7 @@ function ignoredLintPathDiagnostics(paths, options = {}) {
4405
4449
 
4406
4450
  const cwd = options.cwd ?? process.cwd();
4407
4451
  const patterns = ignorePatternsForOptions(options, cwd);
4408
- if (patterns.length === 0) {
4452
+ if (patterns.length === 0 && !options[LEGACY_CONFIG_ENABLED]) {
4409
4453
  return [];
4410
4454
  }
4411
4455
 
@@ -4422,7 +4466,7 @@ function ignoredLintPathDiagnostics(paths, options = {}) {
4422
4466
  continue;
4423
4467
  }
4424
4468
 
4425
- if (pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns)) {
4469
+ if (isPathIgnored(filePath, options)) {
4426
4470
  diagnostics.push(ignoredFileDiagnostic(filePath));
4427
4471
  }
4428
4472
  }
@@ -4494,11 +4538,19 @@ function isPathIgnored(filePath, options = {}) {
4494
4538
  }
4495
4539
 
4496
4540
  const cwd = options.cwd ?? process.cwd();
4541
+ const legacyConfig = legacyConfigForFile(options, filePath);
4542
+ if (legacyConfig) {
4543
+ return Boolean(legacyConfig.isIgnored?.(normalizeESLintFilePath(filePath, cwd)));
4544
+ }
4497
4545
  const normalized = normalizeIgnoredPath(filePath, cwd);
4498
4546
  const patterns = ignorePatternsForOptions(options, cwd, filePath);
4499
4547
  return pathIgnoredByPatterns(normalized, patterns);
4500
4548
  }
4501
4549
 
4550
+ function defersLegacyIgnores(options) {
4551
+ return options[LEGACY_CONFIG_ENABLED] && (options.legacyConfigFile || (!options.noConfig && !configPathForOptions(options)));
4552
+ }
4553
+
4502
4554
  function ignoreDisabled(options = {}) {
4503
4555
  return options.noIgnore || options.ignore === false;
4504
4556
  }
@@ -4876,6 +4928,12 @@ function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
4876
4928
  column: diagnostic.column,
4877
4929
  nodeType: null
4878
4930
  };
4931
+ if (diagnostic.endLine != null) {
4932
+ message.endLine = diagnostic.endLine;
4933
+ }
4934
+ if (diagnostic.endColumn != null) {
4935
+ message.endColumn = diagnostic.endColumn;
4936
+ }
4879
4937
  const fixes = (diagnostic.fixes ?? []).map((fix) => ({
4880
4938
  range: fix.range,
4881
4939
  text: fix.text
@@ -4947,31 +5005,7 @@ function maybeFilterQuietResults(results, options = {}) {
4947
5005
  return results;
4948
5006
  }
4949
5007
 
4950
- function formatESLintResults(results) {
4951
- const lines = [];
4952
- let errorCount = 0;
4953
- let warningCount = 0;
4954
-
4955
- for (const result of results) {
4956
- if (result.messages.length === 0) continue;
4957
- lines.push(result.filePath);
4958
- for (const message of result.messages) {
4959
- const severity = message.severity === 2 ? "error" : "warning";
4960
- lines.push(` ${message.line}:${message.column} ${severity} ${message.message} ${message.ruleId}`);
4961
- }
4962
- errorCount += result.errorCount;
4963
- warningCount += result.warningCount;
4964
- }
4965
-
4966
- if (errorCount || warningCount) {
4967
- lines.push("");
4968
- lines.push(`x ${errorCount + warningCount} problem${errorCount + warningCount === 1 ? "" : "s"} (${errorCount} error${errorCount === 1 ? "" : "s"}, ${warningCount} warning${warningCount === 1 ? "" : "s"})`);
4969
- }
4970
-
4971
- return lines.join("\n");
4972
- }
4973
-
4974
- function formatResultsByName(input, name = "stylish", metadata = {}) {
5008
+ function formatResultsByName(input, name = "stylish", metadata = {}, options = {}) {
4975
5009
  const results = formatterResults(input);
4976
5010
  if (name === "json") {
4977
5011
  return JSON.stringify(input);
@@ -4990,7 +5024,7 @@ function formatResultsByName(input, name = "stylish", metadata = {}) {
4990
5024
  if (name === "unix") {
4991
5025
  return formatUnixResults(results);
4992
5026
  }
4993
- return formatESLintResults(results);
5027
+ return formatESLintResults(results, options);
4994
5028
  }
4995
5029
 
4996
5030
  function formatterResults(input) {
package/index.js CHANGED
@@ -6,6 +6,8 @@ import { dirname, extname, isAbsolute, join, relative, resolve as resolvePath }
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
8
  import { resolveBinary } from "./lib/binary.js";
9
+ import { createLegacyConfigResolver } from "./lib/legacy-config.cjs";
10
+ import { formatESLintResults } from "./lib/stylish-formatter.cjs";
9
11
  import {
10
12
  findConfigPath as findConfigPathFromDirectory,
11
13
  readConfig
@@ -18,6 +20,8 @@ export const version = JSON.parse(readFileSync(new URL("./package.json", import.
18
20
  const LINTABLE_EXTENSIONS = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"]);
19
21
  const MAX_AUTOFIX_PASSES = 10;
20
22
  const CONFIG_DISCOVERY_CACHE = Symbol("configDiscoveryCache");
23
+ const LEGACY_CONFIG_ENABLED = Symbol("legacyConfigEnabled");
24
+ const LEGACY_CONFIG_RESOLVER = Symbol("legacyConfigResolver");
21
25
  const JS_KEYWORDS = new Set([
22
26
  "await", "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete",
23
27
  "do", "else", "export", "extends", "finally", "for", "function", "if", "import", "in",
@@ -418,6 +422,7 @@ const BUILTIN_RULE_IDS = [
418
422
  "@typescript-eslint/no-duplicate-enum-values",
419
423
  "@typescript-eslint/no-extra-semi",
420
424
  "@typescript-eslint/no-extra-non-null-assertion",
425
+ "@typescript-eslint/no-explicit-any",
421
426
  "@typescript-eslint/no-inferrable-types",
422
427
  "@typescript-eslint/no-invalid-void-type",
423
428
  "@typescript-eslint/no-loop-func",
@@ -475,6 +480,7 @@ const NATIVE_ONLY_RULE_IDS = [
475
480
  ];
476
481
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
477
482
  const FIXABLE_BUILTIN_RULE_IDS = new Set([
483
+ "@typescript-eslint/no-explicit-any",
478
484
  "jest/no-deprecated-functions",
479
485
  "jest/no-jasmine-globals",
480
486
  "no-extra-semi",
@@ -482,6 +488,7 @@ const FIXABLE_BUILTIN_RULE_IDS = new Set([
482
488
  "unused-imports/no-unused-imports"
483
489
  ]);
484
490
  const SUGGESTION_BUILTIN_RULE_IDS = new Set([
491
+ "@typescript-eslint/no-explicit-any",
485
492
  "jest/no-focused-tests"
486
493
  ]);
487
494
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
@@ -582,6 +589,9 @@ export class UtooLint {
582
589
 
583
590
  async findConfigFile(filePath) {
584
591
  const options = withConfigCache(eslintConstructorOptions(this.options));
592
+ if (options.legacyConfigFile) {
593
+ return resolvePath(options.cwd ?? process.cwd(), options.legacyConfigFile);
594
+ }
585
595
  if (options.noConfig) {
586
596
  return undefined;
587
597
  }
@@ -620,7 +630,7 @@ export class UtooLint {
620
630
  format(results) {
621
631
  return formatResultsByName(results, name, {
622
632
  rulesMeta: (results) => eslint.getRulesMetaForResults(results)
623
- });
633
+ }, eslint.options);
624
634
  }
625
635
  };
626
636
  }
@@ -2594,7 +2604,7 @@ export class CLIEngine {
2594
2604
 
2595
2605
  getFormatter(name = "stylish") {
2596
2606
  return (results) => {
2597
- return formatResultsByName(results, name);
2607
+ return formatResultsByName(results, name, {}, this.options);
2598
2608
  };
2599
2609
  }
2600
2610
 
@@ -3106,6 +3116,9 @@ export function lintFiles(paths, options = {}) {
3106
3116
  if (usesDefaultTargets) {
3107
3117
  lintPaths = filterDefaultConfigLintPaths(lintPaths, options);
3108
3118
  }
3119
+ if (options[LEGACY_CONFIG_ENABLED] && (!options.noConfig || options.legacyConfigFile) && !options.deferDiagnosticConfigFiltering) {
3120
+ lintPaths = expandNativeConfigRunPaths(lintPaths, options).filter((filePath) => !isPathIgnored(filePath, options));
3121
+ }
3109
3122
  if (lintPaths.length === 0) {
3110
3123
  return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, suppressedDiagnostics: [], exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
3111
3124
  }
@@ -3129,6 +3142,10 @@ export function lintFiles(paths, options = {}) {
3129
3142
  }
3130
3143
 
3131
3144
  function nativeFlatConfigOptions(options) {
3145
+ if (defersLegacyIgnores(options)) {
3146
+ // A legacy config may cascade from a nested directory even with baseConfig.
3147
+ return undefined;
3148
+ }
3132
3149
  if (options.rules || options.extraArgs?.some((arg) => arg.startsWith("--") && arg !== "--fix" && arg !== "--fix-dry-run")) {
3133
3150
  return undefined;
3134
3151
  }
@@ -3200,7 +3217,7 @@ function nativeSerializableConfig(config) {
3200
3217
 
3201
3218
  function expandNativeConfigRunPaths(paths, options) {
3202
3219
  const cwd = options.cwd ?? process.cwd();
3203
- const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
3220
+ const patterns = ignoreDisabled(options) || defersLegacyIgnores(options) ? [] : ignorePatternsForOptions(options, cwd);
3204
3221
  return paths.flatMap((path) => expandLintTarget(path, cwd, patterns) ?? [path]);
3205
3222
  }
3206
3223
 
@@ -3258,7 +3275,7 @@ function finalizeLintReport(report, ignoredDiagnostics, options) {
3258
3275
  }
3259
3276
 
3260
3277
  function nativeConfigRunsForFiles(paths, options) {
3261
- if (options.noConfig && !options.baseConfig && !options.overrideConfig) {
3278
+ if (options.noConfig && !options.legacyConfigFile && !options.baseConfig && !options.overrideConfig) {
3262
3279
  return undefined;
3263
3280
  }
3264
3281
 
@@ -3269,7 +3286,7 @@ function nativeConfigRunsForFiles(paths, options) {
3269
3286
  ? options.filePath ?? options.filename ?? filePath
3270
3287
  : filePath;
3271
3288
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
3272
- const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
3289
+ const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath || legacyConfigForFile(options, matchPath));
3273
3290
  hasConfigSource ||= configured;
3274
3291
  const calculated = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath) : {};
3275
3292
  let rules = calculated.rules;
@@ -3372,7 +3389,9 @@ function isPureRuleSeverity(config) {
3372
3389
  function allDisabledNativeRules(rules) {
3373
3390
  return {
3374
3391
  ...Object.fromEntries(NATIVE_RULE_IDS.map((rule) => [rule, "off"])),
3375
- ...rules
3392
+ ...Object.fromEntries(Object.entries(rules ?? {}).filter(
3393
+ ([rule, value]) => NATIVE_RULE_IDS.includes(rule) || ruleConfigSeverity(value) > 0
3394
+ ))
3376
3395
  };
3377
3396
  }
3378
3397
 
@@ -3556,12 +3575,14 @@ function withConfigCache(options) {
3556
3575
 
3557
3576
  function eslintConstructorOptions(options) {
3558
3577
  const mapped = {
3578
+ [LEGACY_CONFIG_ENABLED]: true,
3559
3579
  cwd: options.cwd,
3560
3580
  threads: options.threads,
3561
3581
  binary: options.binary,
3562
3582
  env: options.env,
3563
3583
  extraArgs: options.extraArgs,
3564
- config: options.config ?? options.configFile,
3584
+ config: options.config,
3585
+ legacyConfigFile: options.config == null ? options.configFile : undefined,
3565
3586
  ignorePath: options.ignorePath,
3566
3587
  ignorePatterns: options.ignorePatterns,
3567
3588
  noIgnore: options.noIgnore ?? options.ignore === false,
@@ -3578,14 +3599,14 @@ function eslintConstructorOptions(options) {
3578
3599
  }
3579
3600
  if (typeof options.overrideConfigFile === "string") {
3580
3601
  mapped.config = options.overrideConfigFile;
3602
+ mapped.legacyConfigFile = undefined;
3603
+ }
3604
+ if (mapped.config && /(?:^|[/\\\\])\.eslintrc(?:\.(?:js|cjs|json|yaml|yml))?$/.test(mapped.config)) {
3605
+ mapped.legacyConfigFile = mapped.config;
3606
+ mapped.config = undefined;
3581
3607
  }
3582
3608
  if (options.overrideConfig) {
3583
3609
  mapped.overrideConfig = Array.isArray(options.overrideConfig) ? options.overrideConfig : { ...options.overrideConfig };
3584
- if (!Array.isArray(mapped.overrideConfig) && !mapped.overrideConfig.rules && options.baseConfig?.rules) {
3585
- mapped.overrideConfig.rules = options.baseConfig.rules;
3586
- }
3587
- } else if (options.baseConfig?.rules) {
3588
- mapped.overrideConfig = { rules: options.baseConfig.rules };
3589
3610
  }
3590
3611
  return mapped;
3591
3612
  }
@@ -3717,13 +3738,13 @@ function rulesFromConfig(config, filePath, cwd) {
3717
3738
  }
3718
3739
 
3719
3740
  function rulesFromFileConfig(options, filePath) {
3720
- if (options.noConfig) {
3741
+ if (options.noConfig && !options.legacyConfigFile) {
3721
3742
  return {};
3722
3743
  }
3723
3744
 
3724
3745
  const configPath = filePath ? configPathForFile(options, filePath) : configPathForOptions(options);
3725
3746
  if (!configPath) {
3726
- return {};
3747
+ return legacyConfigForFile(options, filePath)?.rules ?? {};
3727
3748
  }
3728
3749
 
3729
3750
  const config = readConfig(configPath, options.cwd, options.configCache);
@@ -3731,19 +3752,40 @@ function rulesFromFileConfig(options, filePath) {
3731
3752
  }
3732
3753
 
3733
3754
  function configDataFromFileConfig(options, filePath) {
3734
- if (options.noConfig) {
3755
+ if (options.noConfig && !options.legacyConfigFile) {
3735
3756
  return {};
3736
3757
  }
3737
3758
 
3738
3759
  const configPath = filePath ? configPathForFile(options, filePath) : configPathForOptions(options);
3739
3760
  if (!configPath) {
3740
- return {};
3761
+ return configDataFromConfig(legacyConfigForFile(options, filePath), filePath, options.cwd);
3741
3762
  }
3742
3763
 
3743
3764
  const config = readConfig(configPath, options.cwd, options.configCache);
3744
3765
  return configDataFromConfig(config, filePath, dirname(configPath));
3745
3766
  }
3746
3767
 
3768
+ function legacyConfigForFile(options, filePath) {
3769
+ if (!options[LEGACY_CONFIG_ENABLED] || (options.noConfig && !options.legacyConfigFile)) return undefined;
3770
+ if (filePath ? configPathForFile(options, filePath) : configPathForOptions(options)) return undefined;
3771
+ const cwd = resolvePath(options.cwd ?? process.cwd());
3772
+ let resolver = options.configCache?.get(LEGACY_CONFIG_RESOLVER);
3773
+ if (!resolver) {
3774
+ resolver = createLegacyConfigResolver(cwd, {
3775
+ configFile: options.legacyConfigFile,
3776
+ useEslintrc: !options.noConfig,
3777
+ ignorePath: options.ignorePath,
3778
+ ignorePatterns: [
3779
+ ...ignorePatternsFromConfig(options.baseConfig),
3780
+ ...normalizeIgnorePatterns(options.ignorePatterns),
3781
+ ...ignorePatternsFromConfig(options.overrideConfig)
3782
+ ]
3783
+ });
3784
+ options.configCache?.set(LEGACY_CONFIG_RESOLVER, resolver);
3785
+ }
3786
+ return resolver(normalizeESLintFilePath(filePath ?? options.filePath ?? options.filename ?? "__placeholder__.js", cwd));
3787
+ }
3788
+
3747
3789
  function lintTargetsForInput(paths, options = {}) {
3748
3790
  if (paths == null) {
3749
3791
  return defaultLintTargets(options);
@@ -3887,6 +3929,7 @@ function normalizeConfigPatterns(patterns) {
3887
3929
  }
3888
3930
 
3889
3931
  function configPathForOptions(options) {
3932
+ if (options.legacyConfigFile) return undefined;
3890
3933
  if (options.config) {
3891
3934
  return resolvePath(options.cwd ?? process.cwd(), options.config);
3892
3935
  }
@@ -3896,6 +3939,7 @@ function configPathForOptions(options) {
3896
3939
  }
3897
3940
 
3898
3941
  function configPathForFile(options, filePath) {
3942
+ if (options.legacyConfigFile) return undefined;
3899
3943
  if (options.noConfig) {
3900
3944
  return undefined;
3901
3945
  }
@@ -4289,7 +4333,7 @@ function isLintableFilePath(filePath) {
4289
4333
  function filteredLintPaths(paths, options = {}) {
4290
4334
  const values = normalizeStringArray(Array.isArray(paths) ? paths : [paths], "paths");
4291
4335
  const cwd = options.cwd ?? process.cwd();
4292
- const patterns = ignoreDisabled(options) ? [] : ignorePatternsForOptions(options, cwd);
4336
+ const patterns = ignoreDisabled(options) || defersLegacyIgnores(options) ? [] : ignorePatternsForOptions(options, cwd);
4293
4337
  if (patterns.length === 0 && options.errorOnUnmatchedPattern !== false && !values.some(hasGlobMagic)) {
4294
4338
  return values;
4295
4339
  }
@@ -4408,7 +4452,7 @@ function ignoredLintPathDiagnostics(paths, options = {}) {
4408
4452
 
4409
4453
  const cwd = options.cwd ?? process.cwd();
4410
4454
  const patterns = ignorePatternsForOptions(options, cwd);
4411
- if (patterns.length === 0) {
4455
+ if (patterns.length === 0 && !options[LEGACY_CONFIG_ENABLED]) {
4412
4456
  return [];
4413
4457
  }
4414
4458
 
@@ -4425,7 +4469,7 @@ function ignoredLintPathDiagnostics(paths, options = {}) {
4425
4469
  continue;
4426
4470
  }
4427
4471
 
4428
- if (pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns)) {
4472
+ if (isPathIgnored(filePath, options)) {
4429
4473
  diagnostics.push(ignoredFileDiagnostic(filePath));
4430
4474
  }
4431
4475
  }
@@ -4497,11 +4541,19 @@ function isPathIgnored(filePath, options = {}) {
4497
4541
  }
4498
4542
 
4499
4543
  const cwd = options.cwd ?? process.cwd();
4544
+ const legacyConfig = legacyConfigForFile(options, filePath);
4545
+ if (legacyConfig) {
4546
+ return Boolean(legacyConfig.isIgnored?.(normalizeESLintFilePath(filePath, cwd)));
4547
+ }
4500
4548
  const normalized = normalizeIgnoredPath(filePath, cwd);
4501
4549
  const patterns = ignorePatternsForOptions(options, cwd, filePath);
4502
4550
  return pathIgnoredByPatterns(normalized, patterns);
4503
4551
  }
4504
4552
 
4553
+ function defersLegacyIgnores(options) {
4554
+ return options[LEGACY_CONFIG_ENABLED] && (options.legacyConfigFile || (!options.noConfig && !configPathForOptions(options)));
4555
+ }
4556
+
4505
4557
  function ignoreDisabled(options = {}) {
4506
4558
  return options.noIgnore || options.ignore === false;
4507
4559
  }
@@ -4879,6 +4931,12 @@ function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
4879
4931
  column: diagnostic.column,
4880
4932
  nodeType: null
4881
4933
  };
4934
+ if (diagnostic.endLine != null) {
4935
+ message.endLine = diagnostic.endLine;
4936
+ }
4937
+ if (diagnostic.endColumn != null) {
4938
+ message.endColumn = diagnostic.endColumn;
4939
+ }
4882
4940
  const fixes = (diagnostic.fixes ?? []).map((fix) => ({
4883
4941
  range: fix.range,
4884
4942
  text: fix.text
@@ -4950,31 +5008,7 @@ function maybeFilterQuietResults(results, options = {}) {
4950
5008
  return results;
4951
5009
  }
4952
5010
 
4953
- function formatESLintResults(results) {
4954
- const lines = [];
4955
- let errorCount = 0;
4956
- let warningCount = 0;
4957
-
4958
- for (const result of results) {
4959
- if (result.messages.length === 0) continue;
4960
- lines.push(result.filePath);
4961
- for (const message of result.messages) {
4962
- const severity = message.severity === 2 ? "error" : "warning";
4963
- lines.push(` ${message.line}:${message.column} ${severity} ${message.message} ${message.ruleId}`);
4964
- }
4965
- errorCount += result.errorCount;
4966
- warningCount += result.warningCount;
4967
- }
4968
-
4969
- if (errorCount || warningCount) {
4970
- lines.push("");
4971
- lines.push(`x ${errorCount + warningCount} problem${errorCount + warningCount === 1 ? "" : "s"} (${errorCount} error${errorCount === 1 ? "" : "s"}, ${warningCount} warning${warningCount === 1 ? "" : "s"})`);
4972
- }
4973
-
4974
- return lines.join("\n");
4975
- }
4976
-
4977
- function formatResultsByName(input, name = "stylish", metadata = {}) {
5011
+ function formatResultsByName(input, name = "stylish", metadata = {}, options = {}) {
4978
5012
  const results = formatterResults(input);
4979
5013
  if (name === "json") {
4980
5014
  return JSON.stringify(input);
@@ -4993,7 +5027,7 @@ function formatResultsByName(input, name = "stylish", metadata = {}) {
4993
5027
  if (name === "unix") {
4994
5028
  return formatUnixResults(results);
4995
5029
  }
4996
- return formatESLintResults(results);
5030
+ return formatESLintResults(results, options);
4997
5031
  }
4998
5032
 
4999
5033
  function formatterResults(input) {
@@ -0,0 +1,76 @@
1
+ const { createRequire } = require("node:module");
2
+ const { dirname, join, resolve } = require("node:path");
3
+
4
+ // The same reviewed aliases used by the ESLint config migrator.
5
+ const RULE_ALIASES = new Map([
6
+ ["no-native-reassign", "no-global-assign"],
7
+ ["@typescript-eslint/no-invalid-this", "no-invalid-this"]
8
+ ]);
9
+
10
+ // Resolve configuration only. Parsing, rule execution, and fixes stay native.
11
+ function createLegacyConfigResolver(cwd, { ignorePath, ignorePatterns, configFile, useEslintrc = true } = {}) {
12
+ const resolvedIgnorePath = ignorePath ? resolve(cwd, ignorePath) : undefined;
13
+ const resolvedConfigFile = configFile ? resolve(cwd, configFile) : undefined;
14
+ const projectRequire = createRequire(join(cwd, "package.json"));
15
+ let eslintPackage;
16
+ try {
17
+ eslintPackage = projectRequire.resolve("eslint/package.json");
18
+ } catch (error) {
19
+ if (error.code !== "MODULE_NOT_FOUND") throw error;
20
+ }
21
+
22
+ let load;
23
+ if (eslintPackage) {
24
+ const manifest = projectRequire(eslintPackage);
25
+ // Some shared configs patch ESLint's module-resolution internals. Use their
26
+ // own ESLint 8 config resolver when installed, and never recurse into an
27
+ // eslint package alias that points back to @utoo/lint.
28
+ const major = Number(manifest.version.split(".")[0]);
29
+ if (manifest.name === "eslint" && major === 8) {
30
+ const { CLIEngine } = projectRequire(join(dirname(eslintPackage), "lib/cli-engine/cli-engine.js"));
31
+ const engine = new CLIEngine({ cwd, ignorePath: resolvedIgnorePath, ignorePattern: ignorePatterns, configFile: resolvedConfigFile, useEslintrc });
32
+ load = (filePath) => {
33
+ try {
34
+ return { ...engine.getConfigForFile(filePath), isIgnored: (path) => engine.isPathIgnored(path) };
35
+ } catch (error) {
36
+ if (error.messageTemplate === "no-config-found") return undefined;
37
+ throw error;
38
+ }
39
+ };
40
+ }
41
+ }
42
+
43
+ if (!load) {
44
+ const { Legacy } = require("@eslint/eslintrc");
45
+ const eslintJs = require("@eslint/js");
46
+ const factory = new Legacy.CascadingConfigArrayFactory({
47
+ cwd,
48
+ specificConfigPath: resolvedConfigFile,
49
+ useEslintrc,
50
+ ignorePath: resolvedIgnorePath,
51
+ cliConfig: ignorePatterns?.length ? { ignorePatterns } : undefined,
52
+ getEslintRecommendedConfig: () => eslintJs.configs.recommended,
53
+ getEslintAllConfig: () => eslintJs.configs.all
54
+ });
55
+ load = (filePath) => {
56
+ const configs = factory.getConfigArrayForFile(filePath, { ignoreNotFoundError: true });
57
+ if (!configs.some((entry) => entry.type === "config" && entry.filePath)) return undefined;
58
+ const config = configs.extractConfig(filePath);
59
+ return { ...config.toCompatibleObjectAsConfigFileContent(), isIgnored: config.ignores };
60
+ };
61
+ }
62
+
63
+ const configs = new Map();
64
+ return (filePath) => {
65
+ if (!configs.has(filePath)) {
66
+ const config = load(filePath);
67
+ if (config) {
68
+ config.rules = Object.fromEntries(Object.entries(config.rules).map(([rule, value]) => [RULE_ALIASES.get(rule) ?? rule, value]));
69
+ }
70
+ configs.set(filePath, config);
71
+ }
72
+ return configs.get(filePath);
73
+ };
74
+ }
75
+
76
+ module.exports = { createLegacyConfigResolver };
@@ -0,0 +1,79 @@
1
+ const { stripVTControlCharacters } = require("node:util");
2
+
3
+ function formatESLintResults(results, options = {}) {
4
+ const color = shouldUseColor(options);
5
+ const paint = (text, open, close) => color ? `\u001b[${open}m${text}\u001b[${close}m` : text;
6
+ const lines = [""];
7
+ let errorCount = 0;
8
+ let warningCount = 0;
9
+ let fixableErrorCount = 0;
10
+ let fixableWarningCount = 0;
11
+
12
+ for (const result of results) {
13
+ if (result.messages.length === 0) continue;
14
+ lines.push(paint(result.filePath, 4, 24));
15
+ // Sort a copy: formatting must not reorder the caller's results or JSON output.
16
+ const messages = [...result.messages].sort((left, right) =>
17
+ (left.line ?? 0) - (right.line ?? 0) || (left.column ?? 0) - (right.column ?? 0)
18
+ );
19
+ const rows = messages.map((message) => [
20
+ String(message.line || 0),
21
+ String(message.column || 0),
22
+ message.fatal || message.severity === 2 ? "error" : "warning",
23
+ // Unlike ESLint stylish, retain punctuation in the original rule message.
24
+ message.message,
25
+ message.ruleId || "",
26
+ ]);
27
+ const widths = [0, 0, 0, 0];
28
+ for (const row of rows) {
29
+ for (let index = 0; index < widths.length; index += 1) {
30
+ widths[index] = Math.max(widths[index], stripVTControlCharacters(row[index]).length);
31
+ }
32
+ }
33
+ for (const [line, column, severity, message, ruleId] of rows) {
34
+ const location = " ".repeat(widths[0] - line.length)
35
+ + paint(`${line}:${column}`, 2, 22)
36
+ + " ".repeat(widths[1] - column.length);
37
+ const label = paint(severity, severity === "error" ? 31 : 33, 39)
38
+ + " ".repeat(widths[2] - severity.length);
39
+ const padding = " ".repeat(widths[3] - stripVTControlCharacters(message).length);
40
+ const description = ruleId ? `${message}${padding} ${paint(ruleId, 2, 22)}` : message;
41
+ lines.push(` ${location} ${label} ${description}`);
42
+ }
43
+ lines.push("");
44
+ errorCount += result.errorCount;
45
+ warningCount += result.warningCount;
46
+ fixableErrorCount += result.fixableErrorCount ?? 0;
47
+ fixableWarningCount += result.fixableWarningCount ?? 0;
48
+ }
49
+
50
+ if (errorCount || warningCount) {
51
+ const summary = (text) => paint(paint(text, 1, 22), errorCount ? 31 : 33, 39);
52
+ lines.push(summary(
53
+ `✖ ${pluralize(errorCount + warningCount, "problem")} (${pluralize(errorCount, "error")}, ${pluralize(warningCount, "warning")})`
54
+ ));
55
+ if (fixableErrorCount || fixableWarningCount) {
56
+ lines.push(summary(
57
+ ` ${pluralize(fixableErrorCount, "error")} and ${pluralize(fixableWarningCount, "warning")} potentially fixable with the \`--fix\` option.`
58
+ ));
59
+ }
60
+ lines.push("");
61
+ }
62
+
63
+ return lines.join("\n");
64
+ }
65
+
66
+ function pluralize(count, noun) {
67
+ return `${count} ${noun}${count === 1 ? "" : "s"}`;
68
+ }
69
+
70
+ function shouldUseColor(options) {
71
+ if (typeof options.color === "boolean") return options.color;
72
+ const env = options.env ? { ...process.env, ...options.env } : process.env;
73
+ if (env.NO_COLOR !== undefined || env.NODE_DISABLE_COLORS !== undefined) return false;
74
+ if (env.FORCE_COLOR !== undefined) return env.FORCE_COLOR !== "0";
75
+ if (env.CLICOLOR_FORCE && env.CLICOLOR_FORCE !== "0") return true;
76
+ return env.TERM !== "dumb" && Boolean(process.stdout.isTTY);
77
+ }
78
+
79
+ module.exports = { formatESLintResults };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "High-performance JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -76,11 +76,11 @@
76
76
  "use-at-your-own-risk.js"
77
77
  ],
78
78
  "optionalDependencies": {
79
- "@utoo/lint-darwin-arm64": "0.3.6",
80
- "@utoo/lint-darwin-x64": "0.3.6",
81
- "@utoo/lint-linux-arm64": "0.3.6",
82
- "@utoo/lint-linux-x64": "0.3.6",
83
- "@utoo/lint-win32-x64": "0.3.6"
79
+ "@utoo/lint-darwin-arm64": "0.3.8",
80
+ "@utoo/lint-darwin-x64": "0.3.8",
81
+ "@utoo/lint-linux-arm64": "0.3.8",
82
+ "@utoo/lint-linux-x64": "0.3.8",
83
+ "@utoo/lint-win32-x64": "0.3.8"
84
84
  },
85
85
  "dependencies": {
86
86
  "@eslint/eslintrc": "2.1.4",