@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.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,24 @@ 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",
330
+ "promise/no-promise-in-callback",
331
+ "promise/no-return-in-finally",
332
+ "promise/no-return-wrap",
333
+ "promise/param-names",
334
+ "promise/valid-params",
311
335
  "jsx-a11y/alt-text",
312
336
  "jsx-a11y/anchor-has-content",
313
337
  "jsx-a11y/aria-props",
@@ -321,6 +345,11 @@ const BUILTIN_RULE_IDS = [
321
345
  "jsx-a11y/role-has-required-aria-props",
322
346
  "jsx-a11y/role-supports-aria-props",
323
347
  "jsx-a11y/scope",
348
+ "promise/always-return",
349
+ "promise/catch-or-return",
350
+ "promise/no-callback-in-promise",
351
+ "promise/no-nesting",
352
+ "promise/no-new-statics",
324
353
  "react/button-has-type",
325
354
  "react/default-props-match-prop-types",
326
355
  "react/display-name",
@@ -342,9 +371,12 @@ const BUILTIN_RULE_IDS = [
342
371
  "react/no-danger",
343
372
  "react/no-danger-with-children",
344
373
  "react/no-deprecated",
374
+ "react/no-direct-mutation-state",
345
375
  "react/no-find-dom-node",
376
+ "react/no-forward-ref",
346
377
  "react/no-is-mounted",
347
378
  "react/no-multi-comp",
379
+ "react/no-unstable-nested-components",
348
380
  "react/no-redundant-should-component-update",
349
381
  "react/no-render-return-value",
350
382
  "react/no-string-refs",
@@ -361,7 +393,9 @@ const BUILTIN_RULE_IDS = [
361
393
  "react/self-closing-comp",
362
394
  "react/style-prop-object",
363
395
  "react/void-dom-elements-no-children",
396
+ "react-hooks/exhaustive-deps",
364
397
  "react-hooks/rules-of-hooks",
398
+ "unused-imports/no-unused-imports",
365
399
  "@typescript-eslint/adjacent-overload-signatures",
366
400
  "@typescript-eslint/array-type",
367
401
  "@typescript-eslint/ban-ts-comment",
@@ -379,6 +413,7 @@ const BUILTIN_RULE_IDS = [
379
413
  "@typescript-eslint/no-dupe-class-members",
380
414
  "@typescript-eslint/no-empty-function",
381
415
  "@typescript-eslint/no-empty-interface",
416
+ "@typescript-eslint/no-empty-object-type",
382
417
  "@typescript-eslint/no-duplicate-enum-values",
383
418
  "@typescript-eslint/no-extra-semi",
384
419
  "@typescript-eslint/no-extra-non-null-assertion",
@@ -394,6 +429,7 @@ const BUILTIN_RULE_IDS = [
394
429
  "@typescript-eslint/no-shadow",
395
430
  "@typescript-eslint/no-this-alias",
396
431
  "@typescript-eslint/no-unsafe-declaration-merging",
432
+ "@typescript-eslint/no-unsafe-function-type",
397
433
  "@typescript-eslint/triple-slash-reference",
398
434
  "@typescript-eslint/typedef",
399
435
  "@typescript-eslint/unified-signatures",
@@ -437,7 +473,16 @@ const NATIVE_ONLY_RULE_IDS = [
437
473
  "semantic-errors"
438
474
  ];
439
475
  const NATIVE_RULE_IDS = [...BUILTIN_RULE_IDS, ...NATIVE_ONLY_RULE_IDS];
440
- const FIXABLE_BUILTIN_RULE_IDS = new Set(["no-extra-semi", "@typescript-eslint/no-extra-semi"]);
476
+ const FIXABLE_BUILTIN_RULE_IDS = new Set([
477
+ "jest/no-deprecated-functions",
478
+ "jest/no-jasmine-globals",
479
+ "no-extra-semi",
480
+ "@typescript-eslint/no-extra-semi",
481
+ "unused-imports/no-unused-imports"
482
+ ]);
483
+ const SUGGESTION_BUILTIN_RULE_IDS = new Set([
484
+ "jest/no-focused-tests"
485
+ ]);
441
486
  const BUILTIN_RULES = new Map(BUILTIN_RULE_IDS.map((ruleId) => [ruleId, createBuiltinRule(ruleId)]));
442
487
 
443
488
  export class UtooLint {
@@ -747,7 +792,7 @@ function parserOptionsForConfig(config, filePath, cwd) {
747
792
 
748
793
  function customRuleEntriesForConfig(config, rules, filePath, cwd) {
749
794
  const entries = new Map();
750
- for (const [ruleId, ruleConfig] of ruleConfigEntries(config, filePath, cwd)) {
795
+ for (const [ruleId, ruleConfig] of Object.entries(rulesFromConfig(config, filePath, cwd))) {
751
796
  if (!rules.has(ruleId)) {
752
797
  continue;
753
798
  }
@@ -804,19 +849,6 @@ function matchingConfigEntries(config, filePath = undefined, cwd = undefined) {
804
849
  return [config];
805
850
  }
806
851
 
807
- function ruleConfigEntries(config, filePath = undefined, cwd = undefined) {
808
- if (Array.isArray(config)) {
809
- return config.flatMap((item) => ruleConfigEntries(item, filePath, cwd));
810
- }
811
- if (!config || typeof config !== "object" || !config.rules || typeof config.rules !== "object") {
812
- return [];
813
- }
814
- if (!configAppliesToFile(config, filePath, cwd)) {
815
- return [];
816
- }
817
- return Object.entries(config.rules);
818
- }
819
-
820
852
  function configWithoutCustomRules(config, rules) {
821
853
  if (Array.isArray(config)) {
822
854
  return config.map((item) => configWithoutCustomRules(item, rules));
@@ -2117,10 +2149,7 @@ function mergeRuleTesterConfig(baseConfig, overrideConfig) {
2117
2149
  return {
2118
2150
  ...baseConfig,
2119
2151
  ...overrideConfig,
2120
- rules: {
2121
- ...(baseConfig.rules ?? {}),
2122
- ...(overrideConfig.rules ?? {})
2123
- }
2152
+ rules: mergeRuleConfigMaps(baseConfig.rules, overrideConfig.rules)
2124
2153
  };
2125
2154
  }
2126
2155
 
@@ -2579,6 +2608,20 @@ export class CLIEngine {
2579
2608
  }
2580
2609
  }
2581
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
+
2582
2625
  export function run(args = [], options = {}) {
2583
2626
  const cliArgs = normalizeStringArray(args, "args");
2584
2627
  const env = options.env ? { ...process.env, ...options.env } : process.env;
@@ -2589,7 +2632,8 @@ export function run(args = [], options = {}) {
2589
2632
  env,
2590
2633
  encoding: options.encoding ?? "utf8",
2591
2634
  input: options.input,
2592
- stdio: options.stdio
2635
+ stdio: options.stdio,
2636
+ maxBuffer: nativeMaxBuffer(options, env)
2593
2637
  });
2594
2638
  }
2595
2639
 
@@ -2731,8 +2775,9 @@ export function runCli(args = [], options = {}) {
2731
2775
  extraArgs: parsed.passthroughArgs,
2732
2776
  preserveNativeDefaults: Boolean(parsed.options.noConfig)
2733
2777
  });
2734
- const targets = parsed.targets.length > 0 ? parsed.targets : defaultLintTargets(cliOptions);
2735
- 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 });
2736
2781
  report.diagnostics = normalizeReportDiagnostics(report.diagnostics, cliOptions);
2737
2782
  report.exitCode = exitCodeForDiagnostics(report.diagnostics);
2738
2783
  const status = report.exitCode ?? exitCodeForDiagnostics(report.diagnostics);
@@ -2840,7 +2885,8 @@ export function runFishlint(args = [], options = {}) {
2840
2885
  env,
2841
2886
  encoding: options.encoding ?? "utf8",
2842
2887
  input: options.input,
2843
- stdio: options.stdio
2888
+ stdio: options.stdio,
2889
+ maxBuffer: nativeMaxBuffer(options, env)
2844
2890
  });
2845
2891
  }
2846
2892
 
@@ -3042,9 +3088,13 @@ function booleanFlagValue(arg) {
3042
3088
 
3043
3089
  export function lintFiles(paths, options = {}) {
3044
3090
  options = withConfigCache(options);
3091
+ const usesDefaultTargets = paths == null || options.useDefaultConfigTargets;
3045
3092
  const targets = lintTargetsForInput(paths, options);
3046
3093
  const ignoredDiagnostics = ignoredLintPathDiagnostics(targets, options);
3047
- const lintPaths = filteredLintPaths(targets, options);
3094
+ let lintPaths = filteredLintPaths(targets, options);
3095
+ if (usesDefaultTargets) {
3096
+ lintPaths = filterDefaultConfigLintPaths(lintPaths, options);
3097
+ }
3048
3098
  if (lintPaths.length === 0) {
3049
3099
  return { files: 0, filePaths: [], diagnostics: ignoredDiagnostics, suppressedDiagnostics: [], exitCode: exitCodeForDiagnostics(ignoredDiagnostics) };
3050
3100
  }
@@ -3134,13 +3184,15 @@ function nativeConfigRunsForFiles(paths, options) {
3134
3184
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
3135
3185
  const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath);
3136
3186
  hasConfigSource ||= configured;
3137
- 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;
3138
3190
  if (configured && options.rules) {
3139
3191
  rules = selectedRulesWithConfigOptions(rules, options.rules);
3140
3192
  }
3141
- const signature = configured ? stableConfigSignature(rules) : "<native-defaults>";
3193
+ const signature = configured ? stableConfigSignature({ rules, settings }) : "<native-defaults>";
3142
3194
  if (!groups.has(signature)) {
3143
- groups.set(signature, { paths: [], rules, configured });
3195
+ groups.set(signature, { paths: [], rules, settings, configured });
3144
3196
  }
3145
3197
  groups.get(signature).paths.push(filePath);
3146
3198
  }
@@ -3170,7 +3222,10 @@ function nativeConfigRunsForFiles(paths, options) {
3170
3222
  config: undefined,
3171
3223
  noConfig: true,
3172
3224
  baseConfig: undefined,
3173
- overrideConfig: { rules: allDisabledNativeRules(group.rules) },
3225
+ overrideConfig: {
3226
+ rules: allDisabledNativeRules(group.rules),
3227
+ ...(group.settings ? { settings: group.settings } : {})
3228
+ },
3174
3229
  forceMaterializedConfig: true
3175
3230
  }
3176
3231
  };
@@ -3399,12 +3454,12 @@ function calculatedConfig(options = {}, filePath) {
3399
3454
  );
3400
3455
  return {
3401
3456
  ...configData,
3402
- rules: {
3403
- ...rulesFromConfig(options.baseConfig, filePath, options.cwd),
3404
- ...rulesFromFileConfig(options, filePath),
3405
- ...rulesFromConfig(options.overrideConfig, filePath, options.cwd),
3406
- ...rulesFromNativeRuleList(options.rules)
3407
- }
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
+ )
3408
3463
  };
3409
3464
  }
3410
3465
 
@@ -3499,10 +3554,10 @@ function rulesFromConfig(config, filePath, cwd) {
3499
3554
  return {};
3500
3555
  }
3501
3556
  if (Array.isArray(config)) {
3502
- return config.reduce((rules, entry) => ({
3503
- ...rules,
3504
- ...rulesFromConfig(entry, filePath, cwd)
3505
- }), {});
3557
+ return config.reduce(
3558
+ (rules, entry) => mergeRuleConfigMaps(rules, rulesFromConfig(entry, filePath, cwd)),
3559
+ {}
3560
+ );
3506
3561
  }
3507
3562
  if (!configAppliesToFile(config, filePath, cwd)) {
3508
3563
  return {};
@@ -3555,12 +3610,64 @@ function defaultLintTargets(options = {}) {
3555
3610
  return ["."];
3556
3611
  }
3557
3612
 
3558
- 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);
3559
3619
  return targets.length > 0
3560
3620
  ? targets.map((target) => resolveConfigPattern(target, dirname(configPath)))
3561
3621
  : ["."];
3562
3622
  }
3563
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
+
3564
3671
  function resolveConfigPattern(pattern, configDirectory) {
3565
3672
  if (isAbsolute(pattern)) {
3566
3673
  return pattern;
@@ -3584,8 +3691,10 @@ function configAppliesToFile(config, filePath, cwd) {
3584
3691
  }
3585
3692
 
3586
3693
  const normalized = normalizeIgnoredPath(filePath, cwd ?? process.cwd());
3587
- const files = normalizeConfigPatterns(config.files);
3588
- 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
+ ))) {
3589
3698
  return false;
3590
3699
  }
3591
3700
 
@@ -3593,6 +3702,23 @@ function configAppliesToFile(config, filePath, cwd) {
3593
3702
  return !pathIgnoredByPatterns(normalized, ignores);
3594
3703
  }
3595
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
+
3596
3722
  function normalizeConfigPatterns(patterns) {
3597
3723
  if (!patterns) {
3598
3724
  return [];
@@ -3654,7 +3780,15 @@ function withTemporaryConfig(options, callback) {
3654
3780
  const rules = shouldMaterializeFileConfig
3655
3781
  ? materializedRulesFromConfigs(...configs)
3656
3782
  : runtimeRulesFromConfigs(...configs);
3657
- 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) {
3658
3792
  if (shouldMaterializeFileConfig) {
3659
3793
  return callback({
3660
3794
  ...options,
@@ -3666,7 +3800,7 @@ function withTemporaryConfig(options, callback) {
3666
3800
  }
3667
3801
  const enabledRules = enabledRuleNamesFromConfigs(...configs);
3668
3802
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
3669
- if (!shouldMaterializeFileConfig && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
3803
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
3670
3804
  return callback({
3671
3805
  ...options,
3672
3806
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -3678,7 +3812,7 @@ function withTemporaryConfig(options, callback) {
3678
3812
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
3679
3813
  const configPath = join(tmp, "utlint.config.json");
3680
3814
  try {
3681
- writeFileSync(configPath, JSON.stringify({ rules }));
3815
+ writeFileSync(configPath, JSON.stringify({ rules, ...(hasSettings ? { settings } : {}) }));
3682
3816
  return callback({
3683
3817
  ...options,
3684
3818
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
@@ -3697,11 +3831,7 @@ function enabledRuleNames(rules) {
3697
3831
  }
3698
3832
 
3699
3833
  function runtimeRulesFromConfigs(...configs) {
3700
- const rules = {};
3701
- for (const config of configs) {
3702
- Object.assign(rules, runtimeRulesFromConfig(config));
3703
- }
3704
- return rules;
3834
+ return mergeRuleConfigMaps(...configs.map(runtimeRulesFromConfig));
3705
3835
  }
3706
3836
 
3707
3837
  function materializedRulesFromConfigs(...configs) {
@@ -3713,10 +3843,10 @@ function runtimeRulesFromConfig(config) {
3713
3843
  return {};
3714
3844
  }
3715
3845
  if (Array.isArray(config)) {
3716
- return config.reduce((rules, entry) => ({
3717
- ...rules,
3718
- ...runtimeRulesFromConfig(entry)
3719
- }), {});
3846
+ return config.reduce(
3847
+ (rules, entry) => mergeRuleConfigMaps(rules, runtimeRulesFromConfig(entry)),
3848
+ {}
3849
+ );
3720
3850
  }
3721
3851
 
3722
3852
  const rules = {};
@@ -3726,6 +3856,26 @@ function runtimeRulesFromConfig(config) {
3726
3856
  return rules;
3727
3857
  }
3728
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
+
3729
3879
  function enabledRuleNamesFromConfigs(...configs) {
3730
3880
  const rules = new Set();
3731
3881
  for (const config of configs) {
@@ -4040,7 +4190,7 @@ function collectGlobFiles(target, cwd, patterns, expression, files) {
4040
4190
 
4041
4191
  const child = join(target, entry.name);
4042
4192
  if (entry.isDirectory()) {
4043
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4193
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4044
4194
  collectGlobFiles(child, cwd, patterns, expression, files);
4045
4195
  }
4046
4196
  continue;
@@ -4110,7 +4260,7 @@ function expandLintTarget(target, cwd, patterns) {
4110
4260
  if (stat.isFile()) {
4111
4261
  return isLintableFilePath(target) && !pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns) ? [target] : [];
4112
4262
  }
4113
- if (!stat.isDirectory() || pathIgnoredByPatterns(normalizeIgnoredPath(target, cwd), patterns)) {
4263
+ if (!stat.isDirectory() || !shouldTraverseDirectory(normalizeIgnoredPath(target, cwd), patterns)) {
4114
4264
  return [];
4115
4265
  }
4116
4266
 
@@ -4128,7 +4278,7 @@ function collectLintableFiles(target, cwd, patterns, files) {
4128
4278
 
4129
4279
  const child = join(target, entry.name);
4130
4280
  if (entry.isDirectory()) {
4131
- if (!pathIgnoredByPatterns(normalizeIgnoredPath(child, cwd), patterns)) {
4281
+ if (shouldTraverseDirectory(normalizeIgnoredPath(child, cwd), patterns)) {
4132
4282
  collectLintableFiles(child, cwd, patterns, files);
4133
4283
  }
4134
4284
  continue;
@@ -4261,7 +4411,7 @@ function normalizeIgnoredPath(filePath, cwd) {
4261
4411
  }
4262
4412
 
4263
4413
  function normalizeIgnoredPattern(pattern) {
4264
- return normalizePath(pattern.replace(/^!/, "").replace(/^\//, "")).replace(/\/+$/, "");
4414
+ return normalizePath(pattern.replace(/^!/, "")).replace(/\/+$/, "");
4265
4415
  }
4266
4416
 
4267
4417
  function pathIgnoredByPatterns(target, patterns) {
@@ -4275,10 +4425,55 @@ function pathIgnoredByPatterns(target, patterns) {
4275
4425
  return ignored;
4276
4426
  }
4277
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
+
4278
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
+ }
4279
4470
  if (pattern.endsWith("/**")) {
4280
- const prefix = pattern.slice(0, -3);
4281
- 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}/`);
4282
4477
  }
4283
4478
  if (pattern.startsWith("**/")) {
4284
4479
  const suffix = pattern.slice(3);
@@ -4287,10 +4482,14 @@ function matchesIgnorePattern(target, pattern) {
4287
4482
  }
4288
4483
  }
4289
4484
  if (!hasGlobSyntax(pattern)) {
4485
+ if (anchored || pattern.includes("/")) {
4486
+ return target === pattern || target.startsWith(`${pattern}/`);
4487
+ }
4290
4488
  return target === pattern || target.endsWith(`/${pattern}`) || target.startsWith(`${pattern}/`);
4291
4489
  }
4292
4490
 
4293
- const expression = new RegExp(`(^|/)${globPatternRegExpSource(pattern)}$`);
4491
+ const prefix = anchored || pattern.includes("/") ? "^" : "(^|/)";
4492
+ const expression = new RegExp(`${prefix}${globPatternRegExpSource(pattern)}$`);
4294
4493
  return expression.test(target);
4295
4494
  }
4296
4495
 
@@ -4399,6 +4598,9 @@ function ruleMetaForRuleId(ruleId) {
4399
4598
  if (FIXABLE_BUILTIN_RULE_IDS.has(ruleId)) {
4400
4599
  meta.fixable = "code";
4401
4600
  }
4601
+ if (SUGGESTION_BUILTIN_RULE_IDS.has(ruleId)) {
4602
+ meta.hasSuggestions = true;
4603
+ }
4402
4604
  return meta;
4403
4605
  }
4404
4606
 
@@ -4489,6 +4691,21 @@ function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
4489
4691
  if (fixes.length > 0) {
4490
4692
  message.fix = fixes.length === 1 ? fixes[0] : fixes;
4491
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
+ }
4492
4709
  return message;
4493
4710
  }
4494
4711
 
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.2.8",
4
- "description": "Experimental JavaScript and TypeScript linter powered by Yuku",
3
+ "version": "0.3.4",
4
+ "description": "High-performance JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/fireairforce/utoo-lint.git"
8
+ "url": "git+https://github.com/utooland/utoo-lint.git"
9
9
  },
10
10
  "bugs": {
11
- "url": "https://github.com/fireairforce/utoo-lint/issues"
11
+ "url": "https://github.com/utooland/utoo-lint/issues"
12
12
  },
13
- "homepage": "https://github.com/fireairforce/utoo-lint#readme",
13
+ "homepage": "https://github.com/utooland/utoo-lint#readme",
14
14
  "type": "module",
15
15
  "engines": {
16
- "node": ">=18.0.0"
16
+ "node": ">=20.0.0"
17
17
  },
18
18
  "types": "./index.d.ts",
19
19
  "exports": {
@@ -76,13 +76,15 @@
76
76
  "use-at-your-own-risk.js"
77
77
  ],
78
78
  "optionalDependencies": {
79
- "@utoo/lint-darwin-arm64": "0.2.8",
80
- "@utoo/lint-darwin-x64": "0.2.8",
81
- "@utoo/lint-linux-arm64": "0.2.8",
82
- "@utoo/lint-linux-x64": "0.2.8",
83
- "@utoo/lint-win32-x64": "0.2.8"
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": {