@utoo/lint 0.2.8 → 0.3.4

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
@@ -77,6 +77,7 @@ const BUILTIN_RULE_IDS = [
77
77
  "block-scoped-var",
78
78
  "camelcase",
79
79
  "capitalized-comments",
80
+ "class-methods-use-this",
80
81
  "complexity",
81
82
  "consistent-return",
82
83
  "consistent-this",
@@ -168,6 +169,7 @@ const BUILTIN_RULE_IDS = [
168
169
  "no-inline-comments",
169
170
  "no-inner-declarations",
170
171
  "no-invalid-regexp",
172
+ "no-invalid-this",
171
173
  "no-irregular-whitespace",
172
174
  "no-iterator",
173
175
  "no-label-var",
@@ -176,6 +178,7 @@ const BUILTIN_RULE_IDS = [
176
178
  "no-lonely-if",
177
179
  "no-loop-func",
178
180
  "no-loss-of-precision",
181
+ "no-magic-numbers",
179
182
  "no-mixed-spaces-and-tabs",
180
183
  "no-misleading-character-class",
181
184
  "no-multi-assign",
@@ -234,6 +237,8 @@ const BUILTIN_RULE_IDS = [
234
237
  "no-underscore-dangle",
235
238
  "no-undefined",
236
239
  "no-unneeded-ternary",
240
+ "no-unexpected-multiline",
241
+ "no-unmodified-loop-condition",
237
242
  "no-unreachable",
238
243
  "no-unreachable-loop",
239
244
  "no-unsafe-finally",
@@ -244,6 +249,7 @@ const BUILTIN_RULE_IDS = [
244
249
  "no-unused-private-class-members",
245
250
  "no-unused-vars",
246
251
  "no-use-before-define",
252
+ "no-useless-assignment",
247
253
  "no-useless-backreference",
248
254
  "no-useless-call",
249
255
  "no-useless-catch",
@@ -305,6 +311,24 @@ const BUILTIN_RULE_IDS = [
305
311
  "import/no-named-as-default-member",
306
312
  "import/no-unresolved",
307
313
  "import/no-self-import",
314
+ "jest/no-conditional-expect",
315
+ "jest/no-deprecated-functions",
316
+ "jest/no-export",
317
+ "jest/no-focused-tests",
318
+ "jest/no-identical-title",
319
+ "jest/no-interpolation-in-snapshots",
320
+ "jest/no-jasmine-globals",
321
+ "jest/no-mocks-import",
322
+ "jest/no-standalone-expect",
323
+ "jest/valid-describe-callback",
324
+ "jest/valid-expect-in-promise",
325
+ "jest/valid-expect",
326
+ "jest/valid-title",
327
+ "promise/no-promise-in-callback",
328
+ "promise/no-return-in-finally",
329
+ "promise/no-return-wrap",
330
+ "promise/param-names",
331
+ "promise/valid-params",
308
332
  "jsx-a11y/alt-text",
309
333
  "jsx-a11y/anchor-has-content",
310
334
  "jsx-a11y/aria-props",
@@ -318,6 +342,11 @@ const BUILTIN_RULE_IDS = [
318
342
  "jsx-a11y/role-has-required-aria-props",
319
343
  "jsx-a11y/role-supports-aria-props",
320
344
  "jsx-a11y/scope",
345
+ "promise/always-return",
346
+ "promise/catch-or-return",
347
+ "promise/no-callback-in-promise",
348
+ "promise/no-nesting",
349
+ "promise/no-new-statics",
321
350
  "react/button-has-type",
322
351
  "react/default-props-match-prop-types",
323
352
  "react/display-name",
@@ -339,9 +368,12 @@ const BUILTIN_RULE_IDS = [
339
368
  "react/no-danger",
340
369
  "react/no-danger-with-children",
341
370
  "react/no-deprecated",
371
+ "react/no-direct-mutation-state",
342
372
  "react/no-find-dom-node",
373
+ "react/no-forward-ref",
343
374
  "react/no-is-mounted",
344
375
  "react/no-multi-comp",
376
+ "react/no-unstable-nested-components",
345
377
  "react/no-redundant-should-component-update",
346
378
  "react/no-render-return-value",
347
379
  "react/no-string-refs",
@@ -358,7 +390,9 @@ const BUILTIN_RULE_IDS = [
358
390
  "react/self-closing-comp",
359
391
  "react/style-prop-object",
360
392
  "react/void-dom-elements-no-children",
393
+ "react-hooks/exhaustive-deps",
361
394
  "react-hooks/rules-of-hooks",
395
+ "unused-imports/no-unused-imports",
362
396
  "@typescript-eslint/adjacent-overload-signatures",
363
397
  "@typescript-eslint/array-type",
364
398
  "@typescript-eslint/ban-ts-comment",
@@ -376,6 +410,7 @@ const BUILTIN_RULE_IDS = [
376
410
  "@typescript-eslint/no-dupe-class-members",
377
411
  "@typescript-eslint/no-empty-function",
378
412
  "@typescript-eslint/no-empty-interface",
413
+ "@typescript-eslint/no-empty-object-type",
379
414
  "@typescript-eslint/no-duplicate-enum-values",
380
415
  "@typescript-eslint/no-extra-semi",
381
416
  "@typescript-eslint/no-extra-non-null-assertion",
@@ -391,6 +426,7 @@ const BUILTIN_RULE_IDS = [
391
426
  "@typescript-eslint/no-shadow",
392
427
  "@typescript-eslint/no-this-alias",
393
428
  "@typescript-eslint/no-unsafe-declaration-merging",
429
+ "@typescript-eslint/no-unsafe-function-type",
394
430
  "@typescript-eslint/triple-slash-reference",
395
431
  "@typescript-eslint/typedef",
396
432
  "@typescript-eslint/unified-signatures",
@@ -434,7 +470,16 @@ const NATIVE_ONLY_RULE_IDS = [
434
470
  "semantic-errors"
435
471
  ];
436
472
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
437
- const FIXABLE_BUILTIN_RULE_IDS = new Set(["no-extra-semi", "@typescript-eslint/no-extra-semi"]);
473
+ const FIXABLE_BUILTIN_RULE_IDS = new Set([
474
+ "jest/no-deprecated-functions",
475
+ "jest/no-jasmine-globals",
476
+ "no-extra-semi",
477
+ "@typescript-eslint/no-extra-semi",
478
+ "unused-imports/no-unused-imports"
479
+ ]);
480
+ const SUGGESTION_BUILTIN_RULE_IDS = new Set([
481
+ "jest/no-focused-tests"
482
+ ]);
438
483
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
439
484
 
440
485
  class UtooLint {
@@ -674,6 +719,20 @@ class CLIEngine {
674
719
  }
675
720
  }
676
721
 
722
+ const DEFAULT_NATIVE_MAX_BUFFER = 64 * 1024 * 1024;
723
+
724
+ function nativeMaxBuffer(options, env) {
725
+ const configured = options.maxBuffer ?? env.UTOO_LINT_MAX_BUFFER;
726
+ if (configured === undefined || configured === "") {
727
+ return DEFAULT_NATIVE_MAX_BUFFER;
728
+ }
729
+ const value = typeof configured === "number" ? configured : Number(configured);
730
+ if (!Number.isSafeInteger(value) || value <= 0) {
731
+ throw new TypeError("maxBuffer and UTOO_LINT_MAX_BUFFER must be a positive integer byte count");
732
+ }
733
+ return value;
734
+ }
735
+
677
736
  function run(args = [], options = {}) {
678
737
  const cliArgs = normalizeStringArray(args, "args");
679
738
  const env = options.env ? { ...process.env, ...options.env } : process.env;
@@ -684,7 +743,8 @@ function run(args = [], options = {}) {
684
743
  env,
685
744
  encoding: options.encoding ?? "utf8",
686
745
  input: options.input,
687
- stdio: options.stdio
746
+ stdio: options.stdio,
747
+ maxBuffer: nativeMaxBuffer(options, env)
688
748
  });
689
749
  }
690
750
 
@@ -826,8 +886,9 @@ function runCli(args = [], options = {}) {
826
886
  extraArgs: parsed.passthroughArgs,
827
887
  preserveNativeDefaults: Boolean(parsed.options.noConfig)
828
888
  });
829
- const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
830
- const report = lintFiles(targets, cliOptions);
889
+ const usesDefaultTargets = parsed.targets.length === 0;
890
+ const targets = usesDefaultTargets ? defaultLintTargets(cliOptions) : parsed.targets;
891
+ const report = lintFiles(targets, { ...cliOptions, useDefaultConfigTargets: usesDefaultTargets });
831
892
  report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
832
893
  report.exitCode = exitCodeForDiagnostics(report.diagnostics);
833
894
  const status = report.exitCode ?? exitCodeForDiagnostics(report.diagnostics);
@@ -935,7 +996,8 @@ function runFishlint(args = [], options = {}) {
935
996
  env,
936
997
  encoding: options.encoding ?? "utf8",
937
998
  input: options.input,
938
- stdio: options.stdio
999
+ stdio: options.stdio,
1000
+ maxBuffer: nativeMaxBuffer(options, env)
939
1001
  });
940
1002
  }
941
1003
 
@@ -1137,9 +1199,13 @@ function booleanFlagValue(arg) {
1137
1199
 
1138
1200
  function lintFiles(paths, options = {}) {
1139
1201
  options = withConfigCache(options);
1202
+ const usesDefaultTargets = paths == null || options.useDefaultConfigTargets;
1140
1203
  const targets = lintTargetsForInput(paths, options);
1141
1204
  const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
1142
- const lintPaths = filteredLintPaths(targets, options);
1205
+ let lintPaths = filteredLintPaths(targets, options);
1206
+ if (usesDefaultTargets) {
1207
+ lintPaths = filterDefaultConfigLintPaths(lintPaths, options);
1208
+ }
1143
1209
  if (lintPaths.length === 0) {
1144
1210
  return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, suppressedDiagnostics: [], exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
1145
1211
  }
@@ -1229,13 +1295,15 @@ function nativeConfigRunsForFiles(paths, options) {
1229
1295
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
1230
1296
  const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
1231
1297
  hasConfigSource ||= configured;
1232
- let rules = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath).rules : undefined;
1298
+ const calculated = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath) : {};
1299
+ let rules = calculated.rules;
1300
+ const settings = calculated.settings;
1233
1301
  if (configured && options.rules) {
1234
1302
  rules = selectedRulesWithConfigOptions(rules, options.rules);
1235
1303
  }
1236
- const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
1304
+ const signature = configured ? stableConfigSignature({ rules, settings }) : "<native-defaults>";
1237
1305
  if (!groups.has(signature)) {
1238
- groups.set(signature, { paths: [], rules, configured });
1306
+ groups.set(signature, { paths: [], rules, settings, configured });
1239
1307
  }
1240
1308
  groups.get(signature).paths.push(filePath);
1241
1309
  }
@@ -1265,7 +1333,10 @@ function nativeConfigRunsForFiles(paths, options) {
1265
1333
  config: undefined,
1266
1334
  noConfig: true,
1267
1335
  baseConfig: undefined,
1268
- overrideConfig: { rules: allDisabledNativeRules(group.rules) },
1336
+ overrideConfig: {
1337
+ rules: allDisabledNativeRules(group.rules),
1338
+ ...(group.settings ? { settings: group.settings } : {})
1339
+ },
1269
1340
  forceMaterializedConfig: true
1270
1341
  }
1271
1342
  };
@@ -1494,12 +1565,12 @@ function calculatedConfig(options = {}, filePath) {
1494
1565
  );
1495
1566
  return {
1496
1567
  ...configData,
1497
- rules: {
1498
- ...rulesFromConfig(options.baseConfig, filePath, options.cwd),
1499
- ...rulesFromFileConfig(options, filePath),
1500
- ...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
1501
- ...rulesFromNativeRuleList(options.rules)
1502
- }
1568
+ rules: mergeRuleConfigMaps(
1569
+ rulesFromConfig(options.baseConfig, filePath, options.cwd),
1570
+ rulesFromFileConfig(options, filePath),
1571
+ rulesFromConfig(options.overrideConfig, filePath, options.cwd),
1572
+ rulesFromNativeRuleList(options.rules)
1573
+ )
1503
1574
  };
1504
1575
  }
1505
1576
 
@@ -1594,10 +1665,10 @@ function rulesFromConfig(config, filePath, cwd) {
1594
1665
  return {};
1595
1666
  }
1596
1667
  if (Array.isArray(config)) {
1597
- return config.reduce((rules, entry) => ({
1598
- ...rules,
1599
- ...rulesFromConfig(entry, filePath, cwd)
1600
- }), {});
1668
+ return config.reduce(
1669
+ (rules, entry) => mergeRuleConfigMaps(rules, rulesFromConfig(entry, filePath, cwd)),
1670
+ {}
1671
+ );
1601
1672
  }
1602
1673
  if (!configAppliesToFile(config, filePath, cwd)) {
1603
1674
  return {};
@@ -1650,12 +1721,64 @@ function defaultLintTargets(options = {}) {
1650
1721
  return ["."];
1651
1722
  }
1652
1723
 
1653
- const targets = filePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache));
1724
+ const config = readConfig(configPath, options.cwd, options.configCache);
1725
+ if (configHasUnscopedRules(config)) {
1726
+ return ["."];
1727
+ }
1728
+
1729
+ const targets = filePatternsFromConfig(config);
1654
1730
  return targets.length > 0
1655
1731
  ? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
1656
1732
  : ["."];
1657
1733
  }
1658
1734
 
1735
+ function filterDefaultConfigLintPaths(paths, options) {
1736
+ const configPath = configPathForOptions(options);
1737
+ if (!configPath) {
1738
+ return paths;
1739
+ }
1740
+ const config = readConfig(configPath, options.cwd, options.configCache);
1741
+ if (!configHasFileSelectors(config)) {
1742
+ return paths;
1743
+ }
1744
+ const cwd = dirname(configPath);
1745
+ return paths.filter((filePath) => configSelectsFile(config, filePath, cwd));
1746
+ }
1747
+
1748
+ function configHasFileSelectors(config) {
1749
+ if (Array.isArray(config)) {
1750
+ return config.some(configHasFileSelectors);
1751
+ }
1752
+ return Boolean(config && typeof config === "object" && normalizeConfigFileSelectors(config.files).length > 0);
1753
+ }
1754
+
1755
+ function configHasUnscopedRules(config) {
1756
+ if (Array.isArray(config)) {
1757
+ return config.some(configHasUnscopedRules);
1758
+ }
1759
+ return Boolean(
1760
+ config &&
1761
+ typeof config === "object" &&
1762
+ normalizeConfigFileSelectors(config.files).length === 0 &&
1763
+ config.rules &&
1764
+ typeof config.rules === "object" &&
1765
+ Object.keys(config.rules).length > 0
1766
+ );
1767
+ }
1768
+
1769
+ function configSelectsFile(config, filePath, cwd) {
1770
+ if (Array.isArray(config)) {
1771
+ return config.some((entry) => configSelectsFile(entry, filePath, cwd));
1772
+ }
1773
+ if (!config || typeof config !== "object") {
1774
+ return false;
1775
+ }
1776
+ const selectors = normalizeConfigFileSelectors(config.files);
1777
+ return selectors.length > 0
1778
+ ? configAppliesToFile(config, filePath, cwd)
1779
+ : configHasUnscopedRules(config) && configAppliesToFile(config, filePath, cwd);
1780
+ }
1781
+
1659
1782
  function resolveConfigPattern(pattern, configDirectory) {
1660
1783
  if (isAbsolute(pattern)) {
1661
1784
  return pattern;
@@ -1679,8 +1802,10 @@ function configAppliesToFile(config, filePath, cwd) {
1679
1802
  }
1680
1803
 
1681
1804
  const normalized = normalizeIgnoredPath(filePath, cwd ?? process.cwd());
1682
- const files = normalizeConfigPatterns(config.files);
1683
- if (files.length > 0 && !files.some((pattern) => matchesConfigFilePattern(normalized, normalizeIgnoredPattern(pattern)))) {
1805
+ const files = normalizeConfigFileSelectors(config.files);
1806
+ if (files.length > 0 && !files.some((selector) => selector.every(
1807
+ (pattern) => matchesConfigFilePattern(normalized, normalizeIgnoredPattern(pattern))
1808
+ ))) {
1684
1809
  return false;
1685
1810
  }
1686
1811
 
@@ -1688,6 +1813,23 @@ function configAppliesToFile(config, filePath, cwd) {
1688
1813
  return !pathIgnoredByPatterns(normalized, ignores);
1689
1814
  }
1690
1815
 
1816
+ function normalizeConfigFileSelectors(patterns) {
1817
+ if (!patterns) {
1818
+ return [];
1819
+ }
1820
+ const values = Array.isArray(patterns) ? patterns : [patterns];
1821
+ return values.flatMap((value) => {
1822
+ if (typeof value === "string") {
1823
+ return [[value]];
1824
+ }
1825
+ if (Array.isArray(value)) {
1826
+ const selector = normalizeConfigPatterns(value);
1827
+ return selector.length > 0 ? [selector] : [];
1828
+ }
1829
+ return [];
1830
+ });
1831
+ }
1832
+
1691
1833
  function normalizeConfigPatterns(patterns) {
1692
1834
  if (!patterns) {
1693
1835
  return [];
@@ -1749,7 +1891,15 @@ function withTemporaryConfig(options, callback) {
1749
1891
  const rules = shouldMaterializeFileConfig
1750
1892
  ? materializedRulesFromConfigs(...configs)
1751
1893
  : runtimeRulesFromConfigs(...configs);
1752
- if (Object.keys(rules).length === 0) {
1894
+ const settings = configs.reduce(
1895
+ (result, config) => ({
1896
+ ...result,
1897
+ ...(configDataFromConfig(config, options.filePath ?? options.filename, options.cwd).settings ?? {})
1898
+ }),
1899
+ {}
1900
+ );
1901
+ const hasSettings = Object.keys(settings).length > 0;
1902
+ if (Object.keys(rules).length === 0 && !hasSettings) {
1753
1903
  if (shouldMaterializeFileConfig) {
1754
1904
  return callback({
1755
1905
  ...options,
@@ -1761,7 +1911,7 @@ function withTemporaryConfig(options, callback) {
1761
1911
  }
1762
1912
  const enabledRules = enabledRuleNamesFromConfigs(...configs);
1763
1913
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
1764
- if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
1914
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
1765
1915
  return callback({
1766
1916
  ...options,
1767
1917
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -1773,7 +1923,7 @@ function withTemporaryConfig(options, callback) {
1773
1923
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
1774
1924
  const configPath = join(tmp, "utlint.config.json");
1775
1925
  try {
1776
- writeFileSync(configPath, JSON.stringify({ rules }));
1926
+ writeFileSync(configPath, JSON.stringify({ rules, ...(hasSettings ? { settings } : {}) }));
1777
1927
  return callback({
1778
1928
  ...options,
1779
1929
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
@@ -1792,11 +1942,7 @@ function enabledRuleNames(rules) {
1792
1942
  }
1793
1943
 
1794
1944
  function runtimeRulesFromConfigs(...configs) {
1795
- const rules = {};
1796
- for (const config of configs) {
1797
- Object.assign(rules, runtimeRulesFromConfig(config));
1798
- }
1799
- return rules;
1945
+ return mergeRuleConfigMaps(...configs.map(runtimeRulesFromConfig));
1800
1946
  }
1801
1947
 
1802
1948
  function materializedRulesFromConfigs(...configs) {
@@ -1808,10 +1954,10 @@ function runtimeRulesFromConfig(config) {
1808
1954
  return {};
1809
1955
  }
1810
1956
  if (Array.isArray(config)) {
1811
- return config.reduce((rules, entry) => ({
1812
- ...rules,
1813
- ...runtimeRulesFromConfig(entry)
1814
- }), {});
1957
+ return config.reduce(
1958
+ (rules, entry) => mergeRuleConfigMaps(rules, runtimeRulesFromConfig(entry)),
1959
+ {}
1960
+ );
1815
1961
  }
1816
1962
 
1817
1963
  const rules = {};
@@ -1821,6 +1967,26 @@ function runtimeRulesFromConfig(config) {
1821
1967
  return rules;
1822
1968
  }
1823
1969
 
1970
+ function mergeRuleConfigMaps(...ruleMaps) {
1971
+ const result = {};
1972
+ for (const rules of ruleMaps) {
1973
+ for (const [ruleId, ruleConfig] of Object.entries(rules ?? {})) {
1974
+ const previous = result[ruleId];
1975
+ if (previous !== undefined && isSeverityOnlyRuleConfig(ruleConfig) && Array.isArray(previous) && previous.length > 1) {
1976
+ const severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
1977
+ result[ruleId] = [severity, ...previous.slice(1)];
1978
+ } else {
1979
+ result[ruleId] = ruleConfig;
1980
+ }
1981
+ }
1982
+ }
1983
+ return result;
1984
+ }
1985
+
1986
+ function isSeverityOnlyRuleConfig(ruleConfig) {
1987
+ return !Array.isArray(ruleConfig) || ruleConfig.length === 1;
1988
+ }
1989
+
1824
1990
  function enabledRuleNamesFromConfigs(...configs) {
1825
1991
  const rules = new Set();
1826
1992
  for (const config of configs) {
@@ -2010,7 +2176,7 @@ function parserOptionsForConfig(config, filePath, cwd) {
2010
2176
 
2011
2177
  function customRuleEntriesForConfig(config, rules, filePath, cwd) {
2012
2178
  const entries = new Map();
2013
- for (const [ruleId, ruleConfig] of ruleConfigEntries(config, filePath, cwd)) {
2179
+ for (const [ruleId, ruleConfig] of Object.entries(rulesFromConfig(config, filePath, cwd))) {
2014
2180
  if (!rules.has(ruleId)) {
2015
2181
  continue;
2016
2182
  }
@@ -2067,19 +2233,6 @@ function matchingConfigEntries(config, filePath = undefined, cwd = undefined) {
2067
2233
  return [config];
2068
2234
  }
2069
2235
 
2070
- function ruleConfigEntries(config, filePath = undefined, cwd = undefined) {
2071
- if (Array.isArray(config)) {
2072
- return config.flatMap((item) => ruleConfigEntries(item, filePath, cwd));
2073
- }
2074
- if (!config || typeof config !== "object" || !config.rules || typeof config.rules !== "object") {
2075
- return [];
2076
- }
2077
- if (!configAppliesToFile(config, filePath, cwd)) {
2078
- return [];
2079
- }
2080
- return Object.entries(config.rules);
2081
- }
2082
-
2083
2236
  function configWithoutCustomRules(config, rules) {
2084
2237
  if (Array.isArray(config)) {
2085
2238
  return config.map((item) => configWithoutCustomRules(item, rules));
@@ -3380,10 +3533,7 @@ function mergeRuleTesterConfig(baseConfig, overrideConfig) {
3380
3533
  return {
3381
3534
  ...baseConfig,
3382
3535
  ...overrideConfig,
3383
- rules: {
3384
- ...(baseConfig.rules ?? {}),
3385
- ...(overrideConfig.rules ?? {})
3386
- }
3536
+ rules: mergeRuleConfigMaps(baseConfig.rules, overrideConfig.rules)
3387
3537
  };
3388
3538
  }
3389
3539
 
@@ -4037,7 +4187,7 @@ function collectGlobFiles(target, cwd, patterns, expression, files) {
4037
4187
 
4038
4188
  const child = join(target, entry.name);
4039
4189
  if (entry.isDirectory()) {
4040
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4190
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4041
4191
  collectGlobFiles(child, cwd, patterns, expression, files);
4042
4192
  }
4043
4193
  continue;
@@ -4107,7 +4257,7 @@ function expandLintTarget(target, cwd, patterns) {
4107
4257
  if (stat.isFile()) {
4108
4258
  return isLintableFilePath(target) && !pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns) ? [target] : [];
4109
4259
  }
4110
- if (!stat.isDirectory() || pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns)) {
4260
+ if (!stat.isDirectory() || !shouldTraverseDirectory(normalizeIgnoredPath(target, cwd), patterns)) {
4111
4261
  return [];
4112
4262
  }
4113
4263
 
@@ -4125,7 +4275,7 @@ function collectLintableFiles(target, cwd, patterns, files) {
4125
4275
 
4126
4276
  const child = join(target, entry.name);
4127
4277
  if (entry.isDirectory()) {
4128
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4278
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4129
4279
  collectLintableFiles(child, cwd, patterns, files);
4130
4280
  }
4131
4281
  continue;
@@ -4258,7 +4408,7 @@ function normalizeIgnoredPath(filePath, cwd) {
4258
4408
  }
4259
4409
 
4260
4410
  function normalizeIgnoredPattern(pattern) {
4261
- return normalizePath(pattern.replace(/^!/, "").replace(/^\//, "")).replace(/\/+$/, "");
4411
+ return normalizePath(pattern.replace(/^!/, "")).replace(/\/+$/, "");
4262
4412
  }
4263
4413
 
4264
4414
  function pathIgnoredByPatterns(target, patterns) {
@@ -4272,10 +4422,55 @@ function pathIgnoredByPatterns(target, patterns) {
4272
4422
  return ignored;
4273
4423
  }
4274
4424
 
4425
+ function shouldTraverseDirectory(target, patterns) {
4426
+ return !pathIgnoredByPatterns(target, patterns) || patterns.some(
4427
+ (pattern) => pattern.startsWith("!") && negatedPatternMayMatchDescendant(target, pattern)
4428
+ );
4429
+ }
4430
+
4431
+ function negatedPatternMayMatchDescendant(target, pattern) {
4432
+ pattern = normalizeIgnoredPattern(pattern);
4433
+ if (pattern.startsWith("/")) {
4434
+ pattern = pattern.slice(1);
4435
+ if (target.startsWith("/")) {
4436
+ target = target.slice(1);
4437
+ }
4438
+ }
4439
+ if (!pattern.includes("/")) {
4440
+ return true;
4441
+ }
4442
+
4443
+ const directoryPattern = pattern.slice(0, pattern.lastIndexOf("/"));
4444
+ if (hasGlobSyntax(directoryPattern)) {
4445
+ const directoryExpression = new RegExp(`^${globPatternRegExpSource(directoryPattern)}(?:/.*)?$`);
4446
+ if (directoryExpression.test(target)) {
4447
+ return true;
4448
+ }
4449
+ }
4450
+
4451
+ const globIndex = pattern.search(/[*?[{]/u);
4452
+ const staticPrefix = (globIndex === -1 ? pattern : pattern.slice(0, globIndex)).replace(/\/+$/u, "");
4453
+ return !staticPrefix ||
4454
+ staticPrefix === target ||
4455
+ staticPrefix.startsWith(`${target}/`) ||
4456
+ target.startsWith(`${staticPrefix}/`);
4457
+ }
4458
+
4275
4459
  function matchesIgnorePattern(target, pattern) {
4460
+ const anchored = pattern.startsWith("/");
4461
+ if (anchored) {
4462
+ pattern = pattern.slice(1);
4463
+ if (target.startsWith("/")) {
4464
+ target = target.slice(1);
4465
+ }
4466
+ }
4276
4467
  if (pattern.endsWith("/**")) {
4277
- const prefix = pattern.slice(0, -3);
4278
- return target === prefix || target.startsWith(`${prefix}/`) || target.includes(`/${prefix}/`);
4468
+ const directoryPattern = pattern.slice(0, -3);
4469
+ if (hasGlobSyntax(directoryPattern)) {
4470
+ const expression = new RegExp(`^${globPatternRegExpSource(directoryPattern)}(?:/.*)?$`);
4471
+ return expression.test(target);
4472
+ }
4473
+ return target === directoryPattern || target.startsWith(`${directoryPattern}/`);
4279
4474
  }
4280
4475
  if (pattern.startsWith("**/")) {
4281
4476
  const suffix = pattern.slice(3);
@@ -4284,10 +4479,14 @@ function matchesIgnorePattern(target, pattern) {
4284
4479
  }
4285
4480
  }
4286
4481
  if (!hasGlobSyntax(pattern)) {
4482
+ if (anchored || pattern.includes("/")) {
4483
+ return target === pattern || target.startsWith(`${pattern}/`);
4484
+ }
4287
4485
  return target === pattern || target.endsWith(`/${pattern}`) || target.startsWith(`${pattern}/`);
4288
4486
  }
4289
4487
 
4290
- const expression = new RegExp(`(^|/)${globPatternRegExpSource(pattern)}$`);
4488
+ const prefix = anchored || pattern.includes("/") ? "^" : "(^|/)";
4489
+ const expression = new RegExp(`${prefix}${globPatternRegExpSource(pattern)}$`);
4291
4490
  return expression.test(target);
4292
4491
  }
4293
4492
 
@@ -4396,6 +4595,9 @@ function ruleMetaForRuleId(ruleId) {
4396
4595
  if (FIXABLE_BUILTIN_RULE_IDS.has(ruleId)) {
4397
4596
  meta.fixable = "code";
4398
4597
  }
4598
+ if (SUGGESTION_BUILTIN_RULE_IDS.has(ruleId)) {
4599
+ meta.hasSuggestions = true;
4600
+ }
4399
4601
  return meta;
4400
4602
  }
4401
4603
 
@@ -4486,6 +4688,21 @@ function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
4486
4688
  if (fixes.length > 0) {
4487
4689
  message.fix = fixes.length === 1 ? fixes[0] : fixes;
4488
4690
  }
4691
+ const suggestions = (diagnostic.suggestions ?? []).map((suggestion) => {
4692
+ const suggestionFixes = (suggestion.fix ?? []).map((fix) => ({
4693
+ range: fix.range,
4694
+ text: fix.text
4695
+ }));
4696
+ return {
4697
+ desc: suggestion.desc,
4698
+ ...(suggestionFixes.length === 0 ? {} : {
4699
+ fix: suggestionFixes.length === 1 ? suggestionFixes[0] : suggestionFixes
4700
+ })
4701
+ };
4702
+ });
4703
+ if (suggestions.length > 0) {
4704
+ message.suggestions = suggestions;
4705
+ }
4489
4706
  return message;
4490
4707
  }
4491
4708
 
package/index.d.ts CHANGED
@@ -67,13 +67,24 @@ export interface PluginObject {
67
67
  [key: string]: unknown;
68
68
  }
69
69
 
70
+ export interface JestPluginSettings {
71
+ version?: string | number;
72
+ globalAliases?: Record<string, string[]>;
73
+ [key: string]: unknown;
74
+ }
75
+
76
+ export interface SharedSettings {
77
+ jest?: JestPluginSettings;
78
+ [key: string]: unknown;
79
+ }
80
+
70
81
  export interface ConfigObject {
71
82
  name?: string;
72
83
  files?: Array<string | string[]>;
73
84
  ignores?: string[];
74
85
  rules?: Record<string, RuleConfig>;
75
86
  plugins?: Record<string, PluginObject>;
76
- settings?: Record<string, unknown>;
87
+ settings?: SharedSettings;
77
88
  languageOptions?: {
78
89
  parser?: unknown;
79
90
  parserOptions?: Record<string, unknown>;
@@ -93,6 +104,8 @@ export interface LintOptions {
93
104
  cwd?: string;
94
105
  binary?: string;
95
106
  env?: Record<string, string | undefined>;
107
+ /** Maximum bytes captured from each native stdout/stderr stream. Defaults to 64 MiB. */
108
+ maxBuffer?: number;
96
109
  threads?: number;
97
110
  rules?: string[] | string;
98
111
  format?: "text" | "json" | string;