pickier 0.1.28 → 0.1.29

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.
Files changed (263) hide show
  1. package/dist/ast.d.ts +13 -0
  2. package/dist/bin/cli.js +507 -302
  3. package/dist/plugins/eslint.d.ts +2 -0
  4. package/dist/plugins/general.d.ts +2 -0
  5. package/dist/plugins/index.d.ts +2 -0
  6. package/dist/plugins/lockfile.d.ts +22 -0
  7. package/dist/plugins/markdown.d.ts +2 -0
  8. package/dist/plugins/node.d.ts +2 -0
  9. package/dist/plugins/perfectionist.d.ts +6 -0
  10. package/dist/plugins/pickier.d.ts +2 -0
  11. package/dist/plugins/publint.d.ts +10 -0
  12. package/dist/plugins/quality.d.ts +2 -0
  13. package/dist/plugins/regexp.d.ts +2 -0
  14. package/dist/plugins/shell.d.ts +2 -0
  15. package/dist/plugins/spell.d.ts +3 -0
  16. package/dist/plugins/style.d.ts +2 -0
  17. package/dist/plugins/ts.d.ts +2 -0
  18. package/dist/plugins/unused-imports.d.ts +6 -0
  19. package/dist/plugins/utils.d.ts +4 -0
  20. package/dist/rules/general/_template-tracking.d.ts +31 -0
  21. package/dist/rules/general/array-callback-return.d.ts +2 -0
  22. package/dist/rules/general/constructor-super.d.ts +2 -0
  23. package/dist/rules/general/for-direction.d.ts +2 -0
  24. package/dist/rules/general/getter-return.d.ts +2 -0
  25. package/dist/rules/general/no-async-promise-executor.d.ts +2 -0
  26. package/dist/rules/general/no-compare-neg-zero.d.ts +2 -0
  27. package/dist/rules/general/no-cond-assign.d.ts +2 -0
  28. package/dist/rules/general/no-const-assign.d.ts +2 -0
  29. package/dist/rules/general/no-constant-condition.d.ts +2 -0
  30. package/dist/rules/general/no-constructor-return.d.ts +2 -0
  31. package/dist/rules/general/no-dupe-class-members.d.ts +2 -0
  32. package/dist/rules/general/no-dupe-keys.d.ts +2 -0
  33. package/dist/rules/general/no-duplicate-case.d.ts +2 -0
  34. package/dist/rules/general/no-empty-pattern.d.ts +2 -0
  35. package/dist/rules/general/no-fallthrough.d.ts +2 -0
  36. package/dist/rules/general/no-irregular-whitespace.d.ts +2 -0
  37. package/dist/rules/general/no-loss-of-precision.d.ts +2 -0
  38. package/dist/rules/general/no-new.d.ts +13 -0
  39. package/dist/rules/general/no-promise-executor-return.d.ts +2 -0
  40. package/dist/rules/general/no-redeclare.d.ts +2 -0
  41. package/dist/rules/general/no-regex-spaces.d.ts +10 -0
  42. package/dist/rules/general/no-self-assign.d.ts +2 -0
  43. package/dist/rules/general/no-self-compare.d.ts +2 -0
  44. package/dist/rules/general/no-sparse-arrays.d.ts +2 -0
  45. package/dist/rules/general/no-undef.d.ts +2 -0
  46. package/dist/rules/general/no-unreachable.d.ts +2 -0
  47. package/dist/rules/general/no-unsafe-negation.d.ts +2 -0
  48. package/dist/rules/general/no-unused-vars.d.ts +2 -0
  49. package/dist/rules/general/no-useless-catch.d.ts +2 -0
  50. package/dist/rules/general/prefer-const.d.ts +2 -0
  51. package/dist/rules/general/prefer-object-spread.d.ts +2 -0
  52. package/dist/rules/general/prefer-template.d.ts +2 -0
  53. package/dist/rules/general/use-isnan.d.ts +2 -0
  54. package/dist/rules/general/valid-typeof.d.ts +2 -0
  55. package/dist/rules/imports/first.d.ts +2 -0
  56. package/dist/rules/imports/import-dedupe.d.ts +2 -0
  57. package/dist/rules/imports/named.d.ts +2 -0
  58. package/dist/rules/imports/no-cycle.d.ts +2 -0
  59. package/dist/rules/imports/no-duplicate-imports.d.ts +2 -0
  60. package/dist/rules/imports/no-import-dist.d.ts +2 -0
  61. package/dist/rules/imports/no-import-node-modules-by-path.d.ts +2 -0
  62. package/dist/rules/imports/no-unresolved.d.ts +2 -0
  63. package/dist/rules/lockfile/parser.d.ts +79 -0
  64. package/dist/rules/lockfile/validate-host.d.ts +18 -0
  65. package/dist/rules/lockfile/validate-https.d.ts +14 -0
  66. package/dist/rules/lockfile/validate-integrity.d.ts +19 -0
  67. package/dist/rules/lockfile/validate-package-names.d.ts +23 -0
  68. package/dist/rules/lockfile/validate-scheme.d.ts +17 -0
  69. package/dist/rules/markdown/_fence-tracking.d.ts +32 -0
  70. package/dist/rules/markdown/_shared.d.ts +9 -0
  71. package/dist/rules/markdown/blanks-around-fences.d.ts +5 -0
  72. package/dist/rules/markdown/blanks-around-headings.d.ts +5 -0
  73. package/dist/rules/markdown/blanks-around-lists.d.ts +5 -0
  74. package/dist/rules/markdown/blanks-around-tables.d.ts +5 -0
  75. package/dist/rules/markdown/code-block-style.d.ts +5 -0
  76. package/dist/rules/markdown/code-fence-style.d.ts +5 -0
  77. package/dist/rules/markdown/commands-show-output.d.ts +5 -0
  78. package/dist/rules/markdown/descriptive-link-text.d.ts +5 -0
  79. package/dist/rules/markdown/emphasis-style.d.ts +5 -0
  80. package/dist/rules/markdown/fenced-code-language.d.ts +5 -0
  81. package/dist/rules/markdown/first-line-heading.d.ts +5 -0
  82. package/dist/rules/markdown/heading-increment.d.ts +5 -0
  83. package/dist/rules/markdown/heading-start-left.d.ts +5 -0
  84. package/dist/rules/markdown/heading-style.d.ts +5 -0
  85. package/dist/rules/markdown/hr-style.d.ts +5 -0
  86. package/dist/rules/markdown/line-length.d.ts +5 -0
  87. package/dist/rules/markdown/link-fragments.d.ts +5 -0
  88. package/dist/rules/markdown/link-image-reference-definitions.d.ts +5 -0
  89. package/dist/rules/markdown/link-image-style.d.ts +5 -0
  90. package/dist/rules/markdown/list-indent.d.ts +5 -0
  91. package/dist/rules/markdown/list-marker-space.d.ts +5 -0
  92. package/dist/rules/markdown/no-alt-text.d.ts +5 -0
  93. package/dist/rules/markdown/no-bare-urls.d.ts +5 -0
  94. package/dist/rules/markdown/no-blanks-blockquote.d.ts +5 -0
  95. package/dist/rules/markdown/no-duplicate-heading.d.ts +10 -0
  96. package/dist/rules/markdown/no-emphasis-as-heading.d.ts +9 -0
  97. package/dist/rules/markdown/no-empty-links.d.ts +5 -0
  98. package/dist/rules/markdown/no-hard-tabs.d.ts +5 -0
  99. package/dist/rules/markdown/no-inline-html.d.ts +5 -0
  100. package/dist/rules/markdown/no-missing-space-atx.d.ts +5 -0
  101. package/dist/rules/markdown/no-missing-space-closed-atx.d.ts +5 -0
  102. package/dist/rules/markdown/no-multiple-blanks.d.ts +5 -0
  103. package/dist/rules/markdown/no-multiple-space-atx.d.ts +5 -0
  104. package/dist/rules/markdown/no-multiple-space-blockquote.d.ts +5 -0
  105. package/dist/rules/markdown/no-multiple-space-closed-atx.d.ts +5 -0
  106. package/dist/rules/markdown/no-reversed-links.d.ts +5 -0
  107. package/dist/rules/markdown/no-space-in-code.d.ts +5 -0
  108. package/dist/rules/markdown/no-space-in-emphasis.d.ts +5 -0
  109. package/dist/rules/markdown/no-space-in-links.d.ts +5 -0
  110. package/dist/rules/markdown/no-trailing-punctuation.d.ts +5 -0
  111. package/dist/rules/markdown/no-trailing-spaces.d.ts +5 -0
  112. package/dist/rules/markdown/ol-prefix.d.ts +5 -0
  113. package/dist/rules/markdown/proper-names.d.ts +5 -0
  114. package/dist/rules/markdown/reference-links-images.d.ts +14 -0
  115. package/dist/rules/markdown/required-headings.d.ts +5 -0
  116. package/dist/rules/markdown/single-title.d.ts +5 -0
  117. package/dist/rules/markdown/single-trailing-newline.d.ts +5 -0
  118. package/dist/rules/markdown/strong-style.d.ts +5 -0
  119. package/dist/rules/markdown/table-column-count.d.ts +5 -0
  120. package/dist/rules/markdown/table-column-style.d.ts +5 -0
  121. package/dist/rules/markdown/table-pipe-style.d.ts +5 -0
  122. package/dist/rules/markdown/ul-indent.d.ts +14 -0
  123. package/dist/rules/markdown/ul-style.d.ts +5 -0
  124. package/dist/rules/node/prefer-global-buffer.d.ts +9 -0
  125. package/dist/rules/node/prefer-global-process.d.ts +9 -0
  126. package/dist/rules/quality/complexity.d.ts +2 -0
  127. package/dist/rules/quality/default-case.d.ts +2 -0
  128. package/dist/rules/quality/eqeqeq.d.ts +2 -0
  129. package/dist/rules/quality/max-depth.d.ts +2 -0
  130. package/dist/rules/quality/max-lines-per-function.d.ts +2 -0
  131. package/dist/rules/quality/no-alert.d.ts +2 -0
  132. package/dist/rules/quality/no-await-in-loop.d.ts +2 -0
  133. package/dist/rules/quality/no-caller.d.ts +2 -0
  134. package/dist/rules/quality/no-case-declarations.d.ts +2 -0
  135. package/dist/rules/quality/no-else-return.d.ts +2 -0
  136. package/dist/rules/quality/no-empty-function.d.ts +2 -0
  137. package/dist/rules/quality/no-empty.d.ts +2 -0
  138. package/dist/rules/quality/no-eval.d.ts +2 -0
  139. package/dist/rules/quality/no-extend-native.d.ts +2 -0
  140. package/dist/rules/quality/no-extra-boolean-cast.d.ts +2 -0
  141. package/dist/rules/quality/no-global-assign.d.ts +2 -0
  142. package/dist/rules/quality/no-implied-eval.d.ts +2 -0
  143. package/dist/rules/quality/no-iterator.d.ts +2 -0
  144. package/dist/rules/quality/no-lonely-if.d.ts +2 -0
  145. package/dist/rules/quality/no-new-func.d.ts +2 -0
  146. package/dist/rules/quality/no-new-wrappers.d.ts +2 -0
  147. package/dist/rules/quality/no-new.d.ts +2 -0
  148. package/dist/rules/quality/no-octal.d.ts +2 -0
  149. package/dist/rules/quality/no-param-reassign.d.ts +2 -0
  150. package/dist/rules/quality/no-proto.d.ts +2 -0
  151. package/dist/rules/quality/no-return-assign.d.ts +2 -0
  152. package/dist/rules/quality/no-sequences.d.ts +2 -0
  153. package/dist/rules/quality/no-shadow.d.ts +2 -0
  154. package/dist/rules/quality/no-throw-literal.d.ts +2 -0
  155. package/dist/rules/quality/no-use-before-define.d.ts +2 -0
  156. package/dist/rules/quality/no-useless-call.d.ts +2 -0
  157. package/dist/rules/quality/no-useless-concat.d.ts +2 -0
  158. package/dist/rules/quality/no-useless-escape.d.ts +2 -0
  159. package/dist/rules/quality/no-useless-rename.d.ts +2 -0
  160. package/dist/rules/quality/no-useless-return.d.ts +2 -0
  161. package/dist/rules/quality/no-var.d.ts +2 -0
  162. package/dist/rules/quality/no-with.d.ts +2 -0
  163. package/dist/rules/quality/prefer-arrow-callback.d.ts +2 -0
  164. package/dist/rules/quality/require-await.d.ts +2 -0
  165. package/dist/rules/regexp/no-super-linear-backtracking.d.ts +2 -0
  166. package/dist/rules/regexp/no-unused-capturing-group.d.ts +2 -0
  167. package/dist/rules/regexp/no-useless-lazy.d.ts +9 -0
  168. package/dist/rules/shell/_shared.d.ts +24 -0
  169. package/dist/rules/shell/command-substitution.d.ts +6 -0
  170. package/dist/rules/shell/consistent-case-terminators.d.ts +9 -0
  171. package/dist/rules/shell/function-style.d.ts +6 -0
  172. package/dist/rules/shell/heredoc-indent.d.ts +7 -0
  173. package/dist/rules/shell/indent.d.ts +2 -0
  174. package/dist/rules/shell/keyword-spacing.d.ts +10 -0
  175. package/dist/rules/shell/no-broken-redirect.d.ts +7 -0
  176. package/dist/rules/shell/no-cd-without-check.d.ts +6 -0
  177. package/dist/rules/shell/no-eval.d.ts +6 -0
  178. package/dist/rules/shell/no-exit-in-subshell.d.ts +7 -0
  179. package/dist/rules/shell/no-ls-parsing.d.ts +7 -0
  180. package/dist/rules/shell/no-trailing-semicolons.d.ts +6 -0
  181. package/dist/rules/shell/no-trailing-whitespace.d.ts +5 -0
  182. package/dist/rules/shell/no-useless-cat.d.ts +6 -0
  183. package/dist/rules/shell/no-variable-in-single-quotes.d.ts +7 -0
  184. package/dist/rules/shell/operator-spacing.d.ts +11 -0
  185. package/dist/rules/shell/prefer-double-brackets.d.ts +6 -0
  186. package/dist/rules/shell/prefer-printf.d.ts +6 -0
  187. package/dist/rules/shell/quote-variables.d.ts +2 -0
  188. package/dist/rules/shell/set-options.d.ts +8 -0
  189. package/dist/rules/shell/shebang.d.ts +2 -0
  190. package/dist/rules/sort/exports.d.ts +2 -0
  191. package/dist/rules/sort/heritage-clauses.d.ts +2 -0
  192. package/dist/rules/sort/imports.d.ts +2 -0
  193. package/dist/rules/sort/keys.d.ts +2 -0
  194. package/dist/rules/sort/named-imports.d.ts +2 -0
  195. package/dist/rules/sort/objects.d.ts +2 -0
  196. package/dist/rules/sort/tailwind-classes.d.ts +5 -0
  197. package/dist/rules/style/array-bracket-spacing.d.ts +2 -0
  198. package/dist/rules/style/arrow-parens.d.ts +2 -0
  199. package/dist/rules/style/arrow-spacing.d.ts +2 -0
  200. package/dist/rules/style/block-spacing.d.ts +2 -0
  201. package/dist/rules/style/brace-style.d.ts +10 -0
  202. package/dist/rules/style/comma-dangle.d.ts +2 -0
  203. package/dist/rules/style/comma-spacing.d.ts +2 -0
  204. package/dist/rules/style/comma-style.d.ts +2 -0
  205. package/dist/rules/style/computed-property-spacing.d.ts +2 -0
  206. package/dist/rules/style/consistent-chaining.d.ts +2 -0
  207. package/dist/rules/style/consistent-list-newline.d.ts +2 -0
  208. package/dist/rules/style/curly.d.ts +5 -0
  209. package/dist/rules/style/dot-location.d.ts +2 -0
  210. package/dist/rules/style/function-call-spacing.d.ts +2 -0
  211. package/dist/rules/style/generator-star-spacing.d.ts +2 -0
  212. package/dist/rules/style/if-newline.d.ts +2 -0
  213. package/dist/rules/style/indent-binary-ops.d.ts +2 -0
  214. package/dist/rules/style/indent-unindent.d.ts +2 -0
  215. package/dist/rules/style/key-spacing.d.ts +2 -0
  216. package/dist/rules/style/keyword-spacing.d.ts +2 -0
  217. package/dist/rules/style/lines-between-class-members.d.ts +2 -0
  218. package/dist/rules/style/max-statements-per-line.d.ts +2 -0
  219. package/dist/rules/style/multiline-ternary.d.ts +2 -0
  220. package/dist/rules/style/new-parens.d.ts +2 -0
  221. package/dist/rules/style/no-extra-parens.d.ts +2 -0
  222. package/dist/rules/style/no-floating-decimal.d.ts +2 -0
  223. package/dist/rules/style/no-mixed-operators.d.ts +2 -0
  224. package/dist/rules/style/no-mixed-spaces-and-tabs.d.ts +2 -0
  225. package/dist/rules/style/no-multi-spaces.d.ts +10 -0
  226. package/dist/rules/style/no-multiple-empty-lines.d.ts +9 -0
  227. package/dist/rules/style/no-tabs.d.ts +2 -0
  228. package/dist/rules/style/no-trailing-spaces.d.ts +9 -0
  229. package/dist/rules/style/no-whitespace-before-property.d.ts +2 -0
  230. package/dist/rules/style/object-curly-spacing.d.ts +2 -0
  231. package/dist/rules/style/operator-linebreak.d.ts +2 -0
  232. package/dist/rules/style/padded-blocks.d.ts +2 -0
  233. package/dist/rules/style/quote-props.d.ts +2 -0
  234. package/dist/rules/style/rest-spread-spacing.d.ts +2 -0
  235. package/dist/rules/style/semi-spacing.d.ts +2 -0
  236. package/dist/rules/style/space-before-blocks.d.ts +2 -0
  237. package/dist/rules/style/space-before-function-paren.d.ts +2 -0
  238. package/dist/rules/style/space-in-parens.d.ts +2 -0
  239. package/dist/rules/style/space-infix-ops.d.ts +2 -0
  240. package/dist/rules/style/space-unary-ops.d.ts +2 -0
  241. package/dist/rules/style/spaced-comment.d.ts +2 -0
  242. package/dist/rules/style/switch-colon-spacing.d.ts +2 -0
  243. package/dist/rules/style/template-curly-spacing.d.ts +2 -0
  244. package/dist/rules/style/template-tag-spacing.d.ts +2 -0
  245. package/dist/rules/style/top-level-function.d.ts +6 -0
  246. package/dist/rules/style/wrap-iife.d.ts +2 -0
  247. package/dist/rules/style/yield-star-spacing.d.ts +2 -0
  248. package/dist/rules/ts/member-delimiter-style.d.ts +4 -0
  249. package/dist/rules/ts/no-explicit-any.d.ts +2 -0
  250. package/dist/rules/ts/no-floating-promises.d.ts +2 -0
  251. package/dist/rules/ts/no-misused-promises.d.ts +2 -0
  252. package/dist/rules/ts/no-require-imports.d.ts +2 -0
  253. package/dist/rules/ts/no-top-level-await.d.ts +4 -0
  254. package/dist/rules/ts/no-ts-export-equal.d.ts +2 -0
  255. package/dist/rules/ts/no-unsafe-assignment.d.ts +2 -0
  256. package/dist/rules/ts/prefer-nullish-coalescing.d.ts +2 -0
  257. package/dist/rules/ts/prefer-optional-chain.d.ts +2 -0
  258. package/dist/rules/ts/type-annotation-spacing.d.ts +2 -0
  259. package/dist/rules/ts/type-generic-spacing.d.ts +2 -0
  260. package/dist/rules/ts/type-named-tuple-spacing.d.ts +2 -0
  261. package/dist/src/index.js +481 -289
  262. package/dist/utils.d.ts +12 -3
  263. package/package.json +2 -2
package/dist/bin/cli.js CHANGED
@@ -530,6 +530,16 @@ function unmaskStrings(text, strings) {
530
530
  return text;
531
531
  return text.replace(/@@S(\d+)@@/g, (_, idx) => strings[Number(idx)] ?? "");
532
532
  }
533
+ function collectIdentifierSet(text) {
534
+ const identifiers = new Set;
535
+ const identifierRe = /[$A-Z_][\w$]*/gi;
536
+ let match = identifierRe.exec(text);
537
+ while (match !== null) {
538
+ identifiers.add(match[0]);
539
+ match = identifierRe.exec(text);
540
+ }
541
+ return identifiers;
542
+ }
533
543
  function formatImports(source) {
534
544
  const firstChar = source[0];
535
545
  if (firstChar !== "i" && firstChar !== " " && firstChar !== "\t" && firstChar !== "/" && firstChar !== `
@@ -557,17 +567,8 @@ function formatImports(source) {
557
567
  return source;
558
568
  const rest = lines.slice(idx).join(`
559
569
  `);
560
- const codeText = rest;
561
- const isWordChar = (c) => c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_" || c === "$";
562
- const used = (name) => {
563
- for (let pos = codeText.indexOf(name, 0);pos !== -1; pos = codeText.indexOf(name, pos + name.length)) {
564
- const before = pos > 0 ? codeText[pos - 1] : " ";
565
- const after = pos + name.length < codeText.length ? codeText[pos + name.length] : " ";
566
- if (!isWordChar(before) && !isWordChar(after))
567
- return true;
568
- }
569
- return false;
570
- };
570
+ const usedIdentifiers = collectIdentifierSet(rest);
571
+ const used = (name) => usedIdentifiers.has(name);
571
572
  for (const imp of imports) {
572
573
  if (imp.kind !== "value")
573
574
  continue;
@@ -9909,15 +9910,7 @@ function globToRegex(pattern) {
9909
9910
  }
9910
9911
  return new RegExp(`^${src}$`);
9911
9912
  }
9912
- function matchesAnyPattern(filePath, patterns) {
9913
- for (const p of patterns) {
9914
- const re = globToRegex(p);
9915
- if (re.test(filePath) || re.test(filePath.replace(/\\/g, "/")))
9916
- return true;
9917
- }
9918
- return false;
9919
- }
9920
- function* walkDir(dir, ignore, dot, cwd) {
9913
+ function* walkDirWithMatcher(dir, ignoreMatcher, dot) {
9921
9914
  let entries;
9922
9915
  try {
9923
9916
  entries = readdirSync6(dir);
@@ -9928,8 +9921,7 @@ function* walkDir(dir, ignore, dot, cwd) {
9928
9921
  if (!dot && name.startsWith("."))
9929
9922
  continue;
9930
9923
  const full = join7(dir, name);
9931
- const rel = full.startsWith(`${cwd}/`) ? full.slice(cwd.length + 1) : full;
9932
- if (ignore.length && matchesAnyPattern(rel, ignore))
9924
+ if (ignoreMatcher(full))
9933
9925
  continue;
9934
9926
  let st;
9935
9927
  try {
@@ -9938,17 +9930,21 @@ function* walkDir(dir, ignore, dot, cwd) {
9938
9930
  continue;
9939
9931
  }
9940
9932
  if (st.isDirectory()) {
9941
- yield* walkDir(full, ignore, dot, cwd);
9933
+ yield* walkDirWithMatcher(full, ignoreMatcher, dot);
9942
9934
  } else {
9943
9935
  yield full;
9944
9936
  }
9945
9937
  }
9946
9938
  }
9939
+ function* walkDir(dir, ignore, dot, cwd) {
9940
+ yield* walkDirWithMatcher(dir, createIgnoreMatcher(ignore, cwd), dot);
9941
+ }
9947
9942
  async function glob(patterns, opts = {}) {
9948
9943
  const cwd = opts.cwd ?? process19.cwd();
9949
9944
  const ignore = opts.ignore ?? [];
9950
9945
  const dot = opts.dot ?? false;
9951
9946
  const absolute = opts.absolute ?? true;
9947
+ const ignoreMatcher = createIgnoreMatcher(ignore, cwd);
9952
9948
  if (typeof globalThis.Bun?.Glob !== "undefined") {
9953
9949
  const BunGlob = globalThis.Bun.Glob;
9954
9950
  const results2 = [];
@@ -9957,7 +9953,7 @@ async function glob(patterns, opts = {}) {
9957
9953
  for await (const file of g.scan({ cwd, dot, onlyFiles: opts.onlyFiles ?? true, followSymlinks: false })) {
9958
9954
  const full = isAbsolute3(file) ? file : join7(cwd, file);
9959
9955
  const rel = full.startsWith(`${cwd}/`) ? full.slice(cwd.length + 1) : full;
9960
- if (ignore.length && matchesAnyPattern(rel, ignore))
9956
+ if (ignoreMatcher(full))
9961
9957
  continue;
9962
9958
  results2.push(absolute ? full : rel);
9963
9959
  }
@@ -9972,7 +9968,7 @@ async function glob(patterns, opts = {}) {
9972
9968
  const st = statSync3(full);
9973
9969
  if (!st.isDirectory()) {
9974
9970
  const rel = full.startsWith(`${cwd}/`) ? full.slice(cwd.length + 1) : full;
9975
- if (!ignore.length || !matchesAnyPattern(rel, ignore))
9971
+ if (!ignoreMatcher(full))
9976
9972
  results.push(absolute ? full : rel);
9977
9973
  } else {
9978
9974
  for (const f of walkDir(full, ignore, dot, cwd))
@@ -10147,36 +10143,60 @@ function isCodeFile(file, allowedExts) {
10147
10143
  function toPosixPath(p) {
10148
10144
  return p.replace(/\\/g, "/").replace(/\/+/g, "/");
10149
10145
  }
10150
- function shouldIgnorePath(absPath, ignoreGlobs) {
10151
- const isOutsideProject = !absPath.startsWith(process19.cwd());
10152
- const universalIgnores = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**"];
10153
- const effectiveIgnores = isOutsideProject ? ignoreGlobs.filter((pattern) => universalIgnores.includes(pattern)) : ignoreGlobs;
10154
- const rel = toPosixPath(isOutsideProject ? absPath : absPath.slice(process19.cwd().length));
10155
- for (const g of effectiveIgnores) {
10146
+ function compileIgnoreGlobs(ignoreGlobs) {
10147
+ const compiled = [];
10148
+ for (const g of ignoreGlobs) {
10156
10149
  const gg = toPosixPath(g.trim());
10150
+ if (!gg)
10151
+ continue;
10157
10152
  const filePattern = gg.match(/\*\*\/\*\.(.+)$/);
10158
10153
  if (filePattern) {
10159
- const extension = filePattern[1];
10160
- if (rel.endsWith(`.${extension}`))
10161
- return true;
10154
+ compiled.push({ raw: gg, kind: "extension", value: filePattern[1] });
10162
10155
  continue;
10163
10156
  }
10164
10157
  const m = gg.match(/\*\*\/(.+?)\/\*\*$/);
10165
10158
  if (m) {
10166
- const name = m[1];
10167
- if (rel.includes(`/${name}/`) || rel.endsWith(`/${name}`))
10168
- return true;
10159
+ compiled.push({ raw: gg, kind: "segment", value: m[1] });
10169
10160
  continue;
10170
10161
  }
10171
10162
  const m2 = gg.match(/\*\*\/(.+)$/);
10172
10163
  if (m2) {
10173
- const name = m2[1].replace(/\/$/, "");
10174
- if (rel.includes(`/${name}/`) || rel.endsWith(`/${name}`))
10175
- return true;
10176
- continue;
10164
+ compiled.push({ raw: gg, kind: "suffix", value: m2[1].replace(/\/$/, "") });
10165
+ }
10166
+ }
10167
+ return compiled;
10168
+ }
10169
+ function createIgnoreMatcher(ignoreGlobs, cwd = process19.cwd()) {
10170
+ if (ignoreGlobs.length === 0)
10171
+ return () => false;
10172
+ const compiled = compileIgnoreGlobs(ignoreGlobs);
10173
+ if (compiled.length === 0)
10174
+ return () => false;
10175
+ const universalRaw = new Set(UNIVERSAL_IGNORES);
10176
+ return (absPath) => {
10177
+ const normalizedAbs = toPosixPath(absPath);
10178
+ const normalizedCwd = toPosixPath(cwd).replace(/\/$/, "");
10179
+ const isOutsideProject = !normalizedAbs.startsWith(normalizedCwd);
10180
+ const rel = isOutsideProject ? normalizedAbs : normalizedAbs.slice(normalizedCwd.length);
10181
+ for (const pattern of compiled) {
10182
+ if (isOutsideProject && !universalRaw.has(pattern.raw))
10183
+ continue;
10184
+ if (pattern.kind === "extension") {
10185
+ if (rel.endsWith(`.${pattern.value}`))
10186
+ return true;
10187
+ continue;
10188
+ }
10189
+ if (pattern.kind === "segment" || pattern.kind === "suffix") {
10190
+ const name = pattern.value;
10191
+ if (rel.includes(`/${name}/`) || rel.endsWith(`/${name}`))
10192
+ return true;
10193
+ }
10177
10194
  }
10178
- }
10179
- return false;
10195
+ return false;
10196
+ };
10197
+ }
10198
+ function shouldIgnorePath(absPath, ignoreGlobs) {
10199
+ return createIgnoreMatcher(ignoreGlobs)(absPath);
10180
10200
  }
10181
10201
  var MAX_FIXER_PASSES = 5, ENV, UNIVERSAL_IGNORES, colors, _cachedDefaultConfig = null;
10182
10202
  var init_utils = __esm(() => {
@@ -20705,11 +20725,11 @@ var init_no_unused_vars = __esm(() => {
20705
20725
  }
20706
20726
  };
20707
20727
  computeTemplateLines();
20728
+ const declRe = new RegExp("^\\s*(?:const|let|var)\\s+(.+?)" + ";" + "?\\s*$");
20708
20729
  for (let i = 0;i < lines.length; i++) {
20709
20730
  if (lineStartsInTemplate[i])
20710
20731
  continue;
20711
20732
  const line = lines[i];
20712
- const declRe = new RegExp("^\\s*(?:const|let|var)\\s+(.+?)" + ";" + "?\\s*$");
20713
20733
  const decl = line.match(declRe);
20714
20734
  if (!decl)
20715
20735
  continue;
@@ -22139,6 +22159,90 @@ function findTopLevelEquals(s) {
22139
22159
  }
22140
22160
  return -1;
22141
22161
  }
22162
+ function destructuringReassignsName(text, name) {
22163
+ const re = new RegExp(`\\b${name}\\b`);
22164
+ let i = 0;
22165
+ let inStr = null;
22166
+ let escaped = false;
22167
+ while (i < text.length) {
22168
+ const c = text[i];
22169
+ if (escaped) {
22170
+ escaped = false;
22171
+ i++;
22172
+ continue;
22173
+ }
22174
+ if (c === "\\" && inStr) {
22175
+ escaped = true;
22176
+ i++;
22177
+ continue;
22178
+ }
22179
+ if (inStr) {
22180
+ if (inStr === "single" && c === "'" || inStr === "double" && c === '"' || inStr === "template" && c === "`")
22181
+ inStr = null;
22182
+ i++;
22183
+ continue;
22184
+ }
22185
+ if (c === "'") {
22186
+ inStr = "single";
22187
+ i++;
22188
+ continue;
22189
+ }
22190
+ if (c === '"') {
22191
+ inStr = "double";
22192
+ i++;
22193
+ continue;
22194
+ }
22195
+ if (c === "`") {
22196
+ inStr = "template";
22197
+ i++;
22198
+ continue;
22199
+ }
22200
+ if (c === "[" || c === "{") {
22201
+ const open = c;
22202
+ const close = c === "[" ? "]" : "}";
22203
+ let depth = 1;
22204
+ let j = i + 1;
22205
+ while (j < text.length && depth > 0) {
22206
+ const cj = text[j];
22207
+ if (cj === "\\") {
22208
+ j += 2;
22209
+ continue;
22210
+ }
22211
+ if (cj === open)
22212
+ depth++;
22213
+ else if (cj === close)
22214
+ depth--;
22215
+ if (depth === 0)
22216
+ break;
22217
+ j++;
22218
+ }
22219
+ if (depth === 0 && j < text.length) {
22220
+ let k = j + 1;
22221
+ while (k < text.length && (text[k] === " " || text[k] === "\t" || text[k] === "\r" || text[k] === `
22222
+ `))
22223
+ k++;
22224
+ if (text[k] === "=" && text[k + 1] !== "=" && text[k + 1] !== ">") {
22225
+ const inside = text.slice(i + 1, j);
22226
+ if (re.test(inside)) {
22227
+ const keyOnly = new RegExp(`\\b${name}\\b\\s*:`);
22228
+ const valueAfterColon = new RegExp(`:\\s*\\b${name}\\b`);
22229
+ if (open === "{") {
22230
+ const isKeyOnly = keyOnly.test(inside) && !valueAfterColon.test(inside) && !new RegExp(`(?:^|[\\s,{])\\s*${name}\\s*(?:,|\\s*=|\\s*})`).test(inside);
22231
+ if (!isKeyOnly)
22232
+ return true;
22233
+ } else {
22234
+ return true;
22235
+ }
22236
+ }
22237
+ }
22238
+ }
22239
+ i = j + 1;
22240
+ continue;
22241
+ }
22242
+ i++;
22243
+ }
22244
+ return false;
22245
+ }
22142
22246
  function analyzeLetDecl(line, text) {
22143
22247
  const declRe = new RegExp("^\\s*(?:let|var)\\s+(.+?)" + ";" + "?\\s*$");
22144
22248
  const decl = line.match(declRe);
@@ -22176,7 +22280,8 @@ function analyzeLetDecl(line, text) {
22176
22280
  const assignPattern = `\\b${name}\\s*(?:${assignOps.map((op) => op.replace(/[|\\^$*+?.(){}[\]]/g, (r) => `\\${r}`)).join("|")})`;
22177
22281
  const directAssign = new RegExp(assignPattern).test(rest);
22178
22282
  const incDecChanged = new RegExp(`(?:^|[^$w])(?:\\+\\+|--)\\s*${name}\\b|\\b${name}\\s*(?:\\+\\+|--)`).test(rest);
22179
- result.push({ name, fixable: !directAssign && !incDecChanged });
22283
+ const destructReassigned = destructuringReassignsName(rest, name);
22284
+ result.push({ name, fixable: !directAssign && !incDecChanged && !destructReassigned });
22180
22285
  }
22181
22286
  return result;
22182
22287
  }
@@ -22205,8 +22310,20 @@ var init_prefer_const = __esm(() => {
22205
22310
  },
22206
22311
  fix: (text) => {
22207
22312
  const lines = text.split(/\r?\n/);
22313
+ const disabledLines = new Set;
22314
+ const disableNextRe = /(?:eslint|pickier)-disable-next-line\b([^*\n]*)/;
22315
+ for (let i = 0;i < lines.length; i++) {
22316
+ const m = lines[i].match(disableNextRe);
22317
+ if (!m)
22318
+ continue;
22319
+ const ruleList = m[1].trim();
22320
+ if (ruleList === "" || /\bprefer-const\b/.test(ruleList))
22321
+ disabledLines.add(i + 2);
22322
+ }
22208
22323
  let changed = false;
22209
22324
  for (let i = 0;i < lines.length; i++) {
22325
+ if (disabledLines.has(i + 1))
22326
+ continue;
22210
22327
  const line = lines[i];
22211
22328
  if (!/^\s*let\b/.test(line))
22212
22329
  continue;
@@ -28856,14 +28973,27 @@ function getVariantPriority(cls) {
28856
28973
  }
28857
28974
  return priority;
28858
28975
  }
28976
+ function getClassSortKey(cls) {
28977
+ const cached = sortKeyCache.get(cls);
28978
+ if (cached)
28979
+ return cached;
28980
+ const key = {
28981
+ group: getGroupIndex(cls),
28982
+ variant: getVariantPriority(cls)
28983
+ };
28984
+ sortKeyCache.set(cls, key);
28985
+ return key;
28986
+ }
28859
28987
  function sortClasses(classes) {
28860
28988
  return [...classes].sort((a, b) => {
28861
- const ga = getGroupIndex(a);
28862
- const gb = getGroupIndex(b);
28989
+ const aKey = getClassSortKey(a);
28990
+ const bKey = getClassSortKey(b);
28991
+ const ga = aKey.group;
28992
+ const gb = bKey.group;
28863
28993
  if (ga !== gb)
28864
28994
  return ga - gb;
28865
- const va = getVariantPriority(a);
28866
- const vb = getVariantPriority(b);
28995
+ const va = aKey.variant;
28996
+ const vb = bKey.variant;
28867
28997
  if (va !== vb)
28868
28998
  return va - vb;
28869
28999
  return a.localeCompare(b);
@@ -28882,6 +29012,8 @@ function looksLikeJsExpression(value) {
28882
29012
  }
28883
29013
  function extractClassValues(content) {
28884
29014
  const matches = [];
29015
+ if (!content.includes("class") && !content.includes("clsx") && !content.includes("cn(") && !content.includes("tw(") && !content.includes("cva(") && !content.includes("tv("))
29016
+ return matches;
28885
29017
  for (const re of [ATTR_RE, ATTR_TMPL_RE, UTIL_FN_RE]) {
28886
29018
  re.lastIndex = 0;
28887
29019
  let m;
@@ -28928,7 +29060,7 @@ function isSorted(classes) {
28928
29060
  const sorted = sortClasses(classes);
28929
29061
  return classes.every((c, i) => c === sorted[i]);
28930
29062
  }
28931
- var GROUP_ORDER, VARIANT_RE, ATTR_RE, ATTR_TMPL_RE, UTIL_FN_RE, sortTailwindClassesRule;
29063
+ var GROUP_ORDER, VARIANT_RE, sortKeyCache, ATTR_RE, ATTR_TMPL_RE, UTIL_FN_RE, sortTailwindClassesRule;
28932
29064
  var init_tailwind_classes = __esm(() => {
28933
29065
  GROUP_ORDER = [
28934
29066
  [/^(block|inline|inline-block|flex|inline-flex|grid|inline-grid|flow-root|contents|hidden|table|table-caption|table-cell|table-column|table-column-group|table-footer-group|table-header-group|table-row-group|table-row|list-item|subgrid)$/, 0],
@@ -28952,6 +29084,7 @@ var init_tailwind_classes = __esm(() => {
28952
29084
  [/^(sr-only|not-sr-only)$/, 15]
28953
29085
  ];
28954
29086
  VARIANT_RE = /^([a-z0-9][-a-z0-9]{0,50}:)+(?!\[)/;
29087
+ sortKeyCache = new Map;
28955
29088
  ATTR_RE = /\b(?:class|className|:class)\s*=\s*(?:"([^"]*?)"|'([^']*?)')/g;
28956
29089
  ATTR_TMPL_RE = /\b(?:class|className|:class)\s*=\s*\{`([^`]*?)`\}/g;
28957
29090
  UTIL_FN_RE = /\b(?:clsx|cn|tw|cva|tv)\s*\(\s*(?:"([^"]*?)"|'([^']*?)')/g;
@@ -37647,6 +37780,7 @@ async function runFormat(globs, options) {
37647
37780
  return !absBase.startsWith(process23.cwd());
37648
37781
  });
37649
37782
  const globIgnores = isGlobbingOutsideProject ? [...UNIVERSAL_IGNORES] : cfg.ignores;
37783
+ const ignoreMatcher = createIgnoreMatcher(globIgnores);
37650
37784
  let entries = [];
37651
37785
  const simpleDirPattern = patterns.length === 1 && /\*\*\/\*$/.test(patterns[0]);
37652
37786
  if (simpleDirPattern) {
@@ -37661,7 +37795,7 @@ async function runFormat(globs, options) {
37661
37795
  for (const it of items) {
37662
37796
  const full = join8(dir, it);
37663
37797
  const st = statSync4(full);
37664
- if (shouldIgnorePath(full, globIgnores))
37798
+ if (ignoreMatcher(full))
37665
37799
  continue;
37666
37800
  if (st.isDirectory())
37667
37801
  stack.push(full);
@@ -37687,7 +37821,7 @@ async function runFormat(globs, options) {
37687
37821
  }
37688
37822
  trace("globbed entries", entries.length);
37689
37823
  const files = entries.filter((f) => {
37690
- if (shouldIgnorePath(f, globIgnores))
37824
+ if (ignoreMatcher(f))
37691
37825
  return false;
37692
37826
  const idx = f.lastIndexOf(".");
37693
37827
  if (idx < 0)
@@ -37759,24 +37893,21 @@ function trace2(...args) {
37759
37893
  getLogger2().error("[pickier:trace]", args);
37760
37894
  }
37761
37895
  async function lintText(text, cfg, filePath = "untitled", signal) {
37762
- if (signal?.aborted) {
37896
+ if (signal?.aborted)
37763
37897
  throw new Error("AbortError");
37764
- }
37765
- const cfg2 = cfg;
37766
- cfg2._internalSkipPluginRulesInScan = true;
37767
- const issues = scanContent(filePath, text, cfg);
37898
+ const suppress = parseDisableDirectives(text);
37899
+ const commentLines = getCommentLines(text);
37900
+ const issues = scanContentOptimized(filePath, text, cfg, suppress, commentLines);
37768
37901
  if (signal?.aborted)
37769
37902
  throw new Error("AbortError");
37770
37903
  try {
37771
37904
  const pluginIssues = await applyPlugins(filePath, text, cfg);
37772
- const suppress = parseDisableDirectives(text);
37773
- const commentLines = getCommentLines(text);
37774
37905
  for (const i of pluginIssues) {
37775
37906
  if (signal?.aborted)
37776
37907
  throw new Error("AbortError");
37777
37908
  if (isSuppressed(i.ruleId, i.line, suppress))
37778
37909
  continue;
37779
- if (commentLines.has(i.line))
37910
+ if (commentLines.has(i.line) && shouldSkipCommentOnlyPluginIssue(i.ruleId))
37780
37911
  continue;
37781
37912
  issues.push({
37782
37913
  filePath: i.filePath,
@@ -37810,6 +37941,7 @@ async function runLintProgrammatic(globs, options, signal) {
37810
37941
  return !absBase.startsWith(process25.cwd());
37811
37942
  });
37812
37943
  const globIgnores = isGlobbingOutsideProject ? [...UNIVERSAL_IGNORES] : cfg.ignores;
37944
+ const ignoreMatcher = createIgnoreMatcher(globIgnores);
37813
37945
  let entries = [];
37814
37946
  const nonGlobSingle = patterns.length === 1 && !/[*?[\]{}()!]/.test(patterns[0]);
37815
37947
  if (nonGlobSingle) {
@@ -37835,7 +37967,7 @@ async function runLintProgrammatic(globs, options, signal) {
37835
37967
  for (const it of items) {
37836
37968
  const full = join11(dir, it);
37837
37969
  const st = statSync6(full);
37838
- if (shouldIgnorePath(full, globIgnores))
37970
+ if (ignoreMatcher(full))
37839
37971
  continue;
37840
37972
  if (st.isDirectory())
37841
37973
  stack.push(full);
@@ -37874,7 +38006,7 @@ async function runLintProgrammatic(globs, options, signal) {
37874
38006
  cntNodeModules++;
37875
38007
  continue;
37876
38008
  }
37877
- if (shouldIgnorePath(f, globIgnores)) {
38009
+ if (ignoreMatcher(f)) {
37878
38010
  cntIgnored++;
37879
38011
  continue;
37880
38012
  }
@@ -37887,15 +38019,12 @@ async function runLintProgrammatic(globs, options, signal) {
37887
38019
  }
37888
38020
  trace2("filter:programmatic", { total: cntTotal, included: cntIncluded, node_modules: cntNodeModules, ignored: cntIgnored, wrongExt: cntWrongExt });
37889
38021
  const concurrency = ENV.CONCURRENCY;
37890
- const limit = createLimiter(concurrency);
37891
38022
  const processFile = async (file) => {
37892
38023
  if (signal?.aborted)
37893
38024
  throw new Error("AbortError");
37894
38025
  const src = readFileSync10(file, "utf8");
37895
38026
  const suppress = parseDisableDirectives(src);
37896
38027
  const commentLines = getCommentLines(src);
37897
- const cfgAny = cfg;
37898
- cfgAny._internalSkipPluginRulesInScan = true;
37899
38028
  let issues = scanContentOptimized(file, src, cfg, suppress, commentLines);
37900
38029
  try {
37901
38030
  const pluginIssues = await applyPlugins(file, src, cfg);
@@ -37904,7 +38033,7 @@ async function runLintProgrammatic(globs, options, signal) {
37904
38033
  throw new Error("AbortError");
37905
38034
  if (isSuppressed(i.ruleId, i.line, suppress))
37906
38035
  continue;
37907
- if (commentLines.has(i.line))
38036
+ if (commentLines.has(i.line) && shouldSkipCommentOnlyPluginIssue(i.ruleId))
37908
38037
  continue;
37909
38038
  issues.push({
37910
38039
  filePath: i.filePath,
@@ -37954,7 +38083,7 @@ async function runLintProgrammatic(globs, options, signal) {
37954
38083
  }
37955
38084
  return issues;
37956
38085
  };
37957
- const issueArrays = await Promise.all(files.map((file) => limit(() => processFile(file))));
38086
+ const issueArrays = await processWithConcurrency(files, concurrency, processFile);
37958
38087
  const allIssues = issueArrays.flat();
37959
38088
  const errors = allIssues.filter((i) => i.severity === "error").length;
37960
38089
  const warnings = allIssues.filter((i) => i.severity === "warning").length;
@@ -37976,10 +38105,10 @@ function parseDisableDirectives(content) {
37976
38105
  for (let i = 0;i < lines.length; i++) {
37977
38106
  const t = lines[i].trim();
37978
38107
  const lineNo = i + 1;
37979
- const nextLineMatch = t.match(/^\/\/\s*(?:eslint|pickier)-disable-next-line\s+(\S.*)$/);
38108
+ const nextLineMatch = t.match(/^\/\/\s*(?:eslint|pickier)-disable-next-line(?:\s+(\S.*))?$/);
37980
38109
  if (nextLineMatch) {
37981
- const ruleText = nextLineMatch[1].replace(/\s+--\s.*$/, "");
37982
- const list = ruleText.split(",").map((s) => s.trim()).filter(Boolean);
38110
+ const ruleText = nextLineMatch[1]?.replace(/\s+--\s.*$/, "");
38111
+ const list = ruleText ? ruleText.split(",").map((s) => s.trim()).filter(Boolean) : ["*"];
37983
38112
  if (list.length > 0) {
37984
38113
  const target = lineNo + 1;
37985
38114
  const set = nextLine.get(target) || new Set;
@@ -38070,23 +38199,6 @@ function parseDisableDirectives(content) {
38070
38199
  const sortedEnableLines = Array.from(rangeEnable.keys()).sort((a, b) => a - b);
38071
38200
  return { nextLine, fileLevel, rangeDisable, rangeEnable, sortedDisableLines, sortedEnableLines };
38072
38201
  }
38073
- function binarySearchLargestLessThan(arr, target) {
38074
- if (arr.length === 0 || arr[0] >= target)
38075
- return -1;
38076
- let left = 0;
38077
- let right = arr.length - 1;
38078
- let result = -1;
38079
- while (left <= right) {
38080
- const mid = Math.floor((left + right) / 2);
38081
- if (arr[mid] < target) {
38082
- result = arr[mid];
38083
- left = mid + 1;
38084
- } else {
38085
- right = mid - 1;
38086
- }
38087
- }
38088
- return result;
38089
- }
38090
38202
  function isSuppressed(ruleId, line, directives) {
38091
38203
  if (directives instanceof Map) {
38092
38204
  const set = directives.get(line);
@@ -38101,14 +38213,28 @@ function isSuppressed(ruleId, line, directives) {
38101
38213
  const nextLineSet = directives.nextLine.get(line);
38102
38214
  if (nextLineSet && matchesRule(ruleId, nextLineSet))
38103
38215
  return true;
38104
- const lastDisableLine = binarySearchLargestLessThan(directives.sortedDisableLines, line);
38105
- const lastEnableLine = binarySearchLargestLessThan(directives.sortedEnableLines, line);
38106
- if (lastDisableLine !== -1 && lastDisableLine > lastEnableLine) {
38107
- const disabledRules = directives.rangeDisable.get(lastDisableLine);
38108
- if (matchesRule(ruleId, disabledRules))
38109
- return true;
38216
+ let disabled = false;
38217
+ let disableIndex = 0;
38218
+ let enableIndex = 0;
38219
+ while (true) {
38220
+ const nextDisable = disableIndex < directives.sortedDisableLines.length ? directives.sortedDisableLines[disableIndex] : Number.POSITIVE_INFINITY;
38221
+ const nextEnable = enableIndex < directives.sortedEnableLines.length ? directives.sortedEnableLines[enableIndex] : Number.POSITIVE_INFINITY;
38222
+ const nextDirectiveLine = Math.min(nextDisable, nextEnable);
38223
+ if (nextDirectiveLine >= line)
38224
+ break;
38225
+ if (nextDisable === nextDirectiveLine) {
38226
+ const disabledRules = directives.rangeDisable.get(nextDisable);
38227
+ if (matchesRule(ruleId, disabledRules))
38228
+ disabled = true;
38229
+ disableIndex++;
38230
+ continue;
38231
+ }
38232
+ const enabledRules = directives.rangeEnable.get(nextEnable);
38233
+ if (enabledRules.has("*") || matchesRule(ruleId, enabledRules))
38234
+ disabled = false;
38235
+ enableIndex++;
38110
38236
  }
38111
- return false;
38237
+ return disabled;
38112
38238
  }
38113
38239
  function matchesRule(ruleId, ruleSet) {
38114
38240
  if (ruleSet.size === 0)
@@ -38128,6 +38254,9 @@ function matchesRule(ruleId, ruleSet) {
38128
38254
  }
38129
38255
  return false;
38130
38256
  }
38257
+ function shouldSkipCommentOnlyPluginIssue(ruleId) {
38258
+ return ruleId !== "spaced-comment" && !ruleId.endsWith("/spaced-comment");
38259
+ }
38131
38260
  function ensureHelpText(issue, ruleId) {
38132
38261
  if (issue.help) {
38133
38262
  return issue;
@@ -38164,8 +38293,22 @@ async function withTimeout2(p, ms, label) {
38164
38293
  throw e;
38165
38294
  }
38166
38295
  }
38167
- async function applyPlugins(filePath, content, cfg) {
38168
- const issues = [];
38296
+ async function processWithConcurrency(items, concurrency, worker) {
38297
+ const results = new Array(items.length);
38298
+ if (items.length === 0)
38299
+ return results;
38300
+ let nextIndex = 0;
38301
+ const workerCount = Math.max(1, Math.min(Math.max(1, concurrency), items.length));
38302
+ async function runWorker() {
38303
+ while (nextIndex < items.length) {
38304
+ const index = nextIndex++;
38305
+ results[index] = await worker(items[index], index);
38306
+ }
38307
+ }
38308
+ await Promise.all(Array.from({ length: workerCount }, runWorker));
38309
+ return results;
38310
+ }
38311
+ function getPluginDefinitions(cfg) {
38169
38312
  let pluginDefs = getAllPlugins();
38170
38313
  if (cfg.plugins && cfg.plugins.length > 0) {
38171
38314
  const coreNames = new Set(["pickier", "style", "regexp", "ts"]);
@@ -38175,6 +38318,9 @@ async function applyPlugins(filePath, content, cfg) {
38175
38318
  for (const p of userPlugins)
38176
38319
  pluginDefs.push(p);
38177
38320
  }
38321
+ return pluginDefs;
38322
+ }
38323
+ function getRulesConfig(cfg, pluginDefs) {
38178
38324
  const rulesConfig = { ...cfg.pluginRules || {} };
38179
38325
  if (cfg.rules?.noUnusedCapturingGroup)
38180
38326
  rulesConfig["regexp/no-unused-capturing-group"] = cfg.rules.noUnusedCapturingGroup;
@@ -38184,115 +38330,168 @@ async function applyPlugins(filePath, content, cfg) {
38184
38330
  "antfu/no-top-level-await": "ts/no-top-level-await"
38185
38331
  };
38186
38332
  for (const [alias, target] of Object.entries(ruleAliases)) {
38187
- if (rulesConfig[alias]) {
38333
+ if (rulesConfig[alias])
38188
38334
  rulesConfig[target] = rulesConfig[alias];
38189
- }
38190
38335
  }
38191
38336
  for (const key of Object.keys(cfg.pluginRules || {})) {
38192
38337
  if (!key.includes("/")) {
38193
38338
  for (const p of pluginDefs) {
38194
- if (p.rules && Object.prototype.hasOwnProperty.call(p.rules, key)) {
38339
+ if (p.rules && Object.prototype.hasOwnProperty.call(p.rules, key))
38195
38340
  rulesConfig[`${p.name}/${key}`] = cfg.pluginRules[key];
38196
- }
38197
38341
  }
38198
38342
  }
38199
38343
  }
38200
- const baseCtx = { filePath, config: cfg };
38344
+ return rulesConfig;
38345
+ }
38346
+ function getPluginPlan(cfg) {
38347
+ const cached = pluginPlanCache.get(cfg);
38348
+ if (cached)
38349
+ return cached;
38350
+ const pluginDefs = getPluginDefinitions(cfg);
38351
+ const rulesConfig = getRulesConfig(cfg, pluginDefs);
38352
+ const checkRules = [];
38353
+ const fixRules = [];
38354
+ const fixableRuleIds = new Set(["no-debugger"]);
38355
+ const fixableBareRuleNames = new Set(["no-debugger"]);
38201
38356
  const executedRules = new Set;
38202
38357
  for (const plugin of pluginDefs) {
38203
- const r = plugin.rules;
38204
- for (const ruleName in r) {
38358
+ for (const ruleName in plugin.rules) {
38205
38359
  const fullRuleId = `${plugin.name}/${ruleName}`;
38206
38360
  const setting = getRuleSetting(rulesConfig, fullRuleId);
38207
38361
  if (!setting.enabled)
38208
38362
  continue;
38209
- if (executedRules.has(ruleName)) {
38363
+ if (executedRules.has(ruleName))
38210
38364
  continue;
38211
- }
38212
38365
  executedRules.add(ruleName);
38213
- const rule = r[ruleName];
38214
- const overrideSeverity = setting.severity;
38215
- if (!rule || typeof rule.check !== "function") {
38216
- if (overrideSeverity === "error") {
38217
- issues.push({
38218
- filePath,
38219
- line: 1,
38220
- column: 1,
38221
- ruleId: `${fullRuleId}-internal`,
38222
- message: "Rule missing implementation (check function is undefined)",
38223
- severity: "error"
38224
- });
38225
- }
38226
- continue;
38366
+ const rule = plugin.rules[ruleName];
38367
+ const planned = {
38368
+ pluginName: plugin.name,
38369
+ ruleName,
38370
+ fullRuleId,
38371
+ rule,
38372
+ severity: setting.severity,
38373
+ options: setting.options
38374
+ };
38375
+ if (typeof rule?.check === "function")
38376
+ checkRules.push(planned);
38377
+ else if (setting.severity === "error") {
38378
+ checkRules.push(planned);
38379
+ }
38380
+ if (typeof rule?.fix === "function") {
38381
+ fixRules.push(planned);
38382
+ fixableRuleIds.add(fullRuleId);
38383
+ fixableBareRuleNames.add(ruleName);
38384
+ }
38385
+ }
38386
+ }
38387
+ const plan = { checkRules, fixRules, fixableRuleIds, fixableBareRuleNames };
38388
+ pluginPlanCache.set(cfg, plan);
38389
+ return plan;
38390
+ }
38391
+ function isShellPath(filePath, content) {
38392
+ return /\.(?:sh|bash|zsh|ksh|dash)$/.test(filePath) || /^#!\s*(?:\/usr\/bin\/env\s+)?(?:ba|z|k|da)?sh\b/.test(content);
38393
+ }
38394
+ function isLockfilePath(filePath) {
38395
+ return /(?:^|[/\\])(?:bun\.lock|bun\.lockb|package-lock\.json|pnpm-lock\.yaml|yarn\.lock|npm-shrinkwrap\.json)$/.test(filePath);
38396
+ }
38397
+ function shouldRunPlannedRule(rule, filePath, content) {
38398
+ switch (rule.pluginName) {
38399
+ case "markdown":
38400
+ return filePath.endsWith(".md");
38401
+ case "shell":
38402
+ return isShellPath(filePath, content);
38403
+ case "publint":
38404
+ return /(?:^|[/\\])package\.json$/.test(filePath);
38405
+ case "lockfile":
38406
+ return isLockfilePath(filePath);
38407
+ case "node":
38408
+ case "ts":
38409
+ case "general":
38410
+ case "quality":
38411
+ case "eslint":
38412
+ case "regexp":
38413
+ case "unused-imports":
38414
+ case "perfectionist":
38415
+ return /\.(?:ts|js|tsx|jsx|mts|mjs|cts|cjs)$/.test(filePath);
38416
+ default:
38417
+ return true;
38418
+ }
38419
+ }
38420
+ async function applyPlugins(filePath, content, cfg) {
38421
+ const issues = [];
38422
+ const plan = getPluginPlan(cfg);
38423
+ const baseCtx = { filePath, config: cfg };
38424
+ for (const planned of plan.checkRules) {
38425
+ if (!shouldRunPlannedRule(planned, filePath, content))
38426
+ continue;
38427
+ const { fullRuleId, rule, severity: overrideSeverity } = planned;
38428
+ if (!rule || typeof rule.check !== "function") {
38429
+ if (overrideSeverity === "error") {
38430
+ issues.push({
38431
+ filePath,
38432
+ line: 1,
38433
+ column: 1,
38434
+ ruleId: `${fullRuleId}-internal`,
38435
+ message: "Rule missing implementation (check function is undefined)",
38436
+ severity: "error"
38437
+ });
38227
38438
  }
38228
- try {
38229
- trace2("rule:start", fullRuleId);
38230
- const ruleTimeoutMs = ENV.RULE_TIMEOUT_MS;
38231
- const ctx = { ...baseCtx, options: setting.options };
38232
- const out = await withTimeout2(Promise.resolve().then(() => rule.check(content, ctx)), ruleTimeoutMs, `rule:${fullRuleId}`);
38233
- trace2("rule:end", fullRuleId, Array.isArray(out) ? out.length : 0);
38234
- for (const i of out) {
38235
- const issueWithHelp = ensureHelpText(i, fullRuleId);
38236
- if (overrideSeverity)
38237
- issues.push({ ...issueWithHelp, severity: overrideSeverity });
38238
- else
38239
- issues.push(issueWithHelp);
38240
- }
38241
- } catch (e) {
38439
+ continue;
38440
+ }
38441
+ try {
38442
+ trace2("rule:start", fullRuleId);
38443
+ const ruleTimeoutMs = ENV.RULE_TIMEOUT_MS;
38444
+ const ctx = { ...baseCtx, options: planned.options };
38445
+ const started = performance.now();
38446
+ const out = await withTimeout2(Promise.resolve().then(() => rule.check(content, ctx)), ruleTimeoutMs, `rule:${fullRuleId}`);
38447
+ const elapsed = performance.now() - started;
38448
+ if (elapsed > ruleTimeoutMs) {
38242
38449
  issues.push({
38243
38450
  filePath,
38244
38451
  line: 1,
38245
38452
  column: 1,
38246
38453
  ruleId: `${fullRuleId}-internal`,
38247
- message: `Rule threw: ${e?.message || e}`,
38454
+ message: `Rule exceeded timeout budget: ${Math.round(elapsed)}ms > ${ruleTimeoutMs}ms`,
38248
38455
  severity: "error"
38249
38456
  });
38250
38457
  }
38458
+ trace2("rule:end", fullRuleId, Array.isArray(out) ? out.length : 0);
38459
+ for (const i of out) {
38460
+ const issueWithHelp = ensureHelpText(i, fullRuleId);
38461
+ if (overrideSeverity)
38462
+ issues.push({ ...issueWithHelp, severity: overrideSeverity });
38463
+ else
38464
+ issues.push(issueWithHelp);
38465
+ }
38466
+ } catch (e) {
38467
+ issues.push({
38468
+ filePath,
38469
+ line: 1,
38470
+ column: 1,
38471
+ ruleId: `${fullRuleId}-internal`,
38472
+ message: `Rule threw: ${e?.message || e}`,
38473
+ severity: "error"
38474
+ });
38251
38475
  }
38252
38476
  }
38253
38477
  return issues;
38254
38478
  }
38255
38479
  function applyPluginFixes(filePath, content, cfg) {
38256
- const pluginDefs = getAllPlugins();
38257
- if (cfg.plugins && cfg.plugins.length > 0) {
38258
- for (const p of cfg.plugins) {
38259
- if (typeof p === "string")
38260
- continue;
38261
- pluginDefs.push(p);
38262
- }
38263
- }
38264
- const rulesConfig = { ...cfg.pluginRules || {} };
38265
- if (cfg.rules?.noUnusedCapturingGroup)
38266
- rulesConfig["regexp/no-unused-capturing-group"] = cfg.rules.noUnusedCapturingGroup;
38267
- for (const key of Object.keys(cfg.pluginRules || {})) {
38268
- if (!key.includes("/")) {
38269
- for (const p of pluginDefs) {
38270
- if (p.rules && Object.prototype.hasOwnProperty.call(p.rules, key))
38271
- rulesConfig[`${p.name}/${key}`] = cfg.pluginRules[key];
38272
- }
38273
- }
38274
- }
38480
+ const plan = getPluginPlan(cfg);
38275
38481
  const baseCtx = { filePath, config: cfg };
38276
38482
  let out = content;
38277
38483
  let changed = true;
38278
38484
  let passes = 0;
38279
38485
  while (changed && passes++ < MAX_FIXER_PASSES) {
38280
38486
  changed = false;
38281
- for (const plugin of pluginDefs) {
38282
- for (const ruleName in plugin.rules) {
38283
- const fullRuleId = `${plugin.name}/${ruleName}`;
38284
- const setting = getRuleSetting(rulesConfig, fullRuleId);
38285
- if (!setting.enabled)
38286
- continue;
38287
- const rule = plugin.rules[ruleName];
38288
- if (typeof rule.fix !== "function")
38289
- continue;
38290
- const ctx = { ...baseCtx, options: setting.options };
38291
- const next = rule.fix(out, ctx);
38292
- if (next !== out) {
38293
- out = next;
38294
- changed = true;
38295
- }
38487
+ for (const planned of plan.fixRules) {
38488
+ if (!shouldRunPlannedRule(planned, filePath, out))
38489
+ continue;
38490
+ const ctx = { ...baseCtx, options: planned.options };
38491
+ const next = planned.rule.fix(out, ctx);
38492
+ if (next !== out) {
38493
+ out = next;
38494
+ changed = true;
38296
38495
  }
38297
38496
  }
38298
38497
  }
@@ -38461,6 +38660,7 @@ function getCommentLines(content) {
38461
38660
  let state = "code";
38462
38661
  let lineNo = 1;
38463
38662
  let lineHasCode = false;
38663
+ let lineSawComment = false;
38464
38664
  let lineStartedInBlockComment = false;
38465
38665
  for (let i = 0;i < content.length; i++) {
38466
38666
  const ch = content[i];
@@ -38470,11 +38670,14 @@ function getCommentLines(content) {
38470
38670
  `) {
38471
38671
  if (lineStartedInBlockComment && state === "block-comment") {
38472
38672
  commentLines.add(lineNo);
38473
- } else if (!lineHasCode && state !== "block-comment") {} else if (!lineHasCode) {
38673
+ } else if (!lineHasCode && lineSawComment && state !== "block-comment") {
38674
+ commentLines.add(lineNo);
38675
+ } else if (!lineHasCode && state === "block-comment") {
38474
38676
  commentLines.add(lineNo);
38475
38677
  }
38476
38678
  lineNo++;
38477
38679
  lineHasCode = false;
38680
+ lineSawComment = false;
38478
38681
  lineStartedInBlockComment = state === "block-comment";
38479
38682
  if (state === "line-comment") {
38480
38683
  state = "code";
@@ -38485,6 +38688,7 @@ function getCommentLines(content) {
38485
38688
  case "code":
38486
38689
  if (ch === "/" && next === "/") {
38487
38690
  state = "line-comment";
38691
+ lineSawComment = true;
38488
38692
  i++;
38489
38693
  } else if (ch === "/" && next === "*") {
38490
38694
  let isRegex = false;
@@ -38526,6 +38730,7 @@ function getCommentLines(content) {
38526
38730
  }
38527
38731
  i--;
38528
38732
  } else {
38733
+ lineSawComment = true;
38529
38734
  state = "block-comment";
38530
38735
  i++;
38531
38736
  }
@@ -38620,6 +38825,8 @@ function getCommentLines(content) {
38620
38825
  }
38621
38826
  if (lineStartedInBlockComment && state === "block-comment") {
38622
38827
  commentLines.add(lineNo);
38828
+ } else if (!lineHasCode && lineSawComment && state !== "block-comment") {
38829
+ commentLines.add(lineNo);
38623
38830
  } else if (!lineHasCode && state === "block-comment") {
38624
38831
  commentLines.add(lineNo);
38625
38832
  }
@@ -38636,6 +38843,10 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
38636
38843
  const quotesDisabled = quotesRuleSetting === "off";
38637
38844
  const skipQuotesCheck = quotesDisabled || fileExt === "json" || fileExt === "jsonc" || fileExt === "lock" || isMd || fileExt === "yaml" || fileExt === "yml" || fileExt === "stx" || fileExt === "html" || fileExt === "htm" || fileExt === "vue" || isShell || filePath.endsWith("bun.lock");
38638
38845
  const skipCodeRules = isMd || fileExt === "yaml" || fileExt === "yml" || fileExt === "json" || fileExt === "jsonc" || isShell;
38846
+ const indentRuleSetting = cfg.rules?.indent ?? cfg.pluginRules?.indent ?? cfg.pluginRules?.["style/indent"];
38847
+ const indentDisabled = indentRuleSetting === "off";
38848
+ const isTemplate = fileExt === "stx" || fileExt === "html" || fileExt === "htm" || fileExt === "vue";
38849
+ const shouldCheckIndent = !indentDisabled && !isMd && !isTemplate;
38639
38850
  let quotesReported = false;
38640
38851
  const sevMap = (s) => s === "warn" ? "warning" : s === "error" ? "error" : undefined;
38641
38852
  const wantDebugger = sevMap(cfg.rules.noDebugger);
@@ -38657,33 +38868,34 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
38657
38868
  }
38658
38869
  }
38659
38870
  const linesInTemplate = new Set;
38660
- let inTemplate = false;
38661
- let escaped = false;
38662
- let currentLine = 1;
38663
- for (let i = 0;i < content.length; i++) {
38664
- const ch = content[i];
38665
- if (escaped) {
38666
- escaped = false;
38667
- if (ch === `
38871
+ if (content.includes("`")) {
38872
+ let inTemplate = false;
38873
+ let escaped = false;
38874
+ let currentLine = 1;
38875
+ for (let i = 0;i < content.length; i++) {
38876
+ const ch = content[i];
38877
+ if (escaped) {
38878
+ escaped = false;
38879
+ if (ch === `
38668
38880
  `)
38669
- currentLine++;
38670
- continue;
38671
- }
38672
- if (ch === "\\") {
38673
- escaped = true;
38674
- continue;
38675
- }
38676
- if (ch === `
38881
+ currentLine++;
38882
+ continue;
38883
+ }
38884
+ if (ch === "\\") {
38885
+ escaped = true;
38886
+ continue;
38887
+ }
38888
+ if (ch === `
38677
38889
  `) {
38678
- currentLine++;
38679
- continue;
38680
- }
38681
- if (ch === "`") {
38682
- inTemplate = !inTemplate;
38683
- continue;
38684
- }
38685
- if (inTemplate) {
38686
- linesInTemplate.add(currentLine);
38890
+ currentLine++;
38891
+ continue;
38892
+ }
38893
+ if (ch === "`") {
38894
+ inTemplate = !inTemplate;
38895
+ continue;
38896
+ }
38897
+ if (inTemplate)
38898
+ linesInTemplate.add(currentLine);
38687
38899
  }
38688
38900
  }
38689
38901
  for (let i = 0;i < lines.length; i++) {
@@ -38702,12 +38914,11 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
38702
38914
  quotesReported = true;
38703
38915
  }
38704
38916
  }
38705
- const indentRuleSetting = cfg.rules?.indent ?? cfg.pluginRules?.indent ?? cfg.pluginRules?.["style/indent"];
38706
- const indentDisabled = indentRuleSetting === "off";
38707
- const isTemplate = fileExt === "stx" || fileExt === "html" || fileExt === "htm" || fileExt === "vue";
38708
- const leadingMatch = line.match(/^[ \t]*/);
38709
- const leading = leadingMatch ? leadingMatch[0] : "";
38710
- if (!indentDisabled && !isMd && !isTemplate && leading.length > 0 && !linesInFencedCodeBlock.has(lineNo) && hasIndentIssue(leading, cfg.format.indent, cfg.format.indentStyle, line)) {
38917
+ let wsEnd = 0;
38918
+ while (wsEnd < line.length && (line.charCodeAt(wsEnd) === 32 || line.charCodeAt(wsEnd) === 9))
38919
+ wsEnd++;
38920
+ const leading = wsEnd > 0 ? line.slice(0, wsEnd) : "";
38921
+ if (shouldCheckIndent && leading.length > 0 && !linesInFencedCodeBlock.has(lineNo) && hasIndentIssue(leading, cfg.format.indent, cfg.format.indentStyle, line)) {
38711
38922
  if (!isSuppressed("indent", lineNo, suppress))
38712
38923
  issues.push({ filePath, line: lineNo, column: 1, ruleId: "indent", message: "Incorrect indentation detected", severity: "warning", help: `Use ${cfg.format.indentStyle === "spaces" ? `${cfg.format.indent} spaces` : "tabs"} for indentation. Configure with format.indent and format.indentStyle in your config` });
38713
38924
  }
@@ -38812,24 +39023,43 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
38812
39023
  }
38813
39024
  if (!skipCodeRules && wantNoCondAssign && !linesInTemplate.has(lineNo)) {
38814
39025
  const strippedLine = stripComments2(stripRegexLiterals(line));
38815
- const checkCond = (cond) => /[^=!<>]=(?![=>])/.test(cond.replace(/'[^']*'|"[^"]*"/g, '""'));
38816
- const m1 = strippedLine.match(/\b(?:if|while)\s*\(([^)]*)\)/);
39026
+ const conditionLine = strippedLine.replace(/'[^']*'|"[^"]*"/g, '""');
39027
+ const checkCond = (cond) => /[^=!<>]=(?![=>])/.test(cond);
39028
+ const m1 = conditionLine.match(/\b(?:if|while)\s*\(([^)]*)\)/);
38817
39029
  if (m1) {
38818
39030
  const cond = m1[1];
38819
39031
  if (checkCond(cond)) {
38820
- if (!isSuppressed("no-cond-assign", lineNo, suppress))
38821
- issues.push({ filePath, line: lineNo, column: Math.max(1, line.indexOf("(") + 1), ruleId: "no-cond-assign", message: "Unexpected assignment within a conditional expression", severity: wantNoCondAssign, help: "Use === or == for comparison instead of = (assignment). If assignment was intentional, wrap it in parentheses: if ((x = value))" });
39032
+ if (!isSuppressed("no-cond-assign", lineNo, suppress)) {
39033
+ issues.push({
39034
+ filePath,
39035
+ line: lineNo,
39036
+ column: Math.max(1, line.indexOf("(") + 1),
39037
+ ruleId: "no-cond-assign",
39038
+ message: "Unexpected assignment within a conditional expression",
39039
+ severity: wantNoCondAssign,
39040
+ help: "Use === or == for comparison instead of = (assignment). Wrap intentional assignments in extra parentheses."
39041
+ });
39042
+ }
38822
39043
  }
38823
39044
  }
38824
- const mFor = strippedLine.match(/\bfor\s*\(([^)]*)\)/);
39045
+ const mFor = conditionLine.match(/\bfor\s*\(([^)]*)\)/);
38825
39046
  if (mFor) {
38826
39047
  const inside = mFor[1];
38827
39048
  const parts = inside.split(";");
38828
39049
  if (parts.length >= 2) {
38829
39050
  const cond = parts[1];
38830
39051
  if (checkCond(cond)) {
38831
- if (!isSuppressed("no-cond-assign", lineNo, suppress))
38832
- issues.push({ filePath, line: lineNo, column: Math.max(1, line.indexOf("(") + 1), ruleId: "no-cond-assign", message: "Unexpected assignment within a conditional expression", severity: wantNoCondAssign, help: "Use === or == for comparison instead of = (assignment). If assignment was intentional, wrap it in parentheses: for (let i = 0; (x = arr[i]); i++)" });
39052
+ if (!isSuppressed("no-cond-assign", lineNo, suppress)) {
39053
+ issues.push({
39054
+ filePath,
39055
+ line: lineNo,
39056
+ column: Math.max(1, line.indexOf("(") + 1),
39057
+ ruleId: "no-cond-assign",
39058
+ message: "Unexpected assignment within a conditional expression",
39059
+ severity: wantNoCondAssign,
39060
+ help: "Use === or == for comparison instead of = (assignment). Wrap intentional assignments in extra parentheses."
39061
+ });
39062
+ }
38833
39063
  }
38834
39064
  }
38835
39065
  }
@@ -38841,67 +39071,25 @@ function scanContent(filePath, content, cfg) {
38841
39071
  const suppress = parseDisableDirectives(content);
38842
39072
  const commentLines = getCommentLines(content);
38843
39073
  const issues = scanContentOptimized(filePath, content, cfg, suppress, commentLines);
38844
- if (!cfg._internalSkipPluginRulesInScan) {
38845
- try {
38846
- let pluginDefs = getAllPlugins();
38847
- if (cfg.plugins && cfg.plugins.length > 0) {
38848
- const coreNames = new Set(["pickier", "style", "regexp", "ts"]);
38849
- const userPlugins = cfg.plugins.filter((p) => typeof p !== "string");
38850
- const hasCoreMatch = userPlugins.some((p) => coreNames.has(p.name));
38851
- pluginDefs = hasCoreMatch ? pluginDefs : [];
38852
- for (const p of userPlugins)
38853
- pluginDefs.push(p);
38854
- }
38855
- const rulesConfig = { ...cfg.pluginRules || {} };
38856
- if (cfg.rules?.noUnusedCapturingGroup)
38857
- rulesConfig["regexp/no-unused-capturing-group"] = cfg.rules.noUnusedCapturingGroup;
38858
- for (const key of Object.keys(cfg.pluginRules || {})) {
38859
- if (!key.includes("/")) {
38860
- for (const p of pluginDefs) {
38861
- if (p.rules && Object.prototype.hasOwnProperty.call(p.rules, key))
38862
- rulesConfig[`${p.name}/${key}`] = cfg.pluginRules[key];
38863
- }
38864
- }
38865
- }
38866
- const isRuleEnabled = (ruleId) => {
38867
- const raw = rulesConfig[ruleId];
38868
- const setting = typeof raw === "string" ? raw : undefined;
38869
- return setting === "error" || setting === "warn" || setting === "warning";
38870
- };
38871
- const getRuleSeverity = (ruleId) => {
38872
- const raw = rulesConfig[ruleId];
38873
- const setting = typeof raw === "string" ? raw : undefined;
38874
- if (setting === "error")
38875
- return "error";
38876
- if (setting === "warn" || setting === "warning")
38877
- return "warning";
38878
- return;
38879
- };
38880
- const ctx = { filePath, config: cfg };
38881
- const executedRulesInScan = new Set;
38882
- for (const plugin of pluginDefs) {
38883
- const r = plugin.rules;
38884
- for (const ruleName in r) {
38885
- const fullRuleId = `${plugin.name}/${ruleName}`;
38886
- if (!isRuleEnabled(fullRuleId))
38887
- continue;
38888
- if (executedRulesInScan.has(ruleName))
38889
- continue;
38890
- executedRulesInScan.add(ruleName);
38891
- const rule = r[ruleName];
38892
- if (!rule || typeof rule.check !== "function")
38893
- continue;
38894
- const out = rule.check(content, ctx);
38895
- for (const i of out) {
38896
- if (!isSuppressed(i.ruleId, i.line, suppress)) {
38897
- const sev = getRuleSeverity(fullRuleId);
38898
- issues.push(sev ? { ...i, severity: sev } : i);
38899
- }
38900
- }
38901
- }
39074
+ try {
39075
+ const plan = getPluginPlan(cfg);
39076
+ const ctx = { filePath, config: cfg };
39077
+ for (const planned of plan.checkRules) {
39078
+ if (!shouldRunPlannedRule(planned, filePath, content))
39079
+ continue;
39080
+ if (!planned.rule || typeof planned.rule.check !== "function")
39081
+ continue;
39082
+ const out = planned.rule.check(content, { ...ctx, options: planned.options });
39083
+ for (const i of out) {
39084
+ if (isSuppressed(i.ruleId, i.line, suppress))
39085
+ continue;
39086
+ if (commentLines.has(i.line) && shouldSkipCommentOnlyPluginIssue(i.ruleId))
39087
+ continue;
39088
+ const issueWithHelp = ensureHelpText(i, planned.fullRuleId);
39089
+ issues.push(planned.severity ? { ...issueWithHelp, severity: planned.severity } : issueWithHelp);
38902
39090
  }
38903
- } catch {}
38904
- }
39091
+ }
39092
+ } catch {}
38905
39093
  return issues;
38906
39094
  }
38907
39095
  async function runLint(globs, options) {
@@ -38935,6 +39123,7 @@ async function runLint(globs, options) {
38935
39123
  return !absBase.startsWith(process25.cwd());
38936
39124
  });
38937
39125
  const globIgnores = isGlobbingOutsideProject ? [...UNIVERSAL_IGNORES] : cfg.ignores;
39126
+ const ignoreMatcher = createIgnoreMatcher(globIgnores);
38938
39127
  if (enableDiagnostics) {
38939
39128
  getLogger2().info(`[pickier:diagnostics] Globbing outside project: ${isGlobbingOutsideProject}, ignore patterns: ${globIgnores.length}`);
38940
39129
  if (isGlobbingOutsideProject)
@@ -38971,7 +39160,7 @@ async function runLint(globs, options) {
38971
39160
  for (const it of items) {
38972
39161
  const full = join11(dir, it);
38973
39162
  const st = statSync6(full);
38974
- if (shouldIgnorePath(full, globIgnores))
39163
+ if (ignoreMatcher(full))
38975
39164
  continue;
38976
39165
  if (st.isDirectory())
38977
39166
  stack.push(full);
@@ -39033,7 +39222,7 @@ async function runLint(globs, options) {
39033
39222
  cntNodeModules++;
39034
39223
  continue;
39035
39224
  }
39036
- if (shouldIgnorePath(f, globIgnores)) {
39225
+ if (ignoreMatcher(f)) {
39037
39226
  cntIgnored++;
39038
39227
  continue;
39039
39228
  }
@@ -39064,7 +39253,6 @@ async function runLint(globs, options) {
39064
39253
  }
39065
39254
  }
39066
39255
  const concurrency = ENV.CONCURRENCY;
39067
- const limit = createLimiter(concurrency);
39068
39256
  if (enableDiagnostics)
39069
39257
  getLogger2().info(`[pickier:diagnostics] Starting to process ${files.length} files with concurrency ${concurrency}...`);
39070
39258
  let processedCount = 0;
@@ -39078,23 +39266,33 @@ async function runLint(globs, options) {
39078
39266
  const src = readFileSync10(file, "utf8");
39079
39267
  if (formatOnly) {
39080
39268
  const fixed = formatCode(src, cfg, file);
39081
- if (fixed !== src && !options.dryRun) {
39082
- writeFileSync11(file, fixed, "utf8");
39269
+ if (fixed !== src) {
39270
+ if (!options.dryRun) {
39271
+ writeFileSync11(file, fixed, "utf8");
39272
+ } else {
39273
+ return [{
39274
+ filePath: file,
39275
+ line: 1,
39276
+ column: 1,
39277
+ ruleId: "format",
39278
+ message: "File is not formatted",
39279
+ severity: "error",
39280
+ help: "Run pickier format with --write to apply formatting."
39281
+ }];
39282
+ }
39083
39283
  }
39084
39284
  return [];
39085
39285
  }
39086
39286
  const suppress = parseDisableDirectives(src);
39087
39287
  const isCodeFileForComments = /\.(?:ts|js|tsx|jsx|mts|mjs|cts|cjs)$/.test(file);
39088
39288
  const commentLines = isCodeFileForComments ? getCommentLines(src) : new Set;
39089
- const cfgAny = cfg;
39090
- cfgAny._internalSkipPluginRulesInScan = true;
39091
39289
  let issues = scanContentOptimized(file, src, cfg, suppress, commentLines);
39092
39290
  try {
39093
39291
  const pluginIssues = await applyPlugins(file, src, cfg);
39094
39292
  for (const i of pluginIssues) {
39095
39293
  if (isSuppressed(i.ruleId, i.line, suppress))
39096
39294
  continue;
39097
- if (commentLines.has(i.line))
39295
+ if (commentLines.has(i.line) && shouldSkipCommentOnlyPluginIssue(i.ruleId))
39098
39296
  continue;
39099
39297
  issues.push({
39100
39298
  filePath: i.filePath,
@@ -39153,7 +39351,7 @@ async function runLint(globs, options) {
39153
39351
  trace2("scan done", relative7(process25.cwd(), file), issues.length);
39154
39352
  return issues;
39155
39353
  };
39156
- const issueArrays = await Promise.all(files.map((file) => limit(() => processFile(file))));
39354
+ const issueArrays = await processWithConcurrency(files, concurrency, processFile);
39157
39355
  const allIssues = issueArrays.flat();
39158
39356
  if (enableDiagnostics)
39159
39357
  getLogger2().info(`[pickier:diagnostics] Processing complete! Found ${allIssues.length} issues total`);
@@ -39178,22 +39376,12 @@ async function runLint(globs, options) {
39178
39376
  const problemsText = total === 1 ? "problem" : "problems";
39179
39377
  const errorsText = errors === 1 ? "error" : "errors";
39180
39378
  const warningsText = warnings === 1 ? "warning" : "warnings";
39181
- const pluginDefs = getAllPlugins();
39182
- const fixableRuleIds = new Set;
39183
- fixableRuleIds.add("no-debugger");
39184
- for (const plugin of pluginDefs) {
39185
- for (const ruleName in plugin.rules) {
39186
- const rule = plugin.rules[ruleName];
39187
- if (rule && typeof rule.fix === "function") {
39188
- fixableRuleIds.add(`${plugin.name}/${ruleName}`);
39189
- }
39190
- }
39191
- }
39379
+ const pluginPlan = getPluginPlan(cfg);
39192
39380
  let fixableErrors = 0;
39193
39381
  let fixableWarnings = 0;
39194
39382
  for (const issue of allIssues) {
39195
39383
  const ruleId = issue.ruleId;
39196
- const isFixable = fixableRuleIds.has(ruleId) || Array.from(fixableRuleIds).some((id) => id.endsWith(`/${ruleId}`));
39384
+ const isFixable = pluginPlan.fixableRuleIds.has(ruleId) || pluginPlan.fixableBareRuleNames.has(ruleId);
39197
39385
  if (isFixable) {
39198
39386
  if (issue.severity === "error")
39199
39387
  fixableErrors++;
@@ -39234,13 +39422,14 @@ async function runLint(globs, options) {
39234
39422
  return 1;
39235
39423
  }
39236
39424
  }
39237
- var _logger2 = null;
39425
+ var _logger2 = null, pluginPlanCache;
39238
39426
  var init_linter = __esm(() => {
39239
39427
  init_src();
39240
39428
  init_format();
39241
39429
  init_formatter();
39242
39430
  init_plugins();
39243
39431
  init_utils();
39432
+ pluginPlanCache = new WeakMap;
39244
39433
  });
39245
39434
 
39246
39435
  // src/run.ts
@@ -39262,6 +39451,8 @@ async function runUnified(globs, options) {
39262
39451
  const cfg = await loadConfigFromPath(options.config);
39263
39452
  const src = readFileSync11(filePath, "utf8");
39264
39453
  const fmt = formatCode(src, cfg, filePath);
39454
+ if (options.check && fmt !== src)
39455
+ return 1;
39265
39456
  if (options.write && fmt !== src) {
39266
39457
  writeFileSync12(filePath, fmt, "utf8");
39267
39458
  }
@@ -39919,7 +40110,13 @@ class Command {
39919
40110
  return this;
39920
40111
  }
39921
40112
  isMatched(name) {
39922
- return this.name === name || this.aliasNames.includes(name);
40113
+ if (this.aliasNames.includes(name))
40114
+ return true;
40115
+ if (this.name === name)
40116
+ return true;
40117
+ if (this.namespace && `${this.namespace}:${this.name}` === name)
40118
+ return true;
40119
+ return false;
39923
40120
  }
39924
40121
  get isDefaultCommand() {
39925
40122
  return this.name === "" || this.aliasNames.includes("!");
@@ -40346,7 +40543,7 @@ function onUnknownSubcommand(cliInstance, prefix) {
40346
40543
  cliInstance.on(`${prefix}:*`, () => {
40347
40544
  const args = cliInstance.args ?? [];
40348
40545
  process63.stderr.write(`Unknown ${prefix} subcommand: ${[...args].join(" ")}
40349
- ` + `Run \`${cliInstance.name} ${prefix} --help\` to see available subcommands.
40546
+ ` + `Run \`${cliInstance.name ?? "cli"} ${prefix} --help\` to see available subcommands.
40350
40547
  `);
40351
40548
  process63.exit(64);
40352
40549
  });
@@ -42481,10 +42678,8 @@ Received ${signal}, cleaning up...`);
42481
42678
  console.log(style.dim("Run"), `${this.name} --help`, style.dim("to see all available commands"));
42482
42679
  process53.exit(1);
42483
42680
  }
42484
- async parse(argv = processArgs2, {
42485
- run = true,
42486
- exitOnError = false
42487
- } = {}) {
42681
+ async parse(argv = processArgs2, options = {}) {
42682
+ let { run = true, exitOnError = false } = options;
42488
42683
  if (exitOnError) {
42489
42684
  try {
42490
42685
  return await this.parse(argv, { run });
@@ -43168,7 +43363,7 @@ Run \`${this.name ?? "cli"} --help\` for usage.`;
43168
43363
  this.placeholder = opts.placeholder;
43169
43364
  this.on("key", (char) => {
43170
43365
  if (char === "\t" && this.placeholder) {
43171
- if (!this.value) {
43366
+ if (!this.userInput || this.userInput === "\t") {
43172
43367
  this._setValue(this.placeholder);
43173
43368
  this._setUserInput(this.placeholder, true);
43174
43369
  }
@@ -43392,7 +43587,7 @@ var require_package = __commonJS((exports, module) => {
43392
43587
  module.exports = {
43393
43588
  name: "pickier",
43394
43589
  type: "module",
43395
- version: "0.1.28",
43590
+ version: "0.1.29",
43396
43591
  description: "Format, lint and more in a fraction of seconds.",
43397
43592
  author: "Chris Breuer <chris@stacksjs.org>",
43398
43593
  license: "MIT",
@@ -43474,7 +43669,7 @@ var require_package = __commonJS((exports, module) => {
43474
43669
  "@stacksjs/ts-spell-check": "^0.1.0"
43475
43670
  },
43476
43671
  devDependencies: {
43477
- bunfig: "^0.15.11"
43672
+ bunfig: "^0.15.13"
43478
43673
  }
43479
43674
  };
43480
43675
  });
@@ -43526,7 +43721,14 @@ async function main() {
43526
43721
  const { CLI: CLI2 } = await Promise.resolve().then(() => (init_dist4(), exports_dist3));
43527
43722
  const { version: version3 } = await Promise.resolve().then(() => __toESM(require_package(), 1));
43528
43723
  const cli2 = new CLI2("pickier");
43529
- cli2.command("[...globs]", "Lint files (default)").option("--fix", "Auto-fix lint problems").option("--format", "Format files instead of linting").option("--dry-run", "Simulate fixes without writing").option("--check", "Check formatting without writing (CI-friendly)").option("--max-warnings <n>", "Max warnings before non-zero exit", { default: -1 }).option("--reporter <name>", "stylish|json|compact", { default: "stylish" }).option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file (like .gitignore)").option("--ext <exts>", "Comma-separated extensions").option("--cache", "Enable cache").option("--verbose", "Verbose output").example("pickier .").example("pickier . --fix").example("pickier . --format").example("pickier src --fix --verbose").action(async (cmdGlobs, opts) => {
43724
+ const normalizeActionArgs = (args) => {
43725
+ const [cmdGlobsRaw, optsRaw] = args;
43726
+ const cmdGlobs = Array.isArray(cmdGlobsRaw) ? cmdGlobsRaw.filter((item) => typeof item === "string") : [];
43727
+ const opts = optsRaw && typeof optsRaw === "object" ? optsRaw : {};
43728
+ return [cmdGlobs, opts];
43729
+ };
43730
+ cli2.command("[...globs]", "Lint files (default)").option("--fix", "Auto-fix lint problems").option("--format", "Format files instead of linting").option("--dry-run", "Simulate fixes without writing").option("--check", "Check formatting without writing (CI-friendly)").option("--max-warnings <n>", "Max warnings before non-zero exit", { default: -1 }).option("--reporter <name>", "stylish|json|compact", { default: "stylish" }).option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file (like .gitignore)").option("--ext <exts>", "Comma-separated extensions").option("--cache", "Enable cache").option("--verbose", "Verbose output").example("pickier .").example("pickier . --fix").example("pickier . --format").example("pickier src --fix --verbose").action(async (...args) => {
43731
+ const [cmdGlobs, opts] = normalizeActionArgs(args);
43530
43732
  if (cmdGlobs.length === 0) {
43531
43733
  cli2.outputHelp();
43532
43734
  return;
@@ -43543,17 +43745,20 @@ async function main() {
43543
43745
  const code = await runUnified2(cmdGlobs, { ...opts, mode: runMode });
43544
43746
  process28.exit(code);
43545
43747
  });
43546
- cli2.command("lint [...globs]", "[DEPRECATION] Use `pickier [...globs]` instead. Lint files").option("--fix", "Auto-fix problems").option("--dry-run", "Simulate fixes without writing").option("--max-warnings <n>", "Max warnings before non-zero exit", { default: -1 }).option("--reporter <name>", "stylish|json|compact", { default: "stylish" }).option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file (like .gitignore)").option("--ext <exts>", "Comma-separated extensions").option("--cache", "Enable cache").option("--verbose", "Verbose output").example("pickier lint .").example("pickier lint src --fix").example('pickier lint "src/**/*.{ts,tsx}" --reporter json').action(async (cmdGlobs, opts) => {
43748
+ cli2.command("lint [...globs]", "[DEPRECATION] Use `pickier [...globs]` instead. Lint files").option("--fix", "Auto-fix problems").option("--dry-run", "Simulate fixes without writing").option("--max-warnings <n>", "Max warnings before non-zero exit", { default: -1 }).option("--reporter <name>", "stylish|json|compact", { default: "stylish" }).option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file (like .gitignore)").option("--ext <exts>", "Comma-separated extensions").option("--cache", "Enable cache").option("--verbose", "Verbose output").example("pickier lint .").example("pickier lint src --fix").example('pickier lint "src/**/*.{ts,tsx}" --reporter json').action(async (...args) => {
43749
+ const [cmdGlobs, opts] = normalizeActionArgs(args);
43547
43750
  const { runUnified: runUnified2 } = await Promise.resolve().then(() => (init_run(), exports_run));
43548
43751
  const code = await runUnified2(cmdGlobs, { ...opts, mode: "lint" });
43549
43752
  process28.exit(code);
43550
43753
  });
43551
- cli2.command("format [...globs]", "[DEPRECATION] Use `pickier [...globs] --format` instead. Format files").option("--write", "Write changes to files").option("--check", "Check without writing (CI-friendly)").option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file").option("--ext <exts>", "Comma-separated extensions").option("--verbose", "Verbose output").example("pickier format . --write").example("pickier format . --check").action(async (cmdGlobs, opts) => {
43754
+ cli2.command("format [...globs]", "[DEPRECATION] Use `pickier [...globs] --format` instead. Format files").option("--write", "Write changes to files").option("--check", "Check without writing (CI-friendly)").option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file").option("--ext <exts>", "Comma-separated extensions").option("--verbose", "Verbose output").example("pickier format . --write").example("pickier format . --check").action(async (...args) => {
43755
+ const [cmdGlobs, opts] = normalizeActionArgs(args);
43552
43756
  const { runUnified: runUnified2 } = await Promise.resolve().then(() => (init_run(), exports_run));
43553
43757
  const code = await runUnified2(cmdGlobs, { ...opts, mode: "format" });
43554
43758
  process28.exit(code);
43555
43759
  });
43556
- cli2.command("run [...globs]", "Unified mode (auto, lint, or format)").option("--mode <mode>", "auto|lint|format", { default: "auto" }).option("--fix", "Auto-fix problems (lint mode)").option("--dry-run", "Simulate fixes without writing (lint mode)").option("--max-warnings <n>", "Max warnings before non-zero exit (lint mode)", { default: -1 }).option("--reporter <name>", "stylish|json|compact (lint mode)", { default: "stylish" }).option("--write", "Write changes to files (format mode)").option("--check", "Check without writing (format mode)").option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file (like .gitignore)").option("--ext <exts>", "Comma-separated extensions").option("--cache", "Enable cache (lint mode)").option("--verbose", "Verbose output").example("pickier run . --mode lint --fix").example("pickier run . --mode format --write").action(async (cmdGlobs, opts) => {
43760
+ cli2.command("run [...globs]", "Unified mode (auto, lint, or format)").option("--mode <mode>", "auto|lint|format", { default: "auto" }).option("--fix", "Auto-fix problems (lint mode)").option("--dry-run", "Simulate fixes without writing (lint mode)").option("--max-warnings <n>", "Max warnings before non-zero exit (lint mode)", { default: -1 }).option("--reporter <name>", "stylish|json|compact (lint mode)", { default: "stylish" }).option("--write", "Write changes to files (format mode)").option("--check", "Check without writing (format mode)").option("--config <path>", "Path to pickier config").option("--ignore-path <file>", "Ignore file (like .gitignore)").option("--ext <exts>", "Comma-separated extensions").option("--cache", "Enable cache (lint mode)").option("--verbose", "Verbose output").example("pickier run . --mode lint --fix").example("pickier run . --mode format --write").action(async (...args) => {
43761
+ const [cmdGlobs, opts] = normalizeActionArgs(args);
43557
43762
  const { runUnified: runUnified2 } = await Promise.resolve().then(() => (init_run(), exports_run));
43558
43763
  const code = await runUnified2(cmdGlobs, opts);
43559
43764
  process28.exit(code);