@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/README.md +201 -172
- package/bin/fishlint.js +23 -3
- package/bin/migrate.js +555 -39
- package/bin/utoo-lint.js +15 -15
- package/configs/frontend.d.ts +54 -2
- package/configs/frontend.json +16 -1
- package/index.cjs +277 -60
- package/index.d.ts +14 -1
- package/index.js +277 -60
- package/package.json +13 -11
- package/schema.json +89 -36
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([
|
|
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
|
|
830
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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: {
|
|
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
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
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(
|
|
1598
|
-
|
|
1599
|
-
|
|
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
|
|
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 =
|
|
1683
|
-
if (files.length > 0 && !files.some((
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
1812
|
-
|
|
1813
|
-
|
|
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
|
|
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 (
|
|
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() ||
|
|
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 (
|
|
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(/^!/, "")
|
|
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
|
|
4278
|
-
|
|
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
|
|
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?:
|
|
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;
|