@utoo/lint 0.2.9 → 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.
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/utooland/utoo-lint/main/npm/utoo-lint/schema.json",
3
+ "files": ["src/**/*.{js,jsx,ts,tsx}"],
4
+ "ignores": ["dist", "coverage", "node_modules"],
3
5
  "rules": {
4
6
  "no-debugger": "error",
5
7
  "no-alert": "error",
@@ -12,21 +14,34 @@
12
14
  "no-constant-condition": "error",
13
15
  "no-dupe-keys": "error",
14
16
  "no-duplicate-imports": "error",
17
+ "no-script-url": "error",
15
18
  "import/first": "error",
16
19
  "import/newline-after-import": "warn",
17
20
  "import/no-duplicates": "error",
18
21
  "import/no-self-import": "error",
22
+ "promise/no-nesting": "error",
23
+ "react-hooks/rules-of-hooks": "error",
24
+ "react-hooks/exhaustive-deps": "warn",
19
25
  "react/jsx-no-duplicate-props": "error",
26
+ "react/jsx-key": "error",
20
27
  "react/jsx-no-target-blank": "error",
21
28
  "react/jsx-pascal-case": "warn",
29
+ "react/no-array-index-key": "warn",
30
+ "react/no-children-prop": "error",
22
31
  "react/no-danger": "warn",
32
+ "react/no-danger-with-children": "error",
23
33
  "react/no-find-dom-node": "error",
34
+ "react/no-forward-ref": "warn",
35
+ "react/no-unstable-nested-components": "warn",
24
36
  "react/no-unescaped-entities": "error",
37
+ "react/void-dom-elements-no-children": "error",
25
38
  "jsx-a11y/aria-props": "error",
26
39
  "jsx-a11y/aria-unsupported-elements": "error",
27
40
  "jsx-a11y/iframe-has-title": "error",
28
41
  "jsx-a11y/img-redundant-alt": "warn",
29
42
  "jsx-a11y/no-access-key": "error",
43
+ "unused-imports/no-unused-imports": "warn",
44
+ "@typescript-eslint/ban-types": "warn",
30
45
  "@typescript-eslint/no-unused-vars": "warn",
31
46
  "@typescript-eslint/no-use-before-define": "error",
32
47
  "@typescript-eslint/no-shadow": "error",
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,19 @@ 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",
308
327
  "promise/no-promise-in-callback",
309
328
  "promise/no-return-in-finally",
310
329
  "promise/no-return-wrap",
@@ -349,9 +368,12 @@ const BUILTIN_RULE_IDS = [
349
368
  "react/no-danger",
350
369
  "react/no-danger-with-children",
351
370
  "react/no-deprecated",
371
+ "react/no-direct-mutation-state",
352
372
  "react/no-find-dom-node",
373
+ "react/no-forward-ref",
353
374
  "react/no-is-mounted",
354
375
  "react/no-multi-comp",
376
+ "react/no-unstable-nested-components",
355
377
  "react/no-redundant-should-component-update",
356
378
  "react/no-render-return-value",
357
379
  "react/no-string-refs",
@@ -449,10 +471,15 @@ const NATIVE_ONLY_RULE_IDS = [
449
471
  ];
450
472
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
451
473
  const FIXABLE_BUILTIN_RULE_IDS = new Set([
474
+ "jest/no-deprecated-functions",
475
+ "jest/no-jasmine-globals",
452
476
  "no-extra-semi",
453
477
  "@typescript-eslint/no-extra-semi",
454
478
  "unused-imports/no-unused-imports"
455
479
  ]);
480
+ const SUGGESTION_BUILTIN_RULE_IDS = new Set([
481
+ "jest/no-focused-tests"
482
+ ]);
456
483
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
457
484
 
458
485
  class UtooLint {
@@ -692,6 +719,20 @@ class CLIEngine {
692
719
  }
693
720
  }
694
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
+
695
736
  function run(args = [], options = {}) {
696
737
  const cliArgs = normalizeStringArray(args, "args");
697
738
  const env = options.env ? { ...process.env, ...options.env } : process.env;
@@ -702,7 +743,8 @@ function run(args = [], options = {}) {
702
743
  env,
703
744
  encoding: options.encoding ?? "utf8",
704
745
  input: options.input,
705
- stdio: options.stdio
746
+ stdio: options.stdio,
747
+ maxBuffer: nativeMaxBuffer(options, env)
706
748
  });
707
749
  }
708
750
 
@@ -844,8 +886,9 @@ function runCli(args = [], options = {}) {
844
886
  extraArgs: parsed.passthroughArgs,
845
887
  preserveNativeDefaults: Boolean(parsed.options.noConfig)
846
888
  });
847
- const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
848
- 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 });
849
892
  report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
850
893
  report.exitCode = exitCodeForDiagnostics(report.diagnostics);
851
894
  const status = report.exitCode ?? exitCodeForDiagnostics(report.diagnostics);
@@ -953,7 +996,8 @@ function runFishlint(args = [], options = {}) {
953
996
  env,
954
997
  encoding: options.encoding ?? "utf8",
955
998
  input: options.input,
956
- stdio: options.stdio
999
+ stdio: options.stdio,
1000
+ maxBuffer: nativeMaxBuffer(options, env)
957
1001
  });
958
1002
  }
959
1003
 
@@ -1155,9 +1199,13 @@ function booleanFlagValue(arg) {
1155
1199
 
1156
1200
  function lintFiles(paths, options = {}) {
1157
1201
  options = withConfigCache(options);
1202
+ const usesDefaultTargets = paths == null || options.useDefaultConfigTargets;
1158
1203
  const targets = lintTargetsForInput(paths, options);
1159
1204
  const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
1160
- const lintPaths = filteredLintPaths(targets, options);
1205
+ let lintPaths = filteredLintPaths(targets, options);
1206
+ if (usesDefaultTargets) {
1207
+ lintPaths = filterDefaultConfigLintPaths(lintPaths, options);
1208
+ }
1161
1209
  if (lintPaths.length === 0) {
1162
1210
  return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, suppressedDiagnostics: [], exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
1163
1211
  }
@@ -1247,13 +1295,15 @@ function nativeConfigRunsForFiles(paths, options) {
1247
1295
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
1248
1296
  const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
1249
1297
  hasConfigSource ||= configured;
1250
- 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;
1251
1301
  if (configured && options.rules) {
1252
1302
  rules = selectedRulesWithConfigOptions(rules, options.rules);
1253
1303
  }
1254
- const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
1304
+ const signature = configured ? stableConfigSignature({ rules, settings }) : "<native-defaults>";
1255
1305
  if (!groups.has(signature)) {
1256
- groups.set(signature, { paths: [], rules, configured });
1306
+ groups.set(signature, { paths: [], rules, settings, configured });
1257
1307
  }
1258
1308
  groups.get(signature).paths.push(filePath);
1259
1309
  }
@@ -1283,7 +1333,10 @@ function nativeConfigRunsForFiles(paths, options) {
1283
1333
  config: undefined,
1284
1334
  noConfig: true,
1285
1335
  baseConfig: undefined,
1286
- overrideConfig: { rules: allDisabledNativeRules(group.rules) },
1336
+ overrideConfig: {
1337
+ rules: allDisabledNativeRules(group.rules),
1338
+ ...(group.settings ? { settings: group.settings } : {})
1339
+ },
1287
1340
  forceMaterializedConfig: true
1288
1341
  }
1289
1342
  };
@@ -1512,12 +1565,12 @@ function calculatedConfig(options = {}, filePath) {
1512
1565
  );
1513
1566
  return {
1514
1567
  ...configData,
1515
- rules: {
1516
- ...rulesFromConfig(options.baseConfig, filePath, options.cwd),
1517
- ...rulesFromFileConfig(options, filePath),
1518
- ...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
1519
- ...rulesFromNativeRuleList(options.rules)
1520
- }
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
+ )
1521
1574
  };
1522
1575
  }
1523
1576
 
@@ -1612,10 +1665,10 @@ function rulesFromConfig(config, filePath, cwd) {
1612
1665
  return {};
1613
1666
  }
1614
1667
  if (Array.isArray(config)) {
1615
- return config.reduce((rules, entry) => ({
1616
- ...rules,
1617
- ...rulesFromConfig(entry, filePath, cwd)
1618
- }), {});
1668
+ return config.reduce(
1669
+ (rules, entry) => mergeRuleConfigMaps(rules, rulesFromConfig(entry, filePath, cwd)),
1670
+ {}
1671
+ );
1619
1672
  }
1620
1673
  if (!configAppliesToFile(config, filePath, cwd)) {
1621
1674
  return {};
@@ -1668,12 +1721,64 @@ function defaultLintTargets(options = {}) {
1668
1721
  return ["."];
1669
1722
  }
1670
1723
 
1671
- 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);
1672
1730
  return targets.length > 0
1673
1731
  ? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
1674
1732
  : ["."];
1675
1733
  }
1676
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
+
1677
1782
  function resolveConfigPattern(pattern, configDirectory) {
1678
1783
  if (isAbsolute(pattern)) {
1679
1784
  return pattern;
@@ -1697,8 +1802,10 @@ function configAppliesToFile(config, filePath, cwd) {
1697
1802
  }
1698
1803
 
1699
1804
  const normalized = normalizeIgnoredPath(filePath, cwd ?? process.cwd());
1700
- const files = normalizeConfigPatterns(config.files);
1701
- 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
+ ))) {
1702
1809
  return false;
1703
1810
  }
1704
1811
 
@@ -1706,6 +1813,23 @@ function configAppliesToFile(config, filePath, cwd) {
1706
1813
  return !pathIgnoredByPatterns(normalized, ignores);
1707
1814
  }
1708
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
+
1709
1833
  function normalizeConfigPatterns(patterns) {
1710
1834
  if (!patterns) {
1711
1835
  return [];
@@ -1767,7 +1891,15 @@ function withTemporaryConfig(options, callback) {
1767
1891
  const rules = shouldMaterializeFileConfig
1768
1892
  ? materializedRulesFromConfigs(...configs)
1769
1893
  : runtimeRulesFromConfigs(...configs);
1770
- 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) {
1771
1903
  if (shouldMaterializeFileConfig) {
1772
1904
  return callback({
1773
1905
  ...options,
@@ -1779,7 +1911,7 @@ function withTemporaryConfig(options, callback) {
1779
1911
  }
1780
1912
  const enabledRules = enabledRuleNamesFromConfigs(...configs);
1781
1913
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
1782
- if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
1914
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
1783
1915
  return callback({
1784
1916
  ...options,
1785
1917
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -1791,7 +1923,7 @@ function withTemporaryConfig(options, callback) {
1791
1923
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
1792
1924
  const configPath = join(tmp, "utlint.config.json");
1793
1925
  try {
1794
- writeFileSync(configPath, JSON.stringify({ rules }));
1926
+ writeFileSync(configPath, JSON.stringify({ rules, ...(hasSettings ? { settings } : {}) }));
1795
1927
  return callback({
1796
1928
  ...options,
1797
1929
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
@@ -1810,11 +1942,7 @@ function enabledRuleNames(rules) {
1810
1942
  }
1811
1943
 
1812
1944
  function runtimeRulesFromConfigs(...configs) {
1813
- const rules = {};
1814
- for (const config of configs) {
1815
- Object.assign(rules, runtimeRulesFromConfig(config));
1816
- }
1817
- return rules;
1945
+ return mergeRuleConfigMaps(...configs.map(runtimeRulesFromConfig));
1818
1946
  }
1819
1947
 
1820
1948
  function materializedRulesFromConfigs(...configs) {
@@ -1826,10 +1954,10 @@ function runtimeRulesFromConfig(config) {
1826
1954
  return {};
1827
1955
  }
1828
1956
  if (Array.isArray(config)) {
1829
- return config.reduce((rules, entry) => ({
1830
- ...rules,
1831
- ...runtimeRulesFromConfig(entry)
1832
- }), {});
1957
+ return config.reduce(
1958
+ (rules, entry) => mergeRuleConfigMaps(rules, runtimeRulesFromConfig(entry)),
1959
+ {}
1960
+ );
1833
1961
  }
1834
1962
 
1835
1963
  const rules = {};
@@ -1839,6 +1967,26 @@ function runtimeRulesFromConfig(config) {
1839
1967
  return rules;
1840
1968
  }
1841
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
+
1842
1990
  function enabledRuleNamesFromConfigs(...configs) {
1843
1991
  const rules = new Set();
1844
1992
  for (const config of configs) {
@@ -2028,7 +2176,7 @@ function parserOptionsForConfig(config, filePath, cwd) {
2028
2176
 
2029
2177
  function customRuleEntriesForConfig(config, rules, filePath, cwd) {
2030
2178
  const entries = new Map();
2031
- for (const [ruleId, ruleConfig] of ruleConfigEntries(config, filePath, cwd)) {
2179
+ for (const [ruleId, ruleConfig] of Object.entries(rulesFromConfig(config, filePath, cwd))) {
2032
2180
  if (!rules.has(ruleId)) {
2033
2181
  continue;
2034
2182
  }
@@ -2085,19 +2233,6 @@ function matchingConfigEntries(config, filePath = undefined, cwd = undefined) {
2085
2233
  return [config];
2086
2234
  }
2087
2235
 
2088
- function ruleConfigEntries(config, filePath = undefined, cwd = undefined) {
2089
- if (Array.isArray(config)) {
2090
- return config.flatMap((item) => ruleConfigEntries(item, filePath, cwd));
2091
- }
2092
- if (!config || typeof config !== "object" || !config.rules || typeof config.rules !== "object") {
2093
- return [];
2094
- }
2095
- if (!configAppliesToFile(config, filePath, cwd)) {
2096
- return [];
2097
- }
2098
- return Object.entries(config.rules);
2099
- }
2100
-
2101
2236
  function configWithoutCustomRules(config, rules) {
2102
2237
  if (Array.isArray(config)) {
2103
2238
  return config.map((item) => configWithoutCustomRules(item, rules));
@@ -3398,10 +3533,7 @@ function mergeRuleTesterConfig(baseConfig, overrideConfig) {
3398
3533
  return {
3399
3534
  ...baseConfig,
3400
3535
  ...overrideConfig,
3401
- rules: {
3402
- ...(baseConfig.rules ?? {}),
3403
- ...(overrideConfig.rules ?? {})
3404
- }
3536
+ rules: mergeRuleConfigMaps(baseConfig.rules, overrideConfig.rules)
3405
3537
  };
3406
3538
  }
3407
3539
 
@@ -4055,7 +4187,7 @@ function collectGlobFiles(target, cwd, patterns, expression, files) {
4055
4187
 
4056
4188
  const child = join(target, entry.name);
4057
4189
  if (entry.isDirectory()) {
4058
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4190
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4059
4191
  collectGlobFiles(child, cwd, patterns, expression, files);
4060
4192
  }
4061
4193
  continue;
@@ -4125,7 +4257,7 @@ function expandLintTarget(target, cwd, patterns) {
4125
4257
  if (stat.isFile()) {
4126
4258
  return isLintableFilePath(target) && !pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns) ? [target] : [];
4127
4259
  }
4128
- if (!stat.isDirectory() || pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns)) {
4260
+ if (!stat.isDirectory() || !shouldTraverseDirectory(normalizeIgnoredPath(target, cwd), patterns)) {
4129
4261
  return [];
4130
4262
  }
4131
4263
 
@@ -4143,7 +4275,7 @@ function collectLintableFiles(target, cwd, patterns, files) {
4143
4275
 
4144
4276
  const child = join(target, entry.name);
4145
4277
  if (entry.isDirectory()) {
4146
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4278
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4147
4279
  collectLintableFiles(child, cwd, patterns, files);
4148
4280
  }
4149
4281
  continue;
@@ -4276,7 +4408,7 @@ function normalizeIgnoredPath(filePath, cwd) {
4276
4408
  }
4277
4409
 
4278
4410
  function normalizeIgnoredPattern(pattern) {
4279
- return normalizePath(pattern.replace(/^!/, "").replace(/^\//, "")).replace(/\/+$/, "");
4411
+ return normalizePath(pattern.replace(/^!/, "")).replace(/\/+$/, "");
4280
4412
  }
4281
4413
 
4282
4414
  function pathIgnoredByPatterns(target, patterns) {
@@ -4290,10 +4422,55 @@ function pathIgnoredByPatterns(target, patterns) {
4290
4422
  return ignored;
4291
4423
  }
4292
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
+
4293
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
+ }
4294
4467
  if (pattern.endsWith("/**")) {
4295
- const prefix = pattern.slice(0, -3);
4296
- 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}/`);
4297
4474
  }
4298
4475
  if (pattern.startsWith("**/")) {
4299
4476
  const suffix = pattern.slice(3);
@@ -4302,10 +4479,14 @@ function matchesIgnorePattern(target, pattern) {
4302
4479
  }
4303
4480
  }
4304
4481
  if (!hasGlobSyntax(pattern)) {
4482
+ if (anchored || pattern.includes("/")) {
4483
+ return target === pattern || target.startsWith(`${pattern}/`);
4484
+ }
4305
4485
  return target === pattern || target.endsWith(`/${pattern}`) || target.startsWith(`${pattern}/`);
4306
4486
  }
4307
4487
 
4308
- const expression = new RegExp(`(^|/)${globPatternRegExpSource(pattern)}$`);
4488
+ const prefix = anchored || pattern.includes("/") ? "^" : "(^|/)";
4489
+ const expression = new RegExp(`${prefix}${globPatternRegExpSource(pattern)}$`);
4309
4490
  return expression.test(target);
4310
4491
  }
4311
4492
 
@@ -4414,6 +4595,9 @@ function ruleMetaForRuleId(ruleId) {
4414
4595
  if (FIXABLE_BUILTIN_RULE_IDS.has(ruleId)) {
4415
4596
  meta.fixable = "code";
4416
4597
  }
4598
+ if (SUGGESTION_BUILTIN_RULE_IDS.has(ruleId)) {
4599
+ meta.hasSuggestions = true;
4600
+ }
4417
4601
  return meta;
4418
4602
  }
4419
4603
 
@@ -4504,6 +4688,21 @@ function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
4504
4688
  if (fixes.length > 0) {
4505
4689
  message.fix = fixes.length === 1 ? fixes[0] : fixes;
4506
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
+ }
4507
4706
  return message;
4508
4707
  }
4509
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;