@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.
package/index.js CHANGED
@@ -80,6 +80,7 @@ const BUILTIN_RULE_IDS = [
80
80
  "block-scoped-var",
81
81
  "camelcase",
82
82
  "capitalized-comments",
83
+ "class-methods-use-this",
83
84
  "complexity",
84
85
  "consistent-return",
85
86
  "consistent-this",
@@ -171,6 +172,7 @@ const BUILTIN_RULE_IDS = [
171
172
  "no-inline-comments",
172
173
  "no-inner-declarations",
173
174
  "no-invalid-regexp",
175
+ "no-invalid-this",
174
176
  "no-irregular-whitespace",
175
177
  "no-iterator",
176
178
  "no-label-var",
@@ -179,6 +181,7 @@ const BUILTIN_RULE_IDS = [
179
181
  "no-lonely-if",
180
182
  "no-loop-func",
181
183
  "no-loss-of-precision",
184
+ "no-magic-numbers",
182
185
  "no-mixed-spaces-and-tabs",
183
186
  "no-misleading-character-class",
184
187
  "no-multi-assign",
@@ -237,6 +240,8 @@ const BUILTIN_RULE_IDS = [
237
240
  "no-underscore-dangle",
238
241
  "no-undefined",
239
242
  "no-unneeded-ternary",
243
+ "no-unexpected-multiline",
244
+ "no-unmodified-loop-condition",
240
245
  "no-unreachable",
241
246
  "no-unreachable-loop",
242
247
  "no-unsafe-finally",
@@ -247,6 +252,7 @@ const BUILTIN_RULE_IDS = [
247
252
  "no-unused-private-class-members",
248
253
  "no-unused-vars",
249
254
  "no-use-before-define",
255
+ "no-useless-assignment",
250
256
  "no-useless-backreference",
251
257
  "no-useless-call",
252
258
  "no-useless-catch",
@@ -308,6 +314,19 @@ const BUILTIN_RULE_IDS = [
308
314
  "import/no-named-as-default-member",
309
315
  "import/no-unresolved",
310
316
  "import/no-self-import",
317
+ "jest/no-conditional-expect",
318
+ "jest/no-deprecated-functions",
319
+ "jest/no-export",
320
+ "jest/no-focused-tests",
321
+ "jest/no-identical-title",
322
+ "jest/no-interpolation-in-snapshots",
323
+ "jest/no-jasmine-globals",
324
+ "jest/no-mocks-import",
325
+ "jest/no-standalone-expect",
326
+ "jest/valid-describe-callback",
327
+ "jest/valid-expect-in-promise",
328
+ "jest/valid-expect",
329
+ "jest/valid-title",
311
330
  "promise/no-promise-in-callback",
312
331
  "promise/no-return-in-finally",
313
332
  "promise/no-return-wrap",
@@ -352,9 +371,12 @@ const BUILTIN_RULE_IDS = [
352
371
  "react/no-danger",
353
372
  "react/no-danger-with-children",
354
373
  "react/no-deprecated",
374
+ "react/no-direct-mutation-state",
355
375
  "react/no-find-dom-node",
376
+ "react/no-forward-ref",
356
377
  "react/no-is-mounted",
357
378
  "react/no-multi-comp",
379
+ "react/no-unstable-nested-components",
358
380
  "react/no-redundant-should-component-update",
359
381
  "react/no-render-return-value",
360
382
  "react/no-string-refs",
@@ -452,10 +474,15 @@ const NATIVE_ONLY_RULE_IDS = [
452
474
  ];
453
475
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
454
476
  const FIXABLE_BUILTIN_RULE_IDS = new Set([
477
+ "jest/no-deprecated-functions",
478
+ "jest/no-jasmine-globals",
455
479
  "no-extra-semi",
456
480
  "@typescript-eslint/no-extra-semi",
457
481
  "unused-imports/no-unused-imports"
458
482
  ]);
483
+ const SUGGESTION_BUILTIN_RULE_IDS = new Set([
484
+ "jest/no-focused-tests"
485
+ ]);
459
486
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
460
487
 
461
488
  export class UtooLint {
@@ -765,7 +792,7 @@ function parserOptionsForConfig(config, filePath, cwd) {
765
792
 
766
793
  function customRuleEntriesForConfig(config, rules, filePath, cwd) {
767
794
  const entries = new Map();
768
- for (const [ruleId, ruleConfig] of ruleConfigEntries(config, filePath, cwd)) {
795
+ for (const [ruleId, ruleConfig] of Object.entries(rulesFromConfig(config, filePath, cwd))) {
769
796
  if (!rules.has(ruleId)) {
770
797
  continue;
771
798
  }
@@ -822,19 +849,6 @@ function matchingConfigEntries(config, filePath = undefined, cwd = undefined) {
822
849
  return [config];
823
850
  }
824
851
 
825
- function ruleConfigEntries(config, filePath = undefined, cwd = undefined) {
826
- if (Array.isArray(config)) {
827
- return config.flatMap((item) => ruleConfigEntries(item, filePath, cwd));
828
- }
829
- if (!config || typeof config !== "object" || !config.rules || typeof config.rules !== "object") {
830
- return [];
831
- }
832
- if (!configAppliesToFile(config, filePath, cwd)) {
833
- return [];
834
- }
835
- return Object.entries(config.rules);
836
- }
837
-
838
852
  function configWithoutCustomRules(config, rules) {
839
853
  if (Array.isArray(config)) {
840
854
  return config.map((item) => configWithoutCustomRules(item, rules));
@@ -2135,10 +2149,7 @@ function mergeRuleTesterConfig(baseConfig, overrideConfig) {
2135
2149
  return {
2136
2150
  ...baseConfig,
2137
2151
  ...overrideConfig,
2138
- rules: {
2139
- ...(baseConfig.rules ?? {}),
2140
- ...(overrideConfig.rules ?? {})
2141
- }
2152
+ rules: mergeRuleConfigMaps(baseConfig.rules, overrideConfig.rules)
2142
2153
  };
2143
2154
  }
2144
2155
 
@@ -2597,6 +2608,20 @@ export class CLIEngine {
2597
2608
  }
2598
2609
  }
2599
2610
 
2611
+ const DEFAULT_NATIVE_MAX_BUFFER = 64 * 1024 * 1024;
2612
+
2613
+ function nativeMaxBuffer(options, env) {
2614
+ const configured = options.maxBuffer ?? env.UTOO_LINT_MAX_BUFFER;
2615
+ if (configured === undefined || configured === "") {
2616
+ return DEFAULT_NATIVE_MAX_BUFFER;
2617
+ }
2618
+ const value = typeof configured === "number" ? configured : Number(configured);
2619
+ if (!Number.isSafeInteger(value) || value <= 0) {
2620
+ throw new TypeError("maxBuffer and UTOO_LINT_MAX_BUFFER must be a positive integer byte count");
2621
+ }
2622
+ return value;
2623
+ }
2624
+
2600
2625
  export function run(args = [], options = {}) {
2601
2626
  const cliArgs = normalizeStringArray(args, "args");
2602
2627
  const env = options.env ? { ...process.env, ...options.env } : process.env;
@@ -2607,7 +2632,8 @@ export function run(args = [], options = {}) {
2607
2632
  env,
2608
2633
  encoding: options.encoding ?? "utf8",
2609
2634
  input: options.input,
2610
- stdio: options.stdio
2635
+ stdio: options.stdio,
2636
+ maxBuffer: nativeMaxBuffer(options, env)
2611
2637
  });
2612
2638
  }
2613
2639
 
@@ -2749,8 +2775,9 @@ export function runCli(args = [], options = {}) {
2749
2775
  extraArgs: parsed.passthroughArgs,
2750
2776
  preserveNativeDefaults: Boolean(parsed.options.noConfig)
2751
2777
  });
2752
- const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
2753
- const report = lintFiles(targets, cliOptions);
2778
+ const usesDefaultTargets = parsed.targets.length === 0;
2779
+ const targets = usesDefaultTargets ? defaultLintTargets(cliOptions) : parsed.targets;
2780
+ const report = lintFiles(targets, { ...cliOptions, useDefaultConfigTargets: usesDefaultTargets });
2754
2781
  report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
2755
2782
  report.exitCode = exitCodeForDiagnostics(report.diagnostics);
2756
2783
  const status = report.exitCode ?? exitCodeForDiagnostics(report.diagnostics);
@@ -2858,7 +2885,8 @@ export function runFishlint(args = [], options = {}) {
2858
2885
  env,
2859
2886
  encoding: options.encoding ?? "utf8",
2860
2887
  input: options.input,
2861
- stdio: options.stdio
2888
+ stdio: options.stdio,
2889
+ maxBuffer: nativeMaxBuffer(options, env)
2862
2890
  });
2863
2891
  }
2864
2892
 
@@ -3060,9 +3088,13 @@ function booleanFlagValue(arg) {
3060
3088
 
3061
3089
  export function lintFiles(paths, options = {}) {
3062
3090
  options = withConfigCache(options);
3091
+ const usesDefaultTargets = paths == null || options.useDefaultConfigTargets;
3063
3092
  const targets = lintTargetsForInput(paths, options);
3064
3093
  const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
3065
- const lintPaths = filteredLintPaths(targets, options);
3094
+ let lintPaths = filteredLintPaths(targets, options);
3095
+ if (usesDefaultTargets) {
3096
+ lintPaths = filterDefaultConfigLintPaths(lintPaths, options);
3097
+ }
3066
3098
  if (lintPaths.length === 0) {
3067
3099
  return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, suppressedDiagnostics: [], exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
3068
3100
  }
@@ -3152,13 +3184,15 @@ function nativeConfigRunsForFiles(paths, options) {
3152
3184
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
3153
3185
  const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
3154
3186
  hasConfigSource ||= configured;
3155
- let rules = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath).rules : undefined;
3187
+ const calculated = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath) : {};
3188
+ let rules = calculated.rules;
3189
+ const settings = calculated.settings;
3156
3190
  if (configured && options.rules) {
3157
3191
  rules = selectedRulesWithConfigOptions(rules, options.rules);
3158
3192
  }
3159
- const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
3193
+ const signature = configured ? stableConfigSignature({ rules, settings }) : "<native-defaults>";
3160
3194
  if (!groups.has(signature)) {
3161
- groups.set(signature, { paths: [], rules, configured });
3195
+ groups.set(signature, { paths: [], rules, settings, configured });
3162
3196
  }
3163
3197
  groups.get(signature).paths.push(filePath);
3164
3198
  }
@@ -3188,7 +3222,10 @@ function nativeConfigRunsForFiles(paths, options) {
3188
3222
  config: undefined,
3189
3223
  noConfig: true,
3190
3224
  baseConfig: undefined,
3191
- overrideConfig: { rules: allDisabledNativeRules(group.rules) },
3225
+ overrideConfig: {
3226
+ rules: allDisabledNativeRules(group.rules),
3227
+ ...(group.settings ? { settings: group.settings } : {})
3228
+ },
3192
3229
  forceMaterializedConfig: true
3193
3230
  }
3194
3231
  };
@@ -3417,12 +3454,12 @@ function calculatedConfig(options = {}, filePath) {
3417
3454
  );
3418
3455
  return {
3419
3456
  ...configData,
3420
- rules: {
3421
- ...rulesFromConfig(options.baseConfig, filePath, options.cwd),
3422
- ...rulesFromFileConfig(options, filePath),
3423
- ...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
3424
- ...rulesFromNativeRuleList(options.rules)
3425
- }
3457
+ rules: mergeRuleConfigMaps(
3458
+ rulesFromConfig(options.baseConfig, filePath, options.cwd),
3459
+ rulesFromFileConfig(options, filePath),
3460
+ rulesFromConfig(options.overrideConfig, filePath, options.cwd),
3461
+ rulesFromNativeRuleList(options.rules)
3462
+ )
3426
3463
  };
3427
3464
  }
3428
3465
 
@@ -3517,10 +3554,10 @@ function rulesFromConfig(config, filePath, cwd) {
3517
3554
  return {};
3518
3555
  }
3519
3556
  if (Array.isArray(config)) {
3520
- return config.reduce((rules, entry) => ({
3521
- ...rules,
3522
- ...rulesFromConfig(entry, filePath, cwd)
3523
- }), {});
3557
+ return config.reduce(
3558
+ (rules, entry) => mergeRuleConfigMaps(rules, rulesFromConfig(entry, filePath, cwd)),
3559
+ {}
3560
+ );
3524
3561
  }
3525
3562
  if (!configAppliesToFile(config, filePath, cwd)) {
3526
3563
  return {};
@@ -3573,12 +3610,64 @@ function defaultLintTargets(options = {}) {
3573
3610
  return ["."];
3574
3611
  }
3575
3612
 
3576
- const targets = filePatternsFromConfig(readConfig(configPath, options.cwd, options.configCache));
3613
+ const config = readConfig(configPath, options.cwd, options.configCache);
3614
+ if (configHasUnscopedRules(config)) {
3615
+ return ["."];
3616
+ }
3617
+
3618
+ const targets = filePatternsFromConfig(config);
3577
3619
  return targets.length > 0
3578
3620
  ? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
3579
3621
  : ["."];
3580
3622
  }
3581
3623
 
3624
+ function filterDefaultConfigLintPaths(paths, options) {
3625
+ const configPath = configPathForOptions(options);
3626
+ if (!configPath) {
3627
+ return paths;
3628
+ }
3629
+ const config = readConfig(configPath, options.cwd, options.configCache);
3630
+ if (!configHasFileSelectors(config)) {
3631
+ return paths;
3632
+ }
3633
+ const cwd = dirname(configPath);
3634
+ return paths.filter((filePath) => configSelectsFile(config, filePath, cwd));
3635
+ }
3636
+
3637
+ function configHasFileSelectors(config) {
3638
+ if (Array.isArray(config)) {
3639
+ return config.some(configHasFileSelectors);
3640
+ }
3641
+ return Boolean(config && typeof config === "object" && normalizeConfigFileSelectors(config.files).length > 0);
3642
+ }
3643
+
3644
+ function configHasUnscopedRules(config) {
3645
+ if (Array.isArray(config)) {
3646
+ return config.some(configHasUnscopedRules);
3647
+ }
3648
+ return Boolean(
3649
+ config &&
3650
+ typeof config === "object" &&
3651
+ normalizeConfigFileSelectors(config.files).length === 0 &&
3652
+ config.rules &&
3653
+ typeof config.rules === "object" &&
3654
+ Object.keys(config.rules).length > 0
3655
+ );
3656
+ }
3657
+
3658
+ function configSelectsFile(config, filePath, cwd) {
3659
+ if (Array.isArray(config)) {
3660
+ return config.some((entry) => configSelectsFile(entry, filePath, cwd));
3661
+ }
3662
+ if (!config || typeof config !== "object") {
3663
+ return false;
3664
+ }
3665
+ const selectors = normalizeConfigFileSelectors(config.files);
3666
+ return selectors.length > 0
3667
+ ? configAppliesToFile(config, filePath, cwd)
3668
+ : configHasUnscopedRules(config) && configAppliesToFile(config, filePath, cwd);
3669
+ }
3670
+
3582
3671
  function resolveConfigPattern(pattern, configDirectory) {
3583
3672
  if (isAbsolute(pattern)) {
3584
3673
  return pattern;
@@ -3602,8 +3691,10 @@ function configAppliesToFile(config, filePath, cwd) {
3602
3691
  }
3603
3692
 
3604
3693
  const normalized = normalizeIgnoredPath(filePath, cwd ?? process.cwd());
3605
- const files = normalizeConfigPatterns(config.files);
3606
- if (files.length > 0 && !files.some((pattern) => matchesConfigFilePattern(normalized, normalizeIgnoredPattern(pattern)))) {
3694
+ const files = normalizeConfigFileSelectors(config.files);
3695
+ if (files.length > 0 && !files.some((selector) => selector.every(
3696
+ (pattern) => matchesConfigFilePattern(normalized, normalizeIgnoredPattern(pattern))
3697
+ ))) {
3607
3698
  return false;
3608
3699
  }
3609
3700
 
@@ -3611,6 +3702,23 @@ function configAppliesToFile(config, filePath, cwd) {
3611
3702
  return !pathIgnoredByPatterns(normalized, ignores);
3612
3703
  }
3613
3704
 
3705
+ function normalizeConfigFileSelectors(patterns) {
3706
+ if (!patterns) {
3707
+ return [];
3708
+ }
3709
+ const values = Array.isArray(patterns) ? patterns : [patterns];
3710
+ return values.flatMap((value) => {
3711
+ if (typeof value === "string") {
3712
+ return [[value]];
3713
+ }
3714
+ if (Array.isArray(value)) {
3715
+ const selector = normalizeConfigPatterns(value);
3716
+ return selector.length > 0 ? [selector] : [];
3717
+ }
3718
+ return [];
3719
+ });
3720
+ }
3721
+
3614
3722
  function normalizeConfigPatterns(patterns) {
3615
3723
  if (!patterns) {
3616
3724
  return [];
@@ -3672,7 +3780,15 @@ function withTemporaryConfig(options, callback) {
3672
3780
  const rules = shouldMaterializeFileConfig
3673
3781
  ? materializedRulesFromConfigs(...configs)
3674
3782
  : runtimeRulesFromConfigs(...configs);
3675
- if (Object.keys(rules).length === 0) {
3783
+ const settings = configs.reduce(
3784
+ (result, config) => ({
3785
+ ...result,
3786
+ ...(configDataFromConfig(config, options.filePath ?? options.filename, options.cwd).settings ?? {})
3787
+ }),
3788
+ {}
3789
+ );
3790
+ const hasSettings = Object.keys(settings).length > 0;
3791
+ if (Object.keys(rules).length === 0 && !hasSettings) {
3676
3792
  if (shouldMaterializeFileConfig) {
3677
3793
  return callback({
3678
3794
  ...options,
@@ -3684,7 +3800,7 @@ function withTemporaryConfig(options, callback) {
3684
3800
  }
3685
3801
  const enabledRules = enabledRuleNamesFromConfigs(...configs);
3686
3802
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
3687
- if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
3803
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
3688
3804
  return callback({
3689
3805
  ...options,
3690
3806
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -3696,7 +3812,7 @@ function withTemporaryConfig(options, callback) {
3696
3812
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
3697
3813
  const configPath = join(tmp, "utlint.config.json");
3698
3814
  try {
3699
- writeFileSync(configPath, JSON.stringify({ rules }));
3815
+ writeFileSync(configPath, JSON.stringify({ rules, ...(hasSettings ? { settings } : {}) }));
3700
3816
  return callback({
3701
3817
  ...options,
3702
3818
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
@@ -3715,11 +3831,7 @@ function enabledRuleNames(rules) {
3715
3831
  }
3716
3832
 
3717
3833
  function runtimeRulesFromConfigs(...configs) {
3718
- const rules = {};
3719
- for (const config of configs) {
3720
- Object.assign(rules, runtimeRulesFromConfig(config));
3721
- }
3722
- return rules;
3834
+ return mergeRuleConfigMaps(...configs.map(runtimeRulesFromConfig));
3723
3835
  }
3724
3836
 
3725
3837
  function materializedRulesFromConfigs(...configs) {
@@ -3731,10 +3843,10 @@ function runtimeRulesFromConfig(config) {
3731
3843
  return {};
3732
3844
  }
3733
3845
  if (Array.isArray(config)) {
3734
- return config.reduce((rules, entry) => ({
3735
- ...rules,
3736
- ...runtimeRulesFromConfig(entry)
3737
- }), {});
3846
+ return config.reduce(
3847
+ (rules, entry) => mergeRuleConfigMaps(rules, runtimeRulesFromConfig(entry)),
3848
+ {}
3849
+ );
3738
3850
  }
3739
3851
 
3740
3852
  const rules = {};
@@ -3744,6 +3856,26 @@ function runtimeRulesFromConfig(config) {
3744
3856
  return rules;
3745
3857
  }
3746
3858
 
3859
+ function mergeRuleConfigMaps(...ruleMaps) {
3860
+ const result = {};
3861
+ for (const rules of ruleMaps) {
3862
+ for (const [ruleId, ruleConfig] of Object.entries(rules ?? {})) {
3863
+ const previous = result[ruleId];
3864
+ if (previous !== undefined && isSeverityOnlyRuleConfig(ruleConfig) && Array.isArray(previous) && previous.length > 1) {
3865
+ const severity = Array.isArray(ruleConfig) ? ruleConfig[0] : ruleConfig;
3866
+ result[ruleId] = [severity, ...previous.slice(1)];
3867
+ } else {
3868
+ result[ruleId] = ruleConfig;
3869
+ }
3870
+ }
3871
+ }
3872
+ return result;
3873
+ }
3874
+
3875
+ function isSeverityOnlyRuleConfig(ruleConfig) {
3876
+ return !Array.isArray(ruleConfig) || ruleConfig.length === 1;
3877
+ }
3878
+
3747
3879
  function enabledRuleNamesFromConfigs(...configs) {
3748
3880
  const rules = new Set();
3749
3881
  for (const config of configs) {
@@ -4058,7 +4190,7 @@ function collectGlobFiles(target, cwd, patterns, expression, files) {
4058
4190
 
4059
4191
  const child = join(target, entry.name);
4060
4192
  if (entry.isDirectory()) {
4061
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4193
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4062
4194
  collectGlobFiles(child, cwd, patterns, expression, files);
4063
4195
  }
4064
4196
  continue;
@@ -4128,7 +4260,7 @@ function expandLintTarget(target, cwd, patterns) {
4128
4260
  if (stat.isFile()) {
4129
4261
  return isLintableFilePath(target) && !pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns) ? [target] : [];
4130
4262
  }
4131
- if (!stat.isDirectory() || pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns)) {
4263
+ if (!stat.isDirectory() || !shouldTraverseDirectory(normalizeIgnoredPath(target, cwd), patterns)) {
4132
4264
  return [];
4133
4265
  }
4134
4266
 
@@ -4146,7 +4278,7 @@ function collectLintableFiles(target, cwd, patterns, files) {
4146
4278
 
4147
4279
  const child = join(target, entry.name);
4148
4280
  if (entry.isDirectory()) {
4149
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4281
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4150
4282
  collectLintableFiles(child, cwd, patterns, files);
4151
4283
  }
4152
4284
  continue;
@@ -4279,7 +4411,7 @@ function normalizeIgnoredPath(filePath, cwd) {
4279
4411
  }
4280
4412
 
4281
4413
  function normalizeIgnoredPattern(pattern) {
4282
- return normalizePath(pattern.replace(/^!/, "").replace(/^\//, "")).replace(/\/+$/, "");
4414
+ return normalizePath(pattern.replace(/^!/, "")).replace(/\/+$/, "");
4283
4415
  }
4284
4416
 
4285
4417
  function pathIgnoredByPatterns(target, patterns) {
@@ -4293,10 +4425,55 @@ function pathIgnoredByPatterns(target, patterns) {
4293
4425
  return ignored;
4294
4426
  }
4295
4427
 
4428
+ function shouldTraverseDirectory(target, patterns) {
4429
+ return !pathIgnoredByPatterns(target, patterns) || patterns.some(
4430
+ (pattern) => pattern.startsWith("!") && negatedPatternMayMatchDescendant(target, pattern)
4431
+ );
4432
+ }
4433
+
4434
+ function negatedPatternMayMatchDescendant(target, pattern) {
4435
+ pattern = normalizeIgnoredPattern(pattern);
4436
+ if (pattern.startsWith("/")) {
4437
+ pattern = pattern.slice(1);
4438
+ if (target.startsWith("/")) {
4439
+ target = target.slice(1);
4440
+ }
4441
+ }
4442
+ if (!pattern.includes("/")) {
4443
+ return true;
4444
+ }
4445
+
4446
+ const directoryPattern = pattern.slice(0, pattern.lastIndexOf("/"));
4447
+ if (hasGlobSyntax(directoryPattern)) {
4448
+ const directoryExpression = new RegExp(`^${globPatternRegExpSource(directoryPattern)}(?:/.*)?$`);
4449
+ if (directoryExpression.test(target)) {
4450
+ return true;
4451
+ }
4452
+ }
4453
+
4454
+ const globIndex = pattern.search(/[*?[{]/u);
4455
+ const staticPrefix = (globIndex === -1 ? pattern : pattern.slice(0, globIndex)).replace(/\/+$/u, "");
4456
+ return !staticPrefix ||
4457
+ staticPrefix === target ||
4458
+ staticPrefix.startsWith(`${target}/`) ||
4459
+ target.startsWith(`${staticPrefix}/`);
4460
+ }
4461
+
4296
4462
  function matchesIgnorePattern(target, pattern) {
4463
+ const anchored = pattern.startsWith("/");
4464
+ if (anchored) {
4465
+ pattern = pattern.slice(1);
4466
+ if (target.startsWith("/")) {
4467
+ target = target.slice(1);
4468
+ }
4469
+ }
4297
4470
  if (pattern.endsWith("/**")) {
4298
- const prefix = pattern.slice(0, -3);
4299
- return target === prefix || target.startsWith(`${prefix}/`) || target.includes(`/${prefix}/`);
4471
+ const directoryPattern = pattern.slice(0, -3);
4472
+ if (hasGlobSyntax(directoryPattern)) {
4473
+ const expression = new RegExp(`^${globPatternRegExpSource(directoryPattern)}(?:/.*)?$`);
4474
+ return expression.test(target);
4475
+ }
4476
+ return target === directoryPattern || target.startsWith(`${directoryPattern}/`);
4300
4477
  }
4301
4478
  if (pattern.startsWith("**/")) {
4302
4479
  const suffix = pattern.slice(3);
@@ -4305,10 +4482,14 @@ function matchesIgnorePattern(target, pattern) {
4305
4482
  }
4306
4483
  }
4307
4484
  if (!hasGlobSyntax(pattern)) {
4485
+ if (anchored || pattern.includes("/")) {
4486
+ return target === pattern || target.startsWith(`${pattern}/`);
4487
+ }
4308
4488
  return target === pattern || target.endsWith(`/${pattern}`) || target.startsWith(`${pattern}/`);
4309
4489
  }
4310
4490
 
4311
- const expression = new RegExp(`(^|/)${globPatternRegExpSource(pattern)}$`);
4491
+ const prefix = anchored || pattern.includes("/") ? "^" : "(^|/)";
4492
+ const expression = new RegExp(`${prefix}${globPatternRegExpSource(pattern)}$`);
4312
4493
  return expression.test(target);
4313
4494
  }
4314
4495
 
@@ -4417,6 +4598,9 @@ function ruleMetaForRuleId(ruleId) {
4417
4598
  if (FIXABLE_BUILTIN_RULE_IDS.has(ruleId)) {
4418
4599
  meta.fixable = "code";
4419
4600
  }
4601
+ if (SUGGESTION_BUILTIN_RULE_IDS.has(ruleId)) {
4602
+ meta.hasSuggestions = true;
4603
+ }
4420
4604
  return meta;
4421
4605
  }
4422
4606
 
@@ -4507,6 +4691,21 @@ function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
4507
4691
  if (fixes.length > 0) {
4508
4692
  message.fix = fixes.length === 1 ? fixes[0] : fixes;
4509
4693
  }
4694
+ const suggestions = (diagnostic.suggestions ?? []).map((suggestion) => {
4695
+ const suggestionFixes = (suggestion.fix ?? []).map((fix) => ({
4696
+ range: fix.range,
4697
+ text: fix.text
4698
+ }));
4699
+ return {
4700
+ desc: suggestion.desc,
4701
+ ...(suggestionFixes.length === 0 ? {} : {
4702
+ fix: suggestionFixes.length === 1 ? suggestionFixes[0] : suggestionFixes
4703
+ })
4704
+ };
4705
+ });
4706
+ if (suggestions.length > 0) {
4707
+ message.suggestions = suggestions;
4708
+ }
4510
4709
  return message;
4511
4710
  }
4512
4711
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.2.9",
3
+ "version": "0.3.4",
4
4
  "description": "High-performance JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -76,13 +76,15 @@
76
76
  "use-at-your-own-risk.js"
77
77
  ],
78
78
  "optionalDependencies": {
79
- "@utoo/lint-darwin-arm64": "0.2.9",
80
- "@utoo/lint-darwin-x64": "0.2.9",
81
- "@utoo/lint-linux-arm64": "0.2.9",
82
- "@utoo/lint-linux-x64": "0.2.9",
83
- "@utoo/lint-win32-x64": "0.2.9"
79
+ "@utoo/lint-darwin-arm64": "0.3.4",
80
+ "@utoo/lint-darwin-x64": "0.3.4",
81
+ "@utoo/lint-linux-arm64": "0.3.4",
82
+ "@utoo/lint-linux-x64": "0.3.4",
83
+ "@utoo/lint-win32-x64": "0.3.4"
84
84
  },
85
85
  "dependencies": {
86
+ "@eslint/eslintrc": "2.1.4",
87
+ "@eslint/js": "8.57.1",
86
88
  "jiti": "2.7.0"
87
89
  },
88
90
  "publishConfig": {
package/schema.json CHANGED
@@ -28,6 +28,35 @@
28
28
  "$ref": "#/definitions/ruleSeverity"
29
29
  }
30
30
  },
31
+ "settings": {
32
+ "description": "Shared plugin settings.",
33
+ "type": "object",
34
+ "additionalProperties": true,
35
+ "properties": {
36
+ "jest": {
37
+ "type": "object",
38
+ "additionalProperties": true,
39
+ "properties": {
40
+ "version": {
41
+ "description": "Jest version used by version-aware Jest rules.",
42
+ "oneOf": [
43
+ { "type": "integer", "minimum": 1 },
44
+ { "type": "string", "pattern": "^[0-9]+(?:\\..*)?$" }
45
+ ]
46
+ },
47
+ "globalAliases": {
48
+ "description": "Additional global aliases for canonical Jest functions.",
49
+ "type": "object",
50
+ "additionalProperties": {
51
+ "type": "array",
52
+ "items": { "type": "string", "minLength": 1 },
53
+ "uniqueItems": true
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ },
31
60
  "files": {
32
61
  "description": "File globs this config entry applies to.",
33
62
  "oneOf": [
@@ -37,7 +66,18 @@
37
66
  {
38
67
  "type": "array",
39
68
  "items": {
40
- "type": "string"
69
+ "oneOf": [
70
+ {
71
+ "type": "string"
72
+ },
73
+ {
74
+ "type": "array",
75
+ "items": {
76
+ "type": "string"
77
+ },
78
+ "minItems": 1
79
+ }
80
+ ]
41
81
  }
42
82
  }
43
83
  ]