pickier 0.1.18 → 0.1.19

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 (30) hide show
  1. package/README.md +62 -39
  2. package/dist/bin/cli.js +10847 -11113
  3. package/dist/plugins/publint.d.ts +11 -0
  4. package/dist/plugins/utils.d.ts +2 -1
  5. package/dist/rules/general/prefer-template.d.ts +0 -6
  6. package/dist/rules/markdown/no-duplicate-heading.d.ts +5 -0
  7. package/dist/rules/publint/bin-file-not-executable.d.ts +2 -0
  8. package/dist/rules/publint/deprecated-field-jsnext.d.ts +2 -0
  9. package/dist/rules/publint/exports-default-should-be-last.d.ts +2 -0
  10. package/dist/rules/publint/exports-fallback-array-use.d.ts +2 -0
  11. package/dist/rules/publint/exports-missing-root-entrypoint.d.ts +2 -0
  12. package/dist/rules/publint/exports-module-should-be-esm.d.ts +2 -0
  13. package/dist/rules/publint/exports-module-should-precede-require.d.ts +2 -0
  14. package/dist/rules/publint/exports-types-should-be-first.d.ts +2 -0
  15. package/dist/rules/publint/exports-value-invalid.d.ts +2 -0
  16. package/dist/rules/publint/field-invalid-value-type.d.ts +2 -0
  17. package/dist/rules/publint/file-does-not-exist.d.ts +2 -0
  18. package/dist/rules/publint/file-invalid-format.d.ts +2 -0
  19. package/dist/rules/publint/has-module-but-no-exports.d.ts +2 -0
  20. package/dist/rules/publint/imports-default-should-be-last.d.ts +2 -0
  21. package/dist/rules/publint/imports-key-invalid.d.ts +2 -0
  22. package/dist/rules/publint/imports-module-should-precede-require.d.ts +2 -0
  23. package/dist/rules/publint/imports-value-invalid.d.ts +2 -0
  24. package/dist/rules/publint/index.d.ts +20 -0
  25. package/dist/rules/publint/local-dependency.d.ts +2 -0
  26. package/dist/rules/publint/module-should-be-esm.d.ts +2 -0
  27. package/dist/rules/publint/use-type.d.ts +2 -0
  28. package/dist/rules/publint/utils.d.ts +73 -0
  29. package/dist/src/index.js +2100 -316
  30. package/package.json +1 -1
package/dist/src/index.js CHANGED
@@ -5121,6 +5121,8 @@ var init_config = __esm(async () => {
5121
5121
  "**/.github/**",
5122
5122
  "**/*.test.ts",
5123
5123
  "**/*.spec.ts",
5124
+ "**/.bunpress/**",
5125
+ "**/.vitepress/cache/**",
5124
5126
  "**/*.lock",
5125
5127
  "**/package-lock.json",
5126
5128
  "**/pnpm-lock.yaml"
@@ -5241,14 +5243,14 @@ var init_config = __esm(async () => {
5241
5243
  "markdown/line-length": "off",
5242
5244
  "markdown/commands-show-output": "warn",
5243
5245
  "markdown/fenced-code-language": "warn",
5244
- "markdown/code-block-style": "warn",
5246
+ "markdown/code-block-style": "off",
5245
5247
  "markdown/code-fence-style": "warn",
5246
5248
  "markdown/no-emphasis-as-heading": "warn",
5247
5249
  "markdown/no-space-in-emphasis": "warn",
5248
5250
  "markdown/no-space-in-code": "warn",
5249
5251
  "markdown/emphasis-style": "warn",
5250
5252
  "markdown/strong-style": "warn",
5251
- "markdown/no-inline-html": "warn",
5253
+ "markdown/no-inline-html": "off",
5252
5254
  "markdown/hr-style": "warn",
5253
5255
  "markdown/first-line-heading": "off",
5254
5256
  "markdown/required-headings": "off",
@@ -5261,7 +5263,27 @@ var init_config = __esm(async () => {
5261
5263
  "lockfile/validate-https": "error",
5262
5264
  "lockfile/validate-integrity": ["warn", { requiredAlgorithm: "sha512" }],
5263
5265
  "lockfile/validate-package-names": "error",
5264
- "lockfile/validate-scheme": ["error", { allowedSchemes: ["https:", "git+https:", "git+ssh:"] }]
5266
+ "lockfile/validate-scheme": ["error", { allowedSchemes: ["https:", "git+https:", "git+ssh:"] }],
5267
+ "publint/exports-types-should-be-first": "error",
5268
+ "publint/exports-default-should-be-last": "error",
5269
+ "publint/exports-module-should-precede-require": "error",
5270
+ "publint/exports-value-invalid": "error",
5271
+ "publint/imports-key-invalid": "error",
5272
+ "publint/imports-value-invalid": "error",
5273
+ "publint/imports-default-should-be-last": "error",
5274
+ "publint/imports-module-should-precede-require": "error",
5275
+ "publint/use-type": "warn",
5276
+ "publint/deprecated-field-jsnext": "warn",
5277
+ "publint/field-invalid-value-type": "error",
5278
+ "publint/local-dependency": "error",
5279
+ "publint/has-module-but-no-exports": "warn",
5280
+ "publint/exports-missing-root-entrypoint": "warn",
5281
+ "publint/exports-fallback-array-use": "warn",
5282
+ "publint/file-does-not-exist": "warn",
5283
+ "publint/file-invalid-format": "warn",
5284
+ "publint/module-should-be-esm": "error",
5285
+ "publint/bin-file-not-executable": "error",
5286
+ "publint/exports-module-should-be-esm": "error"
5265
5287
  },
5266
5288
  verbose: true
5267
5289
  };
@@ -14758,9 +14780,25 @@ function codeOnly(rule) {
14758
14780
  } : undefined
14759
14781
  };
14760
14782
  }
14761
- var CODE_EXTS2;
14783
+ function packageJsonOnly(rule) {
14784
+ return {
14785
+ meta: rule.meta,
14786
+ check: (content, context) => {
14787
+ if (!PACKAGE_JSON_RE.test(context.filePath))
14788
+ return [];
14789
+ return rule.check(content, context);
14790
+ },
14791
+ fix: rule.fix ? (content, context) => {
14792
+ if (!PACKAGE_JSON_RE.test(context.filePath))
14793
+ return content;
14794
+ return rule.fix(content, context);
14795
+ } : undefined
14796
+ };
14797
+ }
14798
+ var CODE_EXTS2, PACKAGE_JSON_RE;
14762
14799
  var init_utils = __esm(() => {
14763
14800
  CODE_EXTS2 = /\.(?:ts|js|tsx|jsx|mts|mjs|cts|cjs)$/;
14801
+ PACKAGE_JSON_RE = /(?:^|[/\\])package\.json$/;
14764
14802
  });
14765
14803
 
14766
14804
  // src/plugins/eslint.ts
@@ -15220,7 +15258,78 @@ var init_no_unused_vars = __esm(() => {
15220
15258
  const argIgnoreRe = new RegExp(argsIgnorePattern, "u");
15221
15259
  const lines = text.split(/\r?\n/);
15222
15260
  const full = text;
15261
+ const lineStartsInTemplate = new Array(lines.length).fill(false);
15262
+ {
15263
+ const tmplStack = [];
15264
+ let tInSingle = false;
15265
+ let tInDouble = false;
15266
+ let tEscaped = false;
15267
+ for (let li = 0;li < lines.length; li++) {
15268
+ lineStartsInTemplate[li] = tmplStack.length > 0 && tmplStack[tmplStack.length - 1] === -1;
15269
+ const s = lines[li];
15270
+ for (let k = 0;k < s.length; k++) {
15271
+ const ch = s[k];
15272
+ if (tEscaped) {
15273
+ tEscaped = false;
15274
+ continue;
15275
+ }
15276
+ const inBody = tmplStack.length > 0 && tmplStack[tmplStack.length - 1] === -1;
15277
+ const inExpr = tmplStack.length > 0 && tmplStack[tmplStack.length - 1] >= 0;
15278
+ if (ch === "\\" && (tInSingle || tInDouble || inBody)) {
15279
+ tEscaped = true;
15280
+ continue;
15281
+ }
15282
+ if (tInSingle) {
15283
+ if (ch === "'")
15284
+ tInSingle = false;
15285
+ continue;
15286
+ }
15287
+ if (tInDouble) {
15288
+ if (ch === '"')
15289
+ tInDouble = false;
15290
+ continue;
15291
+ }
15292
+ if (inBody) {
15293
+ if (ch === "`") {
15294
+ tmplStack.pop();
15295
+ } else if (ch === "$" && k + 1 < s.length && s[k + 1] === "{") {
15296
+ tmplStack[tmplStack.length - 1] = 0;
15297
+ k++;
15298
+ }
15299
+ continue;
15300
+ }
15301
+ if (inExpr) {
15302
+ if (ch === "`") {
15303
+ tmplStack.push(-1);
15304
+ } else if (ch === "'") {
15305
+ tInSingle = true;
15306
+ } else if (ch === '"') {
15307
+ tInDouble = true;
15308
+ } else if (ch === "{") {
15309
+ tmplStack[tmplStack.length - 1]++;
15310
+ } else if (ch === "}") {
15311
+ if (tmplStack[tmplStack.length - 1] > 0)
15312
+ tmplStack[tmplStack.length - 1]--;
15313
+ else
15314
+ tmplStack[tmplStack.length - 1] = -1;
15315
+ } else if (ch === "/" && k + 1 < s.length && s[k + 1] === "/")
15316
+ break;
15317
+ continue;
15318
+ }
15319
+ if (ch === "`") {
15320
+ tmplStack.push(-1);
15321
+ } else if (ch === "'") {
15322
+ tInSingle = true;
15323
+ } else if (ch === '"') {
15324
+ tInDouble = true;
15325
+ } else if (ch === "/" && k + 1 < s.length && s[k + 1] === "/")
15326
+ break;
15327
+ }
15328
+ }
15329
+ }
15223
15330
  for (let i = 0;i < lines.length; i++) {
15331
+ if (lineStartsInTemplate[i])
15332
+ continue;
15224
15333
  const line = lines[i];
15225
15334
  const decl = line.match(/^\s*(?:const|let|var)\s+(.+?);?\s*$/);
15226
15335
  if (!decl)
@@ -15278,15 +15387,130 @@ var init_no_unused_vars = __esm(() => {
15278
15387
  if (!part)
15279
15388
  continue;
15280
15389
  const simple = part.match(/^([$A-Z_][\w$]*)/i);
15281
- const destruct = part.match(/^[{[](.+)[}\]]/);
15282
15390
  const names = [];
15283
15391
  if (simple) {
15284
15392
  names.push(simple[1]);
15285
- } else if (destruct) {
15286
- const inner = destruct[1];
15287
- const tokens = inner.split(/[^$\w]+/).filter(Boolean);
15288
- for (const t of tokens)
15289
- names.push(t);
15393
+ } else if (part.startsWith("{") || part.startsWith("[")) {
15394
+ const openChar = part[0];
15395
+ const closeChar = openChar === "{" ? "}" : "]";
15396
+ let dDepth = 0;
15397
+ let endIdx = -1;
15398
+ let dStr = null;
15399
+ let dEsc = false;
15400
+ for (let ci = 0;ci < part.length; ci++) {
15401
+ const ch = part[ci];
15402
+ if (dEsc) {
15403
+ dEsc = false;
15404
+ continue;
15405
+ }
15406
+ if (ch === "\\" && dStr) {
15407
+ dEsc = true;
15408
+ continue;
15409
+ }
15410
+ if (!dStr) {
15411
+ if (ch === "'" || ch === '"' || ch === "`") {
15412
+ dStr = ch === "'" ? "single" : ch === '"' ? "double" : "template";
15413
+ } else if (ch === openChar)
15414
+ dDepth++;
15415
+ else if (ch === closeChar) {
15416
+ dDepth--;
15417
+ if (dDepth === 0) {
15418
+ endIdx = ci;
15419
+ break;
15420
+ }
15421
+ }
15422
+ } else {
15423
+ if (dStr === "single" && ch === "'" || dStr === "double" && ch === '"' || dStr === "template" && ch === "`")
15424
+ dStr = null;
15425
+ }
15426
+ }
15427
+ if (endIdx > 0) {
15428
+ const inner = part.slice(1, endIdx);
15429
+ const fields = [];
15430
+ let fCurrent = "";
15431
+ let fDepth = 0;
15432
+ let fStr = null;
15433
+ let fEsc = false;
15434
+ for (let ci = 0;ci < inner.length; ci++) {
15435
+ const ch = inner[ci];
15436
+ if (fEsc) {
15437
+ fEsc = false;
15438
+ fCurrent += ch;
15439
+ continue;
15440
+ }
15441
+ if (ch === "\\" && fStr) {
15442
+ fEsc = true;
15443
+ fCurrent += ch;
15444
+ continue;
15445
+ }
15446
+ if (!fStr) {
15447
+ if (ch === "'" || ch === '"' || ch === "`") {
15448
+ fStr = ch === "'" ? "single" : ch === '"' ? "double" : "template";
15449
+ fCurrent += ch;
15450
+ continue;
15451
+ }
15452
+ if (ch === "(" || ch === "{" || ch === "[")
15453
+ fDepth++;
15454
+ if (ch === ")" || ch === "}" || ch === "]")
15455
+ fDepth--;
15456
+ if (ch === "," && fDepth === 0) {
15457
+ fields.push(fCurrent.trim());
15458
+ fCurrent = "";
15459
+ continue;
15460
+ }
15461
+ } else {
15462
+ if (fStr === "single" && ch === "'" || fStr === "double" && ch === '"' || fStr === "template" && ch === "`")
15463
+ fStr = null;
15464
+ }
15465
+ fCurrent += ch;
15466
+ }
15467
+ if (fCurrent.trim())
15468
+ fields.push(fCurrent.trim());
15469
+ for (const field of fields) {
15470
+ if (field.startsWith("...")) {
15471
+ const restName = field.slice(3).match(/^([$A-Z_][\w$]*)/i);
15472
+ if (restName)
15473
+ names.push(restName[1]);
15474
+ continue;
15475
+ }
15476
+ const colonIdx = field.indexOf(":");
15477
+ if (colonIdx !== -1) {
15478
+ let value = field.slice(colonIdx + 1).trim();
15479
+ let eqDepth = 0;
15480
+ for (let ci = 0;ci < value.length; ci++) {
15481
+ const ch = value[ci];
15482
+ if (ch === "(" || ch === "{" || ch === "[")
15483
+ eqDepth++;
15484
+ else if (ch === ")" || ch === "}" || ch === "]")
15485
+ eqDepth--;
15486
+ else if (ch === "=" && eqDepth === 0) {
15487
+ value = value.slice(0, ci).trim();
15488
+ break;
15489
+ }
15490
+ }
15491
+ const nameMatch = value.match(/^([$A-Z_][\w$]*)/i);
15492
+ if (nameMatch)
15493
+ names.push(nameMatch[1]);
15494
+ } else {
15495
+ let fieldName = field;
15496
+ let eqDepth = 0;
15497
+ for (let ci = 0;ci < fieldName.length; ci++) {
15498
+ const ch = fieldName[ci];
15499
+ if (ch === "(" || ch === "{" || ch === "[")
15500
+ eqDepth++;
15501
+ else if (ch === ")" || ch === "}" || ch === "]")
15502
+ eqDepth--;
15503
+ else if (ch === "=" && eqDepth === 0) {
15504
+ fieldName = fieldName.slice(0, ci).trim();
15505
+ break;
15506
+ }
15507
+ }
15508
+ const nameMatch = fieldName.match(/^([$A-Z_][\w$]*)/i);
15509
+ if (nameMatch)
15510
+ names.push(nameMatch[1]);
15511
+ }
15512
+ }
15513
+ }
15290
15514
  }
15291
15515
  for (const name of names) {
15292
15516
  if (varIgnoreRe.test(name))
@@ -15406,69 +15630,82 @@ var init_no_unused_vars = __esm(() => {
15406
15630
  const findBodyRange = (startLine, startColFrom) => {
15407
15631
  let openFound = false;
15408
15632
  let depth = 0;
15633
+ let bodyBraceDepth = 0;
15634
+ let bodySawBracePair = false;
15635
+ let bodyAngleDepth = 0;
15636
+ let bodyInStr = null;
15637
+ let bodyEsc = false;
15638
+ let isFirstSearchLine = true;
15639
+ let lastNonWhitespaceBeforeBrace = "";
15640
+ let depthInSingle = false;
15641
+ let depthInDouble = false;
15642
+ const depthTmplStack = [];
15643
+ let depthEscaped = false;
15409
15644
  for (let ln = startLine;ln < lines.length; ln++) {
15410
15645
  const s = lines[ln];
15411
15646
  let lineToProcess = s;
15647
+ const inMultiLineTmplBody = depthTmplStack.length > 0 && depthTmplStack[depthTmplStack.length - 1] === -1;
15412
15648
  let commentIdx = -1;
15413
15649
  let inStr = null;
15414
15650
  let esc = false;
15415
- for (let i = 0;i < s.length - 1; i++) {
15416
- const c = s[i];
15417
- const next = s[i + 1];
15418
- if (esc) {
15419
- esc = false;
15420
- continue;
15421
- }
15422
- if (c === "\\" && inStr) {
15423
- esc = true;
15424
- continue;
15425
- }
15426
- if (!inStr) {
15427
- if (c === "'") {
15428
- inStr = "single";
15429
- } else if (c === '"') {
15430
- inStr = "double";
15431
- } else if (c === "`") {
15432
- inStr = "template";
15433
- } else if (c === "/" && next === "/") {
15434
- commentIdx = i;
15435
- break;
15651
+ if (!inMultiLineTmplBody)
15652
+ for (let i = 0;i < s.length - 1; i++) {
15653
+ const c = s[i];
15654
+ const next = s[i + 1];
15655
+ if (esc) {
15656
+ esc = false;
15657
+ continue;
15436
15658
  }
15437
- } else {
15438
- if (inStr === "single" && c === "'" || inStr === "double" && c === '"' || inStr === "template" && c === "`") {
15439
- inStr = null;
15659
+ if (c === "\\" && inStr) {
15660
+ esc = true;
15661
+ continue;
15662
+ }
15663
+ if (!inStr) {
15664
+ if (c === "'") {
15665
+ inStr = "single";
15666
+ } else if (c === '"') {
15667
+ inStr = "double";
15668
+ } else if (c === "`") {
15669
+ inStr = "template";
15670
+ } else if (c === "/" && next === "/") {
15671
+ commentIdx = i;
15672
+ break;
15673
+ }
15674
+ } else {
15675
+ if (inStr === "single" && c === "'" || inStr === "double" && c === '"' || inStr === "template" && c === "`") {
15676
+ inStr = null;
15677
+ }
15440
15678
  }
15441
15679
  }
15442
- }
15443
15680
  if (commentIdx >= 0) {
15444
15681
  lineToProcess = s.slice(0, commentIdx);
15445
15682
  }
15446
15683
  const stripRegexFromLine = (str) => {
15447
15684
  let result = "";
15448
15685
  let i = 0;
15449
- let inString2 = null;
15450
- let escaped2 = false;
15686
+ let inString = null;
15687
+ let escaped = false;
15451
15688
  while (i < str.length) {
15452
15689
  const ch = str[i];
15453
- if (escaped2) {
15454
- escaped2 = false;
15690
+ if (escaped) {
15691
+ escaped = false;
15455
15692
  result += ch;
15456
15693
  i++;
15457
15694
  continue;
15458
15695
  }
15459
- if (ch === "\\" && inString2) {
15460
- escaped2 = true;
15696
+ if (ch === "\\" && inString) {
15697
+ escaped = true;
15461
15698
  result += ch;
15462
15699
  i++;
15463
15700
  continue;
15464
15701
  }
15465
- if (!inString2) {
15702
+ if (!inString) {
15466
15703
  if (ch === "'") {
15467
- inString2 = "single";
15704
+ inString = "single";
15468
15705
  } else if (ch === '"') {
15469
- inString2 = "double";
15706
+ inString = "double";
15470
15707
  } else if (ch === "`") {
15471
- inString2 = "template";
15708
+ inString = "template";
15472
15709
  } else if (ch === "/" && i > 0) {
15473
15710
  const before = str.slice(0, i).trimEnd();
15474
15711
  if (/[=([{,:;!&|?]$/.test(before) || before.endsWith("return")) {
@@ -15491,8 +15728,8 @@ var init_no_unused_vars = __esm(() => {
15491
15728
  }
15492
15729
  }
15493
15730
  } else {
15494
- if (inString2 === "single" && ch === "'" || inString2 === "double" && ch === '"' || inString2 === "template" && ch === "`") {
15495
- inString2 = null;
15731
+ if (inString === "single" && ch === "'" || inString === "double" && ch === '"' || inString === "template" && ch === "`") {
15732
+ inString = null;
15496
15733
  }
15497
15734
  }
15498
15735
  result += ch;
@@ -15500,63 +15737,71 @@ var init_no_unused_vars = __esm(() => {
15500
15737
  }
15501
15738
  return result;
15502
15739
  };
15503
- lineToProcess = stripRegexFromLine(lineToProcess);
15740
+ if (!inMultiLineTmplBody)
15741
+ lineToProcess = stripRegexFromLine(lineToProcess);
15504
15742
  let startIdx = 0;
15505
15743
  if (!openFound) {
15506
15744
  let foundIdx = -1;
15507
- inStr = null;
15508
- esc = false;
15509
- let angleDepth2 = 0;
15510
- let braceDepth = 0;
15511
- let sawBracePair = false;
15512
- const searchStart = typeof startColFrom === "number" ? startColFrom : 0;
15745
+ let searchStart = isFirstSearchLine ? typeof startColFrom === "number" ? startColFrom : 0 : 0;
15746
+ isFirstSearchLine = false;
15747
+ if (searchStart >= lineToProcess.length) {
15748
+ const arrowInProcessed = lineToProcess.indexOf("=>");
15749
+ searchStart = arrowInProcessed >= 0 ? arrowInProcessed + 2 : 0;
15750
+ }
15513
15751
  for (let i = searchStart;i < lineToProcess.length; i++) {
15514
15752
  const c = lineToProcess[i];
15515
- if (esc) {
15516
- esc = false;
15753
+ if (bodyEsc) {
15754
+ bodyEsc = false;
15517
15755
  continue;
15518
15756
  }
15519
- if (c === "\\" && inStr) {
15520
- esc = true;
15757
+ if (c === "\\" && bodyInStr) {
15758
+ bodyEsc = true;
15521
15759
  continue;
15522
15760
  }
15523
- if (!inStr) {
15761
+ if (!bodyInStr) {
15762
+ if (c !== " " && c !== "\t" && c !== `
15763
+ ` && c !== "\r" && c !== "{" && c !== "}" && bodyBraceDepth === 0) {
15764
+ lastNonWhitespaceBeforeBrace = c;
15765
+ }
15524
15766
  if (c === "'") {
15525
- inStr = "single";
15767
+ bodyInStr = "single";
15526
15768
  } else if (c === '"') {
15527
- inStr = "double";
15769
+ bodyInStr = "double";
15528
15770
  } else if (c === "`") {
15529
- inStr = "template";
15771
+ bodyInStr = "template";
15530
15772
  } else if (c === "<") {
15531
- angleDepth2++;
15773
+ bodyAngleDepth++;
15532
15774
  } else if (c === ">") {
15533
- angleDepth2 = Math.max(0, angleDepth2 - 1);
15775
+ bodyAngleDepth = Math.max(0, bodyAngleDepth - 1);
15534
15776
  } else if (c === "{") {
15535
- if (braceDepth === 0) {
15536
- if (sawBracePair && angleDepth2 === 0) {
15777
+ if (bodyBraceDepth === 0) {
15778
+ if (bodySawBracePair && bodyAngleDepth === 0) {
15537
15779
  foundIdx = i;
15538
15780
  break;
15539
15781
  }
15540
15782
  }
15541
- braceDepth++;
15783
+ bodyBraceDepth++;
15542
15784
  } else if (c === "}") {
15543
- if (braceDepth > 0) {
15544
- braceDepth--;
15545
- if (braceDepth === 0) {
15546
- sawBracePair = true;
15785
+ if (bodyBraceDepth > 0) {
15786
+ bodyBraceDepth--;
15787
+ if (bodyBraceDepth === 0) {
15788
+ bodySawBracePair = true;
15547
15789
  }
15548
15790
  }
15549
15791
  }
15550
15792
  } else {
15551
- if (inStr === "single" && c === "'" || inStr === "double" && c === '"' || inStr === "template" && c === "`") {
15552
- inStr = null;
15793
+ if (bodyInStr === "single" && c === "'" || bodyInStr === "double" && c === '"' || bodyInStr === "template" && c === "`") {
15794
+ bodyInStr = null;
15553
15795
  }
15554
15796
  }
15555
15797
  }
15556
15798
  if (foundIdx === -1) {
15799
+ if (bodyBraceDepth > 0 && lastNonWhitespaceBeforeBrace === ":") {
15800
+ continue;
15801
+ }
15557
15802
  for (let i = searchStart;i < lineToProcess.length; i++) {
15558
15803
  const c = lineToProcess[i];
15559
- if (c === "{" && !inStr) {
15804
+ if (c === "{" && !bodyInStr) {
15560
15805
  foundIdx = i;
15561
15806
  break;
15562
15807
  }
@@ -15567,47 +15812,86 @@ var init_no_unused_vars = __esm(() => {
15567
15812
  openFound = true;
15568
15813
  depth = 1;
15569
15814
  startIdx = foundIdx + 1;
15815
+ startLine = ln;
15570
15816
  }
15571
- let inString = null;
15572
- let escaped = false;
15573
- let angleDepth = 0;
15574
15817
  for (let k = startIdx;k < lineToProcess.length; k++) {
15575
15818
  const ch = lineToProcess[k];
15576
- if (escaped) {
15577
- escaped = false;
15819
+ if (depthEscaped) {
15820
+ depthEscaped = false;
15578
15821
  continue;
15579
15822
  }
15580
- if (ch === "\\" && inString) {
15581
- escaped = true;
15823
+ const inTmplBody = depthTmplStack.length > 0 && depthTmplStack[depthTmplStack.length - 1] === -1;
15824
+ const inTmplExpr = depthTmplStack.length > 0 && depthTmplStack[depthTmplStack.length - 1] >= 0;
15825
+ if (ch === "\\" && (depthInSingle || depthInDouble || inTmplBody)) {
15826
+ depthEscaped = true;
15582
15827
  continue;
15583
15828
  }
15584
- if (!inString) {
15829
+ if (depthInSingle) {
15830
+ if (ch === "'")
15831
+ depthInSingle = false;
15832
+ continue;
15833
+ }
15834
+ if (depthInDouble) {
15835
+ if (ch === '"')
15836
+ depthInDouble = false;
15837
+ continue;
15838
+ }
15839
+ if (inTmplBody) {
15840
+ if (ch === "`") {
15841
+ depthTmplStack.pop();
15842
+ } else if (ch === "$" && k + 1 < lineToProcess.length && lineToProcess[k + 1] === "{") {
15843
+ depthTmplStack[depthTmplStack.length - 1] = 0;
15844
+ k++;
15845
+ }
15846
+ continue;
15847
+ }
15848
+ if (inTmplExpr) {
15849
+ if (ch === "`") {
15850
+ depthTmplStack.push(-1);
15851
+ continue;
15852
+ }
15585
15853
  if (ch === "'") {
15586
- inString = "single";
15587
- } else if (ch === '"') {
15588
- inString = "double";
15589
- } else if (ch === "`") {
15590
- inString = "template";
15591
- } else if (ch === "<") {
15592
- angleDepth++;
15593
- } else if (ch === ">") {
15594
- angleDepth = Math.max(0, angleDepth - 1);
15595
- } else if (ch === "{" && angleDepth === 0) {
15596
- depth++;
15597
- } else if (ch === "}" && angleDepth === 0) {
15598
- depth--;
15599
- if (depth === 0)
15600
- return { from: startLine, to: ln };
15854
+ depthInSingle = true;
15855
+ continue;
15601
15856
  }
15602
- } else {
15603
- if (inString === "single" && ch === "'" || inString === "double" && ch === '"' || inString === "template" && ch === "`") {
15604
- inString = null;
15857
+ if (ch === '"') {
15858
+ depthInDouble = true;
15859
+ continue;
15860
+ }
15861
+ if (ch === "{") {
15862
+ depthTmplStack[depthTmplStack.length - 1]++;
15863
+ continue;
15605
15864
  }
15865
+ if (ch === "}") {
15866
+ if (depthTmplStack[depthTmplStack.length - 1] > 0) {
15867
+ depthTmplStack[depthTmplStack.length - 1]--;
15868
+ } else {
15869
+ depthTmplStack[depthTmplStack.length - 1] = -1;
15870
+ }
15871
+ }
15872
+ continue;
15873
+ }
15874
+ if (ch === "'") {
15875
+ depthInSingle = true;
15876
+ } else if (ch === '"') {
15877
+ depthInDouble = true;
15878
+ } else if (ch === "`") {
15879
+ depthTmplStack.push(-1);
15880
+ } else if (ch === "{") {
15881
+ depth++;
15882
+ } else if (ch === "}") {
15883
+ depth--;
15884
+ if (depth === 0)
15885
+ return { from: startLine, to: ln };
15606
15886
  }
15607
15887
  }
15608
15888
  }
15609
15889
  return null;
15610
15890
  };
15891
+ const mainTmplStack = [];
15892
+ let mainInSingle = false;
15893
+ let mainInDouble = false;
15894
+ let mainEscaped = false;
15611
15895
  for (let i = 0;i < lines.length; i++) {
15612
15896
  const line = lines[i];
15613
15897
  if (/^\s*\/\//.test(line))
@@ -15683,11 +15967,98 @@ var init_no_unused_vars = __esm(() => {
15683
15967
  return result;
15684
15968
  };
15685
15969
  const codeNoRegex = stripRegex(codeOnly2);
15686
- const m = codeNoRegex.match(/\bfunction\b/);
15970
+ let codeClean = codeNoRegex.replace(/'(?:[^'\\]|\\.)*'/g, "''").replace(/"(?:[^"\\]|\\.)*"/g, '""');
15971
+ {
15972
+ let masked = "";
15973
+ for (let ci = 0;ci < codeClean.length; ci++) {
15974
+ const ch = codeClean[ci];
15975
+ if (mainEscaped) {
15976
+ mainEscaped = false;
15977
+ const inBody2 = mainTmplStack.length > 0 && mainTmplStack[mainTmplStack.length - 1] === -1;
15978
+ masked += inBody2 ? " " : ch;
15979
+ continue;
15980
+ }
15981
+ const inBody = mainTmplStack.length > 0 && mainTmplStack[mainTmplStack.length - 1] === -1;
15982
+ const inExpr = mainTmplStack.length > 0 && mainTmplStack[mainTmplStack.length - 1] >= 0;
15983
+ if (ch === "\\" && (mainInSingle || mainInDouble || inBody)) {
15984
+ mainEscaped = true;
15985
+ masked += inBody ? " " : ch;
15986
+ continue;
15987
+ }
15988
+ if (mainInSingle) {
15989
+ if (ch === "'")
15990
+ mainInSingle = false;
15991
+ masked += ch;
15992
+ continue;
15993
+ }
15994
+ if (mainInDouble) {
15995
+ if (ch === '"')
15996
+ mainInDouble = false;
15997
+ masked += ch;
15998
+ continue;
15999
+ }
16000
+ if (inBody) {
16001
+ if (ch === "`") {
16002
+ mainTmplStack.pop();
16003
+ } else if (ch === "$" && ci + 1 < codeClean.length && codeClean[ci + 1] === "{") {
16004
+ mainTmplStack[mainTmplStack.length - 1] = 0;
16005
+ masked += " ";
16006
+ ci++;
16007
+ } else {
16008
+ masked += " ";
16009
+ }
16010
+ continue;
16011
+ }
16012
+ if (inExpr) {
16013
+ if (ch === "`") {
16014
+ mainTmplStack.push(-1);
16015
+ masked += " ";
16016
+ } else if (ch === "'") {
16017
+ mainInSingle = true;
16018
+ masked += ch;
16019
+ } else if (ch === '"') {
16020
+ mainInDouble = true;
16021
+ masked += ch;
16022
+ } else if (ch === "{") {
16023
+ mainTmplStack[mainTmplStack.length - 1]++;
16024
+ masked += ch;
16025
+ } else if (ch === "}") {
16026
+ if (mainTmplStack[mainTmplStack.length - 1] > 0) {
16027
+ mainTmplStack[mainTmplStack.length - 1]--;
16028
+ masked += ch;
16029
+ } else {
16030
+ mainTmplStack[mainTmplStack.length - 1] = -1;
16031
+ masked += " ";
16032
+ }
16033
+ } else {
16034
+ masked += ch;
16035
+ }
16036
+ continue;
16037
+ }
16038
+ if (ch === "`") {
16039
+ mainTmplStack.push(-1);
16040
+ masked += " ";
16041
+ } else if (ch === "'") {
16042
+ mainInSingle = true;
16043
+ masked += ch;
16044
+ } else if (ch === '"') {
16045
+ mainInDouble = true;
16046
+ masked += ch;
16047
+ } else {
16048
+ masked += ch;
16049
+ }
16050
+ }
16051
+ codeClean = masked;
16052
+ }
16053
+ const m = codeClean.match(/\bfunction\b/);
15687
16054
  if (m) {
15688
16055
  if (line.includes("function scanContent") || line.includes("function findMatching")) {
15689
16056
  continue;
15690
16057
  }
16058
+ const afterFunc = codeClean.slice(m.index + 8).trimStart();
16059
+ if (afterFunc.startsWith(":")) {
16060
+ continue;
16061
+ }
15691
16062
  const funcIdx = m.index;
15692
16063
  const openParenIdx = line.indexOf("(", funcIdx);
15693
16064
  if (openParenIdx === -1)
@@ -15746,7 +16117,7 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
15746
16117
  }
15747
16118
  }
15748
16119
  for (const name of params) {
15749
- if (!name || argIgnoreRe.test(name))
16120
+ if (!name || argIgnoreRe.test(name) || name === "undefined")
15750
16121
  continue;
15751
16122
  const re = new RegExp(`\\b${name}\\b`, "g");
15752
16123
  if (!re.test(bodyText)) {
@@ -15756,7 +16127,7 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
15756
16127
  continue;
15757
16128
  }
15758
16129
  const arrowIdx = line.indexOf("=>");
15759
- if (arrowIdx !== -1 && codeNoRegex.includes("=>")) {
16130
+ if (arrowIdx !== -1 && codeClean.includes("=>")) {
15760
16131
  let closeParenIdx = -1;
15761
16132
  for (let k = arrowIdx - 1;k >= 0; k--) {
15762
16133
  const ch = line[k];
@@ -15786,16 +16157,33 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
15786
16157
  }
15787
16158
  if (openParenIdx !== -1) {
15788
16159
  let isTypeSignature = false;
16160
+ let angleDepthBack = 0;
15789
16161
  for (let k = openParenIdx - 1;k >= 0; k--) {
15790
16162
  const ch = line[k];
15791
- if (ch === ":") {
16163
+ if (ch === ">") {
16164
+ angleDepthBack++;
16165
+ continue;
16166
+ }
16167
+ if (ch === "<") {
16168
+ if (angleDepthBack > 0) {
16169
+ angleDepthBack--;
16170
+ continue;
16171
+ }
16172
+ isTypeSignature = true;
16173
+ break;
16174
+ }
16175
+ if (ch === ":" && angleDepthBack === 0) {
15792
16176
  isTypeSignature = true;
15793
16177
  break;
15794
16178
  }
15795
- if (ch === "=" || ch === "," || ch === "(" || ch === "{" || ch === "[") {
16179
+ if (angleDepthBack > 0)
16180
+ continue;
16181
+ if (ch === ",")
16182
+ continue;
16183
+ if (ch === "=" || ch === "(" || ch === "{" || ch === "[") {
15796
16184
  break;
15797
16185
  }
15798
- if (ch !== " " && ch !== "\t" && !/\w/.test(ch)) {
16186
+ if (ch !== " " && ch !== "\t" && !/[\w.]/.test(ch)) {
15799
16187
  break;
15800
16188
  }
15801
16189
  }
@@ -15832,9 +16220,12 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
15832
16220
  let parenDepth = 0;
15833
16221
  let braceDepth = 0;
15834
16222
  let bracketDepth = 0;
16223
+ let inTemplate = false;
15835
16224
  for (let k = arrowIdx + 2;k < line.length; k++) {
15836
16225
  const ch = line[k];
15837
- if (ch === "(")
16226
+ if (ch === "`")
16227
+ inTemplate = !inTemplate;
16228
+ else if (ch === "(")
15838
16229
  parenDepth++;
15839
16230
  else if (ch === ")")
15840
16231
  parenDepth--;
@@ -15848,12 +16239,36 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
15848
16239
  bracketDepth--;
15849
16240
  }
15850
16241
  let nextLine = i + 1;
15851
- while (nextLine < lines.length && (parenDepth > 0 || braceDepth > 0 || bracketDepth > 0)) {
16242
+ if ((!bodyText.trim() || inTemplate) && nextLine < lines.length) {
16243
+ bodyText += `
16244
+ ${lines[nextLine]}`;
16245
+ for (let k = 0;k < lines[nextLine].length; k++) {
16246
+ const ch = lines[nextLine][k];
16247
+ if (ch === "`")
16248
+ inTemplate = !inTemplate;
16249
+ else if (ch === "(")
16250
+ parenDepth++;
16251
+ else if (ch === ")")
16252
+ parenDepth--;
16253
+ else if (ch === "{")
16254
+ braceDepth++;
16255
+ else if (ch === "}")
16256
+ braceDepth--;
16257
+ else if (ch === "[")
16258
+ bracketDepth++;
16259
+ else if (ch === "]")
16260
+ bracketDepth--;
16261
+ }
16262
+ nextLine++;
16263
+ }
16264
+ while (nextLine < lines.length && (parenDepth > 0 || braceDepth > 0 || bracketDepth > 0 || inTemplate)) {
15852
16265
  bodyText += `
15853
16266
  ${lines[nextLine]}`;
15854
16267
  for (let k = 0;k < lines[nextLine].length; k++) {
15855
16268
  const ch = lines[nextLine][k];
15856
- if (ch === "(")
16269
+ if (ch === "`")
16270
+ inTemplate = !inTemplate;
16271
+ else if (ch === "(")
15857
16272
  parenDepth++;
15858
16273
  else if (ch === ")")
15859
16274
  parenDepth--;
@@ -15870,7 +16285,7 @@ ${lines[nextLine]}`;
15870
16285
  }
15871
16286
  }
15872
16287
  for (const name of params) {
15873
- if (!name || argIgnoreRe.test(name))
16288
+ if (!name || argIgnoreRe.test(name) || name === "undefined")
15874
16289
  continue;
15875
16290
  const re = new RegExp(`\\b${name}\\b`, "g");
15876
16291
  if (!re.test(bodyText)) {
@@ -15886,10 +16301,17 @@ ${lines[nextLine]}`;
15886
16301
  {
15887
16302
  const reSingleArrow = /(?:^|[=,:({\s])\s*([$A-Z_][\w$]*)\s*=>/gi;
15888
16303
  let match;
15889
- while ((match = reSingleArrow.exec(codeNoRegex)) !== null) {
16304
+ while ((match = reSingleArrow.exec(codeClean)) !== null) {
15890
16305
  const name = match[1];
15891
- if (!name || argIgnoreRe.test(name))
16306
+ if (!name || argIgnoreRe.test(name) || name === "undefined")
16307
+ continue;
16308
+ const beforeMatch = codeClean.slice(0, match.index + match[0].indexOf(name)).trimEnd();
16309
+ if (/\)\s*:$/.test(beforeMatch) || /\)\s*:\s*$/.test(beforeMatch)) {
16310
+ continue;
16311
+ }
16312
+ if (/^(?:string|number|boolean|void|never|any|unknown|object|bigint|symbol|undefined|null)$/.test(name)) {
15892
16313
  continue;
16314
+ }
15893
16315
  const arrowPattern = new RegExp(`\\b${name}\\s*=>`);
15894
16316
  const arrowMatch = line.match(arrowPattern);
15895
16317
  if (!arrowMatch)
@@ -15921,9 +16343,12 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
15921
16343
  let parenDepth = 0;
15922
16344
  let braceDepth = 0;
15923
16345
  let bracketDepth = 0;
16346
+ let inTemplate = false;
15924
16347
  for (let k = arrowIdx2 + 2;k < line.length; k++) {
15925
16348
  const ch = line[k];
15926
- if (ch === "(")
16349
+ if (ch === "`")
16350
+ inTemplate = !inTemplate;
16351
+ else if (ch === "(")
15927
16352
  parenDepth++;
15928
16353
  else if (ch === ")")
15929
16354
  parenDepth--;
@@ -15937,12 +16362,36 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
15937
16362
  bracketDepth--;
15938
16363
  }
15939
16364
  let nextLine = i + 1;
15940
- while (nextLine < lines.length && (parenDepth > 0 || braceDepth > 0 || bracketDepth > 0)) {
16365
+ if ((!bodyText.trim() || inTemplate) && nextLine < lines.length) {
16366
+ bodyText += `
16367
+ ${lines[nextLine]}`;
16368
+ for (let k = 0;k < lines[nextLine].length; k++) {
16369
+ const ch = lines[nextLine][k];
16370
+ if (ch === "`")
16371
+ inTemplate = !inTemplate;
16372
+ else if (ch === "(")
16373
+ parenDepth++;
16374
+ else if (ch === ")")
16375
+ parenDepth--;
16376
+ else if (ch === "{")
16377
+ braceDepth++;
16378
+ else if (ch === "}")
16379
+ braceDepth--;
16380
+ else if (ch === "[")
16381
+ bracketDepth++;
16382
+ else if (ch === "]")
16383
+ bracketDepth--;
16384
+ }
16385
+ nextLine++;
16386
+ }
16387
+ while (nextLine < lines.length && (parenDepth > 0 || braceDepth > 0 || bracketDepth > 0 || inTemplate)) {
15941
16388
  bodyText += `
15942
16389
  ${lines[nextLine]}`;
15943
16390
  for (let k = 0;k < lines[nextLine].length; k++) {
15944
16391
  const ch = lines[nextLine][k];
15945
- if (ch === "(")
16392
+ if (ch === "`")
16393
+ inTemplate = !inTemplate;
16394
+ else if (ch === "(")
15946
16395
  parenDepth++;
15947
16396
  else if (ch === ")")
15948
16397
  parenDepth--;
@@ -15971,23 +16420,82 @@ ${lines[nextLine]}`;
15971
16420
  });
15972
16421
 
15973
16422
  // src/rules/general/prefer-const.ts
15974
- var preferConstRule;
15975
- var init_prefer_const = __esm(() => {
15976
- preferConstRule = {
15977
- meta: { docs: "Suggest 'const' for variables that are never reassigned (heuristic)" },
15978
- check: (text, ctx) => {
15979
- const issues = [];
15980
- const lines = text.split(/\r?\n/);
15981
- for (let i = 0;i < lines.length; i++) {
15982
- const line = lines[i];
15983
- const decl = line.match(/^\s*(?:let|var)\s+(.+?);?\s*$/);
15984
- if (!decl)
15985
- continue;
15986
- const after = decl[1];
15987
- const parts = after.split(",");
15988
- for (const partRaw of parts) {
15989
- const part = partRaw.trim();
15990
- if (!part)
16423
+ function splitTopLevel(s, sep2) {
16424
+ const parts = [];
16425
+ let depth = 0;
16426
+ let start = 0;
16427
+ let inStr = null;
16428
+ let escaped = false;
16429
+ for (let i = 0;i < s.length; i++) {
16430
+ const c = s[i];
16431
+ if (escaped) {
16432
+ escaped = false;
16433
+ continue;
16434
+ }
16435
+ if (c === "\\" && inStr) {
16436
+ escaped = true;
16437
+ continue;
16438
+ }
16439
+ if (!inStr) {
16440
+ if (c === "'")
16441
+ inStr = "single";
16442
+ else if (c === '"')
16443
+ inStr = "double";
16444
+ else if (c === "`")
16445
+ inStr = "template";
16446
+ else if (c === "(" || c === "{" || c === "[")
16447
+ depth++;
16448
+ else if (c === ")" || c === "}" || c === "]")
16449
+ depth--;
16450
+ else if (c === sep2 && depth === 0) {
16451
+ parts.push(s.slice(start, i));
16452
+ start = i + 1;
16453
+ }
16454
+ } else {
16455
+ if (inStr === "single" && c === "'" || inStr === "double" && c === '"' || inStr === "template" && c === "`")
16456
+ inStr = null;
16457
+ }
16458
+ }
16459
+ parts.push(s.slice(start));
16460
+ return parts;
16461
+ }
16462
+ function findTopLevelEquals(s) {
16463
+ let depth = 0;
16464
+ for (let i = 0;i < s.length; i++) {
16465
+ const c = s[i];
16466
+ if (c === "{" || c === "(" || c === "[" || c === "<")
16467
+ depth++;
16468
+ else if (c === "}" || c === ")" || c === "]" || c === ">")
16469
+ depth--;
16470
+ else if (c === "=" && depth === 0 && s[i + 1] !== "=" && s[i - 1] !== "!" && s[i - 1] !== "<" && s[i - 1] !== ">")
16471
+ return i;
16472
+ }
16473
+ return -1;
16474
+ }
16475
+ var preferConstRule;
16476
+ var init_prefer_const = __esm(() => {
16477
+ preferConstRule = {
16478
+ meta: { docs: "Suggest 'const' for variables that are never reassigned (heuristic)" },
16479
+ check: (text, ctx) => {
16480
+ const issues = [];
16481
+ const lines = text.split(/\r?\n/);
16482
+ for (let i = 0;i < lines.length; i++) {
16483
+ const line = lines[i];
16484
+ const decl = line.match(/^\s*(?:let|var)\s+(.+?);?\s*$/);
16485
+ if (!decl)
16486
+ continue;
16487
+ let after = decl[1];
16488
+ const eqIdx = findTopLevelEquals(after);
16489
+ if (eqIdx >= 0) {
16490
+ const colonIdx = after.indexOf(":");
16491
+ if (colonIdx >= 0 && colonIdx < eqIdx) {
16492
+ after = after.slice(0, colonIdx) + after.slice(eqIdx);
16493
+ }
16494
+ }
16495
+ const parts = splitTopLevel(after, ",");
16496
+ for (const partRaw of parts) {
16497
+ const part = partRaw.trim();
16498
+ if (!part)
15991
16499
  continue;
15992
16500
  const simple = part.match(/^([$A-Z_][\w$]*)/i);
15993
16501
  const destruct = part.match(/^[{[]/);
@@ -16141,6 +16649,25 @@ var init_prefer_object_spread = __esm(() => {
16141
16649
  });
16142
16650
 
16143
16651
  // src/rules/general/prefer-template.ts
16652
+ function isInsideString(line, pos) {
16653
+ let inSingle = false;
16654
+ let inDouble = false;
16655
+ let inTemplate = false;
16656
+ for (let i = 0;i < pos; i++) {
16657
+ const c = line[i];
16658
+ if (c === "\\") {
16659
+ i++;
16660
+ continue;
16661
+ }
16662
+ if (c === "'" && !inDouble && !inTemplate)
16663
+ inSingle = !inSingle;
16664
+ else if (c === '"' && !inSingle && !inTemplate)
16665
+ inDouble = !inDouble;
16666
+ else if (c === "`" && !inSingle && !inDouble)
16667
+ inTemplate = !inTemplate;
16668
+ }
16669
+ return inSingle || inDouble || inTemplate;
16670
+ }
16144
16671
  var preferTemplate;
16145
16672
  var init_prefer_template = __esm(() => {
16146
16673
  preferTemplate = {
@@ -16164,6 +16691,15 @@ var init_prefer_template = __esm(() => {
16164
16691
  const matchIdx = line.indexOf(match[0]);
16165
16692
  if (commentIdx >= 0 && matchIdx > commentIdx)
16166
16693
  continue;
16694
+ if (isInsideString(line, matchIdx))
16695
+ continue;
16696
+ const isIdentPlusString = !!trimmed.match(identifierPlusString);
16697
+ const isStringPlusIdent = !!trimmed.match(stringPlusIdentifier);
16698
+ if (isIdentPlusString && !isStringPlusIdent) {
16699
+ const afterMatch = trimmed.slice(trimmed.indexOf(match[0]) + match[0].length);
16700
+ if (afterMatch.startsWith("."))
16701
+ continue;
16702
+ }
16167
16703
  issues.push({
16168
16704
  filePath: context.filePath,
16169
16705
  line: i + 1,
@@ -17150,7 +17686,7 @@ var init_blanks_around_tables = __esm(() => {
17150
17686
  if (line.trim().length > 0) {
17151
17687
  issues.push({
17152
17688
  filePath: ctx.filePath,
17153
- line: i,
17689
+ line: i + 1,
17154
17690
  column: 1,
17155
17691
  ruleId: "markdown/blanks-around-tables",
17156
17692
  message: "Tables should be surrounded by blank lines",
@@ -17177,11 +17713,12 @@ var init_code_block_style = __esm(() => {
17177
17713
  const options = ctx.options || {};
17178
17714
  const style = options.style || "consistent";
17179
17715
  let detectedStyle = null;
17716
+ let inFence = false;
17180
17717
  for (let i = 0;i < lines.length; i++) {
17181
17718
  const line = lines[i];
17182
17719
  const isFenced = /^(`{3,}|~{3,})/.test(line);
17183
- const isIndented = /^( {4}|\t)/.test(line) && line.trim().length > 0;
17184
17720
  if (isFenced) {
17721
+ inFence = !inFence;
17185
17722
  if (style === "indented") {
17186
17723
  issues.push({
17187
17724
  filePath: ctx.filePath,
@@ -17205,7 +17742,13 @@ var init_code_block_style = __esm(() => {
17205
17742
  });
17206
17743
  }
17207
17744
  }
17208
- } else if (isIndented) {
17745
+ continue;
17746
+ }
17747
+ if (inFence) {
17748
+ continue;
17749
+ }
17750
+ const isIndented = /^( {4}|\t)/.test(line) && line.trim().length > 0;
17751
+ if (isIndented) {
17209
17752
  const prevLine = i > 0 ? lines[i - 1] : "";
17210
17753
  const isAfterBlankLine = prevLine.trim().length === 0 || i === 0;
17211
17754
  if (isAfterBlankLine && style === "fenced") {
@@ -17408,9 +17951,17 @@ var init_descriptive_link_text = __esm(() => {
17408
17951
  const issues = [];
17409
17952
  const lines = text.split(/\r?\n/);
17410
17953
  const nonDescriptive = ["click here", "here", "link", "read more", "more", "this"];
17954
+ let inFence = false;
17411
17955
  for (let i = 0;i < lines.length; i++) {
17412
17956
  const line = lines[i];
17413
- const matches = line.matchAll(/\[([^\]]+)\]\([^)]+\)/g);
17957
+ if (/^(`{3,}|~{3,})/.test(line.trim())) {
17958
+ inFence = !inFence;
17959
+ continue;
17960
+ }
17961
+ if (inFence)
17962
+ continue;
17963
+ const stripped = line.replace(/``[^`]+``/g, (m) => " ".repeat(m.length)).replace(/`[^`]+`/g, (m) => " ".repeat(m.length));
17964
+ const matches = stripped.matchAll(/\[([^\]]+)\]\([^)]+\)/g);
17414
17965
  for (const match of matches) {
17415
17966
  const linkText = match[1].toLowerCase().trim();
17416
17967
  if (nonDescriptive.includes(linkText)) {
@@ -17444,9 +17995,17 @@ var init_emphasis_style = __esm(() => {
17444
17995
  const options = ctx.options || {};
17445
17996
  const style = options.style || "consistent";
17446
17997
  let detectedStyle = null;
17998
+ let inFence = false;
17447
17999
  for (let i = 0;i < lines.length; i++) {
17448
18000
  const line = lines[i];
17449
- const asteriskMatches = line.matchAll(/(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g);
18001
+ if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
18002
+ inFence = !inFence;
18003
+ continue;
18004
+ }
18005
+ if (inFence)
18006
+ continue;
18007
+ const stripped = line.replace(/``[^`]+``/g, " ").replace(/`[^`]+`/g, " ");
18008
+ const asteriskMatches = stripped.matchAll(/(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g);
17450
18009
  for (const match of asteriskMatches) {
17451
18010
  if (style === "underscore") {
17452
18011
  issues.push({
@@ -17472,7 +18031,7 @@ var init_emphasis_style = __esm(() => {
17472
18031
  }
17473
18032
  }
17474
18033
  }
17475
- const underscoreMatches = line.matchAll(/(?<!_)_(?!_)([^_]+)_(?!_)/g);
18034
+ const underscoreMatches = stripped.matchAll(/(?<!_)_(?!_)([^_]+)_(?!_)/g);
17476
18035
  for (const match of underscoreMatches) {
17477
18036
  if (style === "asterisk") {
17478
18037
  issues.push({
@@ -17963,7 +18522,7 @@ var init_link_image_style = __esm(() => {
17963
18522
  let inHtmlComment = false;
17964
18523
  for (let i = 0;i < lines.length; i++) {
17965
18524
  const line = lines[i];
17966
- if (/^(`{3,}|~{3,})/.test(line.trim())) {
18525
+ if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
17967
18526
  inFence = !inFence;
17968
18527
  continue;
17969
18528
  }
@@ -18163,9 +18722,17 @@ var init_no_alt_text = __esm(() => {
18163
18722
  check: (text, ctx) => {
18164
18723
  const issues = [];
18165
18724
  const lines = text.split(/\r?\n/);
18725
+ let inFence = false;
18166
18726
  for (let i = 0;i < lines.length; i++) {
18167
18727
  const line = lines[i];
18168
- const emptyAltMatches = line.matchAll(/!\[\s*\]\([^)]+\)/g);
18728
+ if (/^(`{3,}|~{3,})/.test(line.trim())) {
18729
+ inFence = !inFence;
18730
+ continue;
18731
+ }
18732
+ if (inFence)
18733
+ continue;
18734
+ const stripped = line.replace(/``[^`]+``/g, (m) => " ".repeat(m.length)).replace(/`[^`]+`/g, (m) => " ".repeat(m.length));
18735
+ const emptyAltMatches = stripped.matchAll(/!\[\s*\]\([^)]+\)/g);
18169
18736
  for (const match of emptyAltMatches) {
18170
18737
  const column = match.index + 1;
18171
18738
  issues.push({
@@ -18305,8 +18872,8 @@ var init_no_duplicate_heading = __esm(() => {
18305
18872
  check: (text, ctx) => {
18306
18873
  const issues = [];
18307
18874
  const lines = text.split(/\r?\n/);
18308
- const headings = new Map;
18309
18875
  let inFence = false;
18876
+ const headingsByLevel = Array.from({ length: 7 }, () => new Map);
18310
18877
  for (let i = 0;i < lines.length; i++) {
18311
18878
  const line = lines[i];
18312
18879
  if (/^(`{3,}|~{3,})/.test(line.trim())) {
@@ -18315,38 +18882,41 @@ var init_no_duplicate_heading = __esm(() => {
18315
18882
  }
18316
18883
  if (inFence)
18317
18884
  continue;
18318
- const atxMatch = line.match(/^#{1,6}\s+(.+?)(?:\s*#+\s*)?$/);
18885
+ let content = null;
18886
+ let level = 0;
18887
+ const atxMatch = line.match(/^(#{1,6})\s+(.+?)(?:\s*#+\s*)?$/);
18319
18888
  if (atxMatch) {
18320
- const content = atxMatch[1].trim();
18321
- if (headings.has(content)) {
18322
- issues.push({
18323
- filePath: ctx.filePath,
18324
- line: i + 1,
18325
- column: 1,
18326
- ruleId: "markdown/no-duplicate-heading",
18327
- message: `Duplicate heading "${content}" (first occurrence on line ${headings.get(content)})`,
18328
- severity: "error"
18329
- });
18330
- } else {
18331
- headings.set(content, i + 1);
18332
- }
18889
+ level = atxMatch[1].length;
18890
+ content = atxMatch[2].trim();
18333
18891
  }
18334
- const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
18335
- if (/^(=+|-+)\s*$/.test(nextLine) && line.trim().length > 0) {
18336
- const content = line.trim();
18337
- if (headings.has(content)) {
18338
- issues.push({
18339
- filePath: ctx.filePath,
18340
- line: i + 1,
18341
- column: 1,
18342
- ruleId: "markdown/no-duplicate-heading",
18343
- message: `Duplicate heading "${content}" (first occurrence on line ${headings.get(content)})`,
18344
- severity: "error"
18345
- });
18346
- } else {
18347
- headings.set(content, i + 1);
18892
+ if (!content) {
18893
+ const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
18894
+ if (/^(=+)\s*$/.test(nextLine) && line.trim().length > 0) {
18895
+ level = 1;
18896
+ content = line.trim();
18897
+ } else if (/^(-+)\s*$/.test(nextLine) && line.trim().length > 0) {
18898
+ level = 2;
18899
+ content = line.trim();
18348
18900
  }
18349
18901
  }
18902
+ if (!content || level === 0)
18903
+ continue;
18904
+ for (let l = level + 1;l <= 6; l++) {
18905
+ headingsByLevel[l].clear();
18906
+ }
18907
+ const siblings = headingsByLevel[level];
18908
+ if (siblings.has(content)) {
18909
+ issues.push({
18910
+ filePath: ctx.filePath,
18911
+ line: i + 1,
18912
+ column: 1,
18913
+ ruleId: "markdown/no-duplicate-heading",
18914
+ message: `Duplicate heading "${content}" (first occurrence on line ${siblings.get(content)})`,
18915
+ severity: "error"
18916
+ });
18917
+ } else {
18918
+ siblings.set(content, i + 1);
18919
+ }
18350
18920
  }
18351
18921
  return issues;
18352
18922
  }
@@ -18503,10 +19073,15 @@ var init_no_inline_html = __esm(() => {
18503
19073
  }
18504
19074
  if (inFence)
18505
19075
  continue;
18506
- const stripped = line.replace(/`[^`]+`/g, (m) => " ".repeat(m.length));
19076
+ let stripped = line;
19077
+ stripped = stripped.replace(/``[^`]+``/g, (m) => " ".repeat(m.length));
19078
+ stripped = stripped.replace(/`[^`]+`/g, (m) => " ".repeat(m.length));
18507
19079
  const matches = stripped.matchAll(/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi);
18508
19080
  for (const match of matches) {
18509
19081
  const tagName = match[1].toLowerCase();
19082
+ const afterTag = stripped.slice(match.index + 1 + tagName.length);
19083
+ if (afterTag.startsWith("://") || tagName === "mailto" && afterTag.startsWith(":"))
19084
+ continue;
18510
19085
  if (!allowedElements.includes(tagName)) {
18511
19086
  const column = match.index + 1;
18512
19087
  issues.push({
@@ -19259,7 +19834,8 @@ var init_reference_links_images = __esm(() => {
19259
19834
  continue;
19260
19835
  if (/^\[([^\]]+)\]:\s*\S+/.test(line))
19261
19836
  continue;
19262
- const stripped = line.replace(/`[^`]+`/g, (m) => " ".repeat(m.length));
19837
+ let stripped = line.replace(/`[^`]+`/g, (m) => " ".repeat(m.length));
19838
+ stripped = stripped.replace(/!?\[[^\]]*\]\([^)]*\)/g, (m) => " ".repeat(m.length));
19263
19839
  const linkMatches = stripped.matchAll(/\[([^\]]+)\](?:\[([^\]]+)\])?(?!\()/g);
19264
19840
  for (const match of linkMatches) {
19265
19841
  const label = (match[2] || match[1]).toLowerCase();
@@ -19531,8 +20107,19 @@ var init_table_column_count = __esm(() => {
19531
20107
  let inTable = false;
19532
20108
  let expectedColumns = -1;
19533
20109
  let tableStartLine = -1;
20110
+ let inFence = false;
19534
20111
  for (let i = 0;i < lines.length; i++) {
19535
20112
  const line = lines[i];
20113
+ if (/^(`{3,}|~{3,})/.test(line.trim())) {
20114
+ inFence = !inFence;
20115
+ if (inTable) {
20116
+ inTable = false;
20117
+ expectedColumns = -1;
20118
+ }
20119
+ continue;
20120
+ }
20121
+ if (inFence)
20122
+ continue;
19536
20123
  const isTableLine = /\|/.test(line) && line.trim().length > 0;
19537
20124
  if (isTableLine) {
19538
20125
  const columns = line.split("|").filter((col) => col.trim().length > 0 || line.trim().startsWith("|") || line.trim().endsWith("|"));
@@ -19730,8 +20317,15 @@ var init_ul_style = __esm(() => {
19730
20317
  const options = ctx.options || {};
19731
20318
  const style = options.style || "consistent";
19732
20319
  let detectedStyle = null;
20320
+ let inFence = false;
19733
20321
  for (let i = 0;i < lines.length; i++) {
19734
20322
  const line = lines[i];
20323
+ if (/^(`{3,}|~{3,})/.test(line.trim())) {
20324
+ inFence = !inFence;
20325
+ continue;
20326
+ }
20327
+ if (inFence)
20328
+ continue;
19735
20329
  const match = line.match(/^(\s*)([*\-+])\s+/);
19736
20330
  if (match) {
19737
20331
  const marker = match[2];
@@ -19800,7 +20394,14 @@ var init_ul_style = __esm(() => {
19800
20394
  }
19801
20395
  }
19802
20396
  }
20397
+ let inFence = false;
19803
20398
  const fixedLines = lines.map((line) => {
20399
+ if (/^(`{3,}|~{3,})/.test(line.trim())) {
20400
+ inFence = !inFence;
20401
+ return line;
20402
+ }
20403
+ if (inFence)
20404
+ return line;
19804
20405
  return line.replace(/^(\s*)([*\-+])(\s+)/, `$1${targetMarker}$3`);
19805
20406
  });
19806
20407
  return fixedLines.join(`
@@ -20029,7 +20630,7 @@ var init_prefer_global_process = __esm(() => {
20029
20630
  check(content, context) {
20030
20631
  const issues = [];
20031
20632
  const lines = content.split(/\r?\n/);
20032
- const hasProcessImport = content.match(/(?:import|require)\s*\(?.*?['"]process['"]/);
20633
+ const hasProcessImport = content.match(/(?:import|require)\s*\(?.*?['"](?:node:)?process['"]/);
20033
20634
  if (!hasProcessImport) {
20034
20635
  for (let i = 0;i < lines.length; i++) {
20035
20636
  const line = lines[i];
@@ -20060,70 +20661,1033 @@ var init_prefer_global_process = __esm(() => {
20060
20661
  }
20061
20662
  }
20062
20663
  }
20063
- return issues;
20664
+ return issues;
20665
+ }
20666
+ };
20667
+ });
20668
+
20669
+ // src/plugins/node.ts
20670
+ var nodePlugin;
20671
+ var init_node = __esm(() => {
20672
+ init_prefer_global_buffer();
20673
+ init_prefer_global_process();
20674
+ init_utils();
20675
+ nodePlugin = {
20676
+ name: "node",
20677
+ rules: {
20678
+ "prefer-global/buffer": codeOnly(preferGlobalBuffer),
20679
+ "prefer-global/process": codeOnly(preferGlobalProcess)
20680
+ }
20681
+ };
20682
+ });
20683
+
20684
+ // src/rules/sort/imports.ts
20685
+ var sortImportsRule;
20686
+ var init_imports = __esm(() => {
20687
+ init_format();
20688
+ sortImportsRule = {
20689
+ meta: { docs: "Enforce sorted imports (delegates to formatter check only)" },
20690
+ check: (text, ctx) => {
20691
+ const lines = text.split(/\r?\n/);
20692
+ let idx = 0;
20693
+ while (idx < lines.length && (/^\s*$/.test(lines[idx]) || /^\s*\/.\/.*/.test(lines[idx]) || /^\s*\/.\*/.test(lines[idx])))
20694
+ idx++;
20695
+ const start = idx;
20696
+ const imports = [];
20697
+ while (idx < lines.length && /^\s*import\b/.test(lines[idx])) {
20698
+ imports.push(lines[idx].trim());
20699
+ idx++;
20700
+ }
20701
+ if (imports.length === 0)
20702
+ return [];
20703
+ const block = imports.join(`
20704
+ `);
20705
+ const rest = lines.slice(idx).join(`
20706
+ `);
20707
+ const reconstructed = `${block}
20708
+ ${rest}`;
20709
+ const formatted = formatImports(reconstructed);
20710
+ if (formatted !== reconstructed) {
20711
+ return [{ filePath: ctx.filePath, line: start + 1, column: 1, ruleId: "sort-imports", message: "Imports are not sorted/grouped consistently", severity: "warning" }];
20712
+ }
20713
+ return [];
20714
+ },
20715
+ fix: (text) => formatImports(text)
20716
+ };
20717
+ });
20718
+
20719
+ // src/plugins/perfectionist.ts
20720
+ var perfectionistPlugin;
20721
+ var init_perfectionist = __esm(() => {
20722
+ init_imports();
20723
+ init_utils();
20724
+ perfectionistPlugin = {
20725
+ name: "perfectionist",
20726
+ rules: {
20727
+ "sort-imports": codeOnly(sortImportsRule)
20728
+ }
20729
+ };
20730
+ });
20731
+
20732
+ // src/rules/publint/utils.ts
20733
+ import { dirname as dirname7, resolve as resolve11 } from "path";
20734
+ function parsePackageJson(content) {
20735
+ try {
20736
+ const pkg = JSON.parse(content);
20737
+ if (typeof pkg !== "object" || pkg === null || Array.isArray(pkg))
20738
+ return null;
20739
+ return pkg;
20740
+ } catch {
20741
+ return null;
20742
+ }
20743
+ }
20744
+ function findJsonKeyLine(content, keyPath) {
20745
+ const lines = content.split(`
20746
+ `);
20747
+ let bestLine = 1;
20748
+ let searchStart = 0;
20749
+ for (const key of keyPath) {
20750
+ const keyPattern = new RegExp(`"${escapeRegex(key)}"\\s*:`);
20751
+ for (let i = searchStart;i < lines.length; i++) {
20752
+ if (keyPattern.test(lines[i])) {
20753
+ bestLine = i + 1;
20754
+ searchStart = i + 1;
20755
+ break;
20756
+ }
20757
+ }
20758
+ }
20759
+ return bestLine;
20760
+ }
20761
+ function getPkgDir(filePath) {
20762
+ return dirname7(filePath);
20763
+ }
20764
+ function resolvePkgPath(pkgDir, relativePath) {
20765
+ return resolve11(pkgDir, relativePath);
20766
+ }
20767
+ function formatPkgPath(path2) {
20768
+ let formatted = "pkg";
20769
+ for (const part of path2) {
20770
+ if (/^\d+$/.test(part)) {
20771
+ formatted += `[${part}]`;
20772
+ } else if (!/^[a-z_$][a-z0-9_$]*$/i.test(part)) {
20773
+ formatted += `["${part}"]`;
20774
+ } else {
20775
+ formatted += `.${part}`;
20776
+ }
20777
+ }
20778
+ return formatted;
20779
+ }
20780
+ function getPublishedField(pkg, field) {
20781
+ if (pkg.publishConfig?.[field] !== undefined) {
20782
+ return [pkg.publishConfig[field], ["publishConfig", field]];
20783
+ }
20784
+ return [pkg[field], [field]];
20785
+ }
20786
+ function getCodeFormat(code) {
20787
+ const stripped = stripComments(code);
20788
+ const isEsm = ESM_CONTENT_RE.test(stripped);
20789
+ const isCjs = CJS_CONTENT_RE.test(stripped);
20790
+ if (isEsm && isCjs)
20791
+ return "mixed";
20792
+ if (isEsm)
20793
+ return "ESM";
20794
+ if (isCjs)
20795
+ return "CJS";
20796
+ return "unknown";
20797
+ }
20798
+ function getFilePathFormat(filePath, pkgType) {
20799
+ if (filePath.endsWith(".mjs"))
20800
+ return "ESM";
20801
+ if (filePath.endsWith(".cjs"))
20802
+ return "CJS";
20803
+ return pkgType === "module" ? "ESM" : "CJS";
20804
+ }
20805
+ function getCodeFormatExtension(format) {
20806
+ if (format === "ESM")
20807
+ return ".mjs";
20808
+ if (format === "CJS")
20809
+ return ".cjs";
20810
+ return ".js";
20811
+ }
20812
+ function isExplicitExtension(ext) {
20813
+ return ext === ".mjs" || ext === ".cjs";
20814
+ }
20815
+ function isLintableFilePath(filePath) {
20816
+ return filePath.endsWith(".js") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs");
20817
+ }
20818
+ function startsWithShebang(code) {
20819
+ return /^#!\s*\//.test(code);
20820
+ }
20821
+ function stripComments(code) {
20822
+ return code.replace(/\/\*(.|[\r\n])*?\*\//gm, "").replace(/\/\/.*/g, "");
20823
+ }
20824
+ function crawlExportsOrImports(value, basePath, isImports, visitor) {
20825
+ _crawl(value, basePath, isImports, visitor);
20826
+ }
20827
+ function _crawl(value, currentPath, isImports, visitor) {
20828
+ if (typeof value === "string") {
20829
+ visitor(value, { path: currentPath, isImports });
20830
+ return;
20831
+ }
20832
+ if (Array.isArray(value)) {
20833
+ visitor(value, { path: currentPath, isImports });
20834
+ for (let i = 0;i < value.length; i++) {
20835
+ _crawl(value[i], currentPath.concat(String(i)), isImports, visitor);
20836
+ }
20837
+ return;
20838
+ }
20839
+ if (value !== null && typeof value === "object") {
20840
+ const keys = Object.keys(value);
20841
+ visitor(value, { path: currentPath, isImports }, keys);
20842
+ for (const key of keys) {
20843
+ _crawl(value[key], currentPath.concat(key), isImports, visitor);
20844
+ }
20845
+ }
20846
+ }
20847
+ function createIssue(filePath, content, keyPath, ruleId, message, severity, help) {
20848
+ return {
20849
+ filePath,
20850
+ line: findJsonKeyLine(content, keyPath),
20851
+ column: 1,
20852
+ ruleId,
20853
+ message,
20854
+ severity,
20855
+ ...help && { help }
20856
+ };
20857
+ }
20858
+ function escapeRegex(str) {
20859
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20860
+ }
20861
+ var ESM_CONTENT_RE, CJS_CONTENT_RE;
20862
+ var init_utils2 = __esm(() => {
20863
+ ESM_CONTENT_RE = /([\s;]|^)(import[\w,{}\s*]*from|import\s*['"*{]|export\b\s*(?:[*{]|default|type|function|const|var|let|async function)|import\.meta\b)/m;
20864
+ CJS_CONTENT_RE = /([\s;]|^)(module.exports\b|exports\.\w|require\s*\(|global\.\w|Object\.(defineProperty|defineProperties|assign)\s*\(\s*exports\b)/m;
20865
+ });
20866
+
20867
+ // src/rules/publint/use-type.ts
20868
+ var useType;
20869
+ var init_use_type = __esm(() => {
20870
+ init_utils2();
20871
+ useType = {
20872
+ meta: {
20873
+ docs: 'Suggest specifying the "type" field in package.json',
20874
+ recommended: true
20875
+ },
20876
+ check(content, context) {
20877
+ const pkg = parsePackageJson(content);
20878
+ if (!pkg)
20879
+ return [];
20880
+ if (pkg.type == null) {
20881
+ return [createIssue(context.filePath, content, ["name"], "publint/use-type", 'The package does not specify the "type" field. Node.js may attempt to detect the package type causing a small performance hit. Consider adding "type": "commonjs" or "type": "module".', "warning", 'Adding "type" helps Node.js resolve module format without detection overhead.')];
20882
+ }
20883
+ return [];
20884
+ }
20885
+ };
20886
+ });
20887
+
20888
+ // src/rules/publint/local-dependency.ts
20889
+ var localDependency;
20890
+ var init_local_dependency = __esm(() => {
20891
+ init_utils2();
20892
+ localDependency = {
20893
+ meta: {
20894
+ docs: "Disallow local file references in dependencies",
20895
+ recommended: true
20896
+ },
20897
+ check(content, context) {
20898
+ const pkg = parsePackageJson(content);
20899
+ if (!pkg)
20900
+ return [];
20901
+ const issues = [];
20902
+ if (pkg.dependencies && typeof pkg.dependencies === "object") {
20903
+ for (const depName of Object.keys(pkg.dependencies)) {
20904
+ const depVersion = pkg.dependencies[depName];
20905
+ if (typeof depVersion === "string" && (depVersion.startsWith("file:") || depVersion.startsWith("link:"))) {
20906
+ issues.push(createIssue(context.filePath, content, ["dependencies", depName], "publint/local-dependency", `The "${depName}" dependency references "${depVersion}" that will likely not work when installed by end-users.`, "error", "Local file references (file: or link:) are not portable and will fail for consumers of the package."));
20907
+ }
20908
+ }
20909
+ }
20910
+ return issues;
20911
+ }
20912
+ };
20913
+ });
20914
+
20915
+ // src/rules/publint/deprecated-field-jsnext.ts
20916
+ var deprecatedFieldJsnext;
20917
+ var init_deprecated_field_jsnext = __esm(() => {
20918
+ init_utils2();
20919
+ deprecatedFieldJsnext = {
20920
+ meta: {
20921
+ docs: "Disallow deprecated jsnext:main and jsnext fields",
20922
+ recommended: true
20923
+ },
20924
+ check(content, context) {
20925
+ const pkg = parsePackageJson(content);
20926
+ if (!pkg)
20927
+ return [];
20928
+ const issues = [];
20929
+ if (pkg["jsnext:main"] != null) {
20930
+ issues.push(createIssue(context.filePath, content, ["jsnext:main"], "publint/deprecated-field-jsnext", 'pkg["jsnext:main"] is deprecated. pkg.module should be used instead.', "warning", 'The jsnext:main field is no longer recognized by modern bundlers. Use "module" instead.'));
20931
+ }
20932
+ if (pkg.jsnext != null) {
20933
+ issues.push(createIssue(context.filePath, content, ["jsnext"], "publint/deprecated-field-jsnext", "pkg.jsnext is deprecated. pkg.module should be used instead.", "warning", 'The jsnext field is no longer recognized by modern bundlers. Use "module" instead.'));
20934
+ }
20935
+ return issues;
20936
+ }
20937
+ };
20938
+ });
20939
+
20940
+ // src/rules/publint/has-module-but-no-exports.ts
20941
+ var hasModuleButNoExports;
20942
+ var init_has_module_but_no_exports = __esm(() => {
20943
+ init_utils2();
20944
+ hasModuleButNoExports = {
20945
+ meta: {
20946
+ docs: "Suggest adding exports when module field is present",
20947
+ recommended: true
20948
+ },
20949
+ check(content, context) {
20950
+ const pkg = parsePackageJson(content);
20951
+ if (!pkg)
20952
+ return [];
20953
+ const [moduleValue, modulePath] = getPublishedField(pkg, "module");
20954
+ const [exportsValue] = getPublishedField(pkg, "exports");
20955
+ if (moduleValue != null && exportsValue == null) {
20956
+ return [createIssue(context.filePath, content, modulePath, "publint/has-module-but-no-exports", "pkg.module is used to output ESM, but pkg.exports is not defined. As Node.js doesn't read pkg.module, the ESM output may be skipped. Consider adding pkg.exports to export the ESM output.", "warning", 'Node.js only reads "exports" for module resolution. Without it, the ESM entry point in "module" is ignored by Node.js.')];
20957
+ }
20958
+ return [];
20959
+ }
20960
+ };
20961
+ });
20962
+
20963
+ // src/rules/publint/field-invalid-value-type.ts
20964
+ var FIELD_TYPE_CHECKS, fieldInvalidValueType;
20965
+ var init_field_invalid_value_type = __esm(() => {
20966
+ init_utils2();
20967
+ FIELD_TYPE_CHECKS = [
20968
+ { field: "main", expectTypes: ["string"] },
20969
+ { field: "module", expectTypes: ["string"] },
20970
+ { field: "types", expectTypes: ["string"] },
20971
+ { field: "typings", expectTypes: ["string"] },
20972
+ { field: "browser", expectTypes: ["string", "object"] },
20973
+ { field: "bin", expectTypes: ["string", "object"] },
20974
+ { field: "exports", expectTypes: ["string", "object"] },
20975
+ { field: "imports", expectTypes: ["object"] },
20976
+ { field: "name", expectTypes: ["string"] },
20977
+ { field: "version", expectTypes: ["string"] },
20978
+ { field: "description", expectTypes: ["string"] },
20979
+ { field: "license", expectTypes: ["string"] }
20980
+ ];
20981
+ fieldInvalidValueType = {
20982
+ meta: {
20983
+ docs: "Ensure package.json fields have correct value types",
20984
+ recommended: true
20985
+ },
20986
+ check(content, context) {
20987
+ const pkg = parsePackageJson(content);
20988
+ if (!pkg)
20989
+ return [];
20990
+ const issues = [];
20991
+ for (const { field, expectTypes } of FIELD_TYPE_CHECKS) {
20992
+ const [value, path2] = getPublishedField(pkg, field);
20993
+ if (value == null)
20994
+ continue;
20995
+ const actualType = typeof value;
20996
+ if (!expectTypes.includes(actualType)) {
20997
+ const expectStr = expectTypes.length === 1 ? expectTypes[0] : `${expectTypes.slice(0, -1).join(", ")} or ${expectTypes[expectTypes.length - 1]}`;
20998
+ issues.push(createIssue(context.filePath, content, path2, "publint/field-invalid-value-type", `${formatPkgPath(path2)} has an invalid ${actualType} type. Expected a ${expectStr} type instead.`, "error", `The "${field}" field must be of type ${expectStr}.`));
20999
+ }
21000
+ }
21001
+ return issues;
21002
+ }
21003
+ };
21004
+ });
21005
+
21006
+ // src/rules/publint/exports-types-should-be-first.ts
21007
+ function objectHasKeyNested(obj, key) {
21008
+ for (const k in obj) {
21009
+ if (k === key)
21010
+ return true;
21011
+ if (typeof obj[k] === "object" && obj[k] !== null && objectHasKeyNested(obj[k], key))
21012
+ return true;
21013
+ }
21014
+ return false;
21015
+ }
21016
+ var exportsTypesShouldBeFirst;
21017
+ var init_exports_types_should_be_first = __esm(() => {
21018
+ init_utils2();
21019
+ exportsTypesShouldBeFirst = {
21020
+ meta: {
21021
+ docs: 'Ensure "types" condition is first in exports objects',
21022
+ recommended: true
21023
+ },
21024
+ check(content, context) {
21025
+ const pkg = parsePackageJson(content);
21026
+ if (!pkg)
21027
+ return [];
21028
+ const issues = [];
21029
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21030
+ if (exportsValue == null)
21031
+ return [];
21032
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx, objectKeys) => {
21033
+ if (!objectKeys || typeof value !== "object" || Array.isArray(value))
21034
+ return;
21035
+ if (!("types" in value))
21036
+ return;
21037
+ const typesIndex = objectKeys.indexOf("types");
21038
+ if (typesIndex === 0)
21039
+ return;
21040
+ const precedingKeys = objectKeys.slice(0, typesIndex).filter((key) => {
21041
+ if (key.startsWith("types"))
21042
+ return false;
21043
+ if (typeof value[key] === "object" && value[key] !== null && objectHasKeyNested(value[key], "types")) {
21044
+ return false;
21045
+ }
21046
+ return true;
21047
+ });
21048
+ if (precedingKeys.length > 0) {
21049
+ issues.push(createIssue(context.filePath, content, ctx.path.concat("types"), "publint/exports-types-should-be-first", `${formatPkgPath(ctx.path.concat("types"))} should be the first condition in the object so it can be resolved by TypeScript.`, "error", 'TypeScript resolves conditions in order. The "types" condition must come first to ensure correct type resolution.'));
21050
+ }
21051
+ });
21052
+ return issues;
21053
+ }
21054
+ };
21055
+ });
21056
+
21057
+ // src/rules/publint/exports-default-should-be-last.ts
21058
+ var exportsDefaultShouldBeLast;
21059
+ var init_exports_default_should_be_last = __esm(() => {
21060
+ init_utils2();
21061
+ exportsDefaultShouldBeLast = {
21062
+ meta: {
21063
+ docs: 'Ensure "default" condition is last in exports objects',
21064
+ recommended: true
21065
+ },
21066
+ check(content, context) {
21067
+ const pkg = parsePackageJson(content);
21068
+ if (!pkg)
21069
+ return [];
21070
+ const issues = [];
21071
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21072
+ if (exportsValue == null)
21073
+ return [];
21074
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx, objectKeys) => {
21075
+ if (!objectKeys || typeof value !== "object" || Array.isArray(value))
21076
+ return;
21077
+ if (!("default" in value))
21078
+ return;
21079
+ if (objectKeys[objectKeys.length - 1] !== "default") {
21080
+ issues.push(createIssue(context.filePath, content, ctx.path.concat("default"), "publint/exports-default-should-be-last", `${formatPkgPath(ctx.path.concat("default"))} should be the last condition in the object so it doesn't take precedence over the keys following it.`, "error", 'Conditions are resolved in order. "default" acts as a fallback and should come last.'));
21081
+ }
21082
+ });
21083
+ return issues;
21084
+ }
21085
+ };
21086
+ });
21087
+
21088
+ // src/rules/publint/exports-module-should-precede-require.ts
21089
+ var exportsModuleShouldPrecedeRequire;
21090
+ var init_exports_module_should_precede_require = __esm(() => {
21091
+ init_utils2();
21092
+ exportsModuleShouldPrecedeRequire = {
21093
+ meta: {
21094
+ docs: 'Ensure "module" condition precedes "require" in exports',
21095
+ recommended: true
21096
+ },
21097
+ check(content, context) {
21098
+ const pkg = parsePackageJson(content);
21099
+ if (!pkg)
21100
+ return [];
21101
+ const issues = [];
21102
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21103
+ if (exportsValue == null)
21104
+ return [];
21105
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx, objectKeys) => {
21106
+ if (!objectKeys || typeof value !== "object" || Array.isArray(value))
21107
+ return;
21108
+ if (!("module" in value) || !("require" in value))
21109
+ return;
21110
+ if (objectKeys.indexOf("module") > objectKeys.indexOf("require")) {
21111
+ issues.push(createIssue(context.filePath, content, ctx.path.concat("module"), "publint/exports-module-should-precede-require", `${formatPkgPath(ctx.path.concat("module"))} should come before the "require" condition so it can take precedence when used by a bundler.`, "error", 'Bundlers prioritize conditions by order. "module" should precede "require" to ensure ESM is preferred.'));
21112
+ }
21113
+ });
21114
+ return issues;
21115
+ }
21116
+ };
21117
+ });
21118
+
21119
+ // src/rules/publint/exports-value-invalid.ts
21120
+ var exportsValueInvalid;
21121
+ var init_exports_value_invalid = __esm(() => {
21122
+ init_utils2();
21123
+ exportsValueInvalid = {
21124
+ meta: {
21125
+ docs: 'Ensure exports values start with "./"',
21126
+ recommended: true
21127
+ },
21128
+ check(content, context) {
21129
+ const pkg = parsePackageJson(content);
21130
+ if (!pkg)
21131
+ return [];
21132
+ const issues = [];
21133
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21134
+ if (exportsValue == null)
21135
+ return [];
21136
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx) => {
21137
+ if (typeof value !== "string")
21138
+ return;
21139
+ if (value.startsWith("./"))
21140
+ return;
21141
+ const suggestValue = "./" + value.replace(/^[/]+/, "");
21142
+ issues.push(createIssue(context.filePath, content, ctx.path, "publint/exports-value-invalid", `${formatPkgPath(ctx.path)} is "${value}" which is invalid as it does not start with "./". Use "${suggestValue}" instead.`, "error", 'All exports values must be relative paths starting with "./".'));
21143
+ });
21144
+ return issues;
21145
+ }
21146
+ };
21147
+ });
21148
+
21149
+ // src/rules/publint/exports-missing-root-entrypoint.ts
21150
+ var exportsMissingRootEntrypoint;
21151
+ var init_exports_missing_root_entrypoint = __esm(() => {
21152
+ init_utils2();
21153
+ exportsMissingRootEntrypoint = {
21154
+ meta: {
21155
+ docs: "Ensure exports has a root entrypoint when main/module exist",
21156
+ recommended: true
21157
+ },
21158
+ check(content, context) {
21159
+ const pkg = parsePackageJson(content);
21160
+ if (!pkg)
21161
+ return [];
21162
+ const [mainValue] = getPublishedField(pkg, "main");
21163
+ const [moduleValue] = getPublishedField(pkg, "module");
21164
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21165
+ if (mainValue == null && moduleValue == null || exportsValue == null)
21166
+ return [];
21167
+ if (typeof exportsValue !== "object" || Array.isArray(exportsValue))
21168
+ return [];
21169
+ const exportsKeys = Object.keys(exportsValue);
21170
+ if (exportsKeys.length === 0)
21171
+ return [];
21172
+ const hasSubpathKeys = exportsKeys[0]?.startsWith(".");
21173
+ if (!hasSubpathKeys)
21174
+ return [];
21175
+ if (exportsKeys.includes("."))
21176
+ return [];
21177
+ const mainFields = [];
21178
+ if (mainValue)
21179
+ mainFields.push("main");
21180
+ if (moduleValue)
21181
+ mainFields.push("module");
21182
+ return [createIssue(context.filePath, content, exportsPath, "publint/exports-missing-root-entrypoint", `${formatPkgPath(exportsPath)} is missing the root entrypoint export, which is defined in pkg.${mainFields[0]}. Environments that support "exports" will ignore pkg.${mainFields[0]} as "exports" takes the highest priority.`, "warning", `Add ${formatPkgPath(exportsPath.concat("."))} to ensure the root entrypoint is accessible.`)];
21183
+ }
21184
+ };
21185
+ });
21186
+
21187
+ // src/rules/publint/exports-fallback-array-use.ts
21188
+ var exportsFallbackArrayUse;
21189
+ var init_exports_fallback_array_use = __esm(() => {
21190
+ init_utils2();
21191
+ exportsFallbackArrayUse = {
21192
+ meta: {
21193
+ docs: "Warn against using fallback arrays in exports",
21194
+ recommended: true
21195
+ },
21196
+ check(content, context) {
21197
+ const pkg = parsePackageJson(content);
21198
+ if (!pkg)
21199
+ return [];
21200
+ const issues = [];
21201
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21202
+ if (exportsValue == null)
21203
+ return [];
21204
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx) => {
21205
+ if (!Array.isArray(value))
21206
+ return;
21207
+ issues.push(createIssue(context.filePath, content, ctx.path, "publint/exports-fallback-array-use", `${formatPkgPath(ctx.path)} uses fallback arrays which is not recommended. It works differently in some tools and may face inconsistent behaviors.`, "warning", "Fallback arrays pick the first parseable value and have no practical use case in Node.js currently."));
21208
+ });
21209
+ return issues;
21210
+ }
21211
+ };
21212
+ });
21213
+
21214
+ // src/rules/publint/imports-key-invalid.ts
21215
+ var importsKeyInvalid;
21216
+ var init_imports_key_invalid = __esm(() => {
21217
+ init_utils2();
21218
+ importsKeyInvalid = {
21219
+ meta: {
21220
+ docs: 'Ensure imports keys start with "#"',
21221
+ recommended: true
21222
+ },
21223
+ check(content, context) {
21224
+ const pkg = parsePackageJson(content);
21225
+ if (!pkg)
21226
+ return [];
21227
+ const issues = [];
21228
+ const [importsValue, importsPath] = getPublishedField(pkg, "imports");
21229
+ if (importsValue == null || typeof importsValue !== "object" || Array.isArray(importsValue))
21230
+ return [];
21231
+ for (const key of Object.keys(importsValue)) {
21232
+ if (!key.startsWith("#")) {
21233
+ const suggestKey = "#" + key.replace(/^[/]+/, "");
21234
+ const path2 = importsPath.concat(key);
21235
+ issues.push(createIssue(context.filePath, content, path2, "publint/imports-key-invalid", `${formatPkgPath(path2)} is invalid as the imports key does not start with "#". Use "${suggestKey}" instead.`, "error", 'All imports keys must start with "#" to distinguish them from package specifiers.'));
21236
+ }
21237
+ }
21238
+ return issues;
21239
+ }
21240
+ };
21241
+ });
21242
+
21243
+ // src/rules/publint/imports-value-invalid.ts
21244
+ var importsValueInvalid;
21245
+ var init_imports_value_invalid = __esm(() => {
21246
+ init_utils2();
21247
+ importsValueInvalid = {
21248
+ meta: {
21249
+ docs: 'Ensure imports values start with "./"',
21250
+ recommended: true
21251
+ },
21252
+ check(content, context) {
21253
+ const pkg = parsePackageJson(content);
21254
+ if (!pkg)
21255
+ return [];
21256
+ const issues = [];
21257
+ const [importsValue, importsPath] = getPublishedField(pkg, "imports");
21258
+ if (importsValue == null || typeof importsValue !== "object")
21259
+ return [];
21260
+ crawlExportsOrImports(importsValue, importsPath, true, (value, ctx) => {
21261
+ if (typeof value !== "string")
21262
+ return;
21263
+ if (!value.startsWith("."))
21264
+ return;
21265
+ if (value.startsWith("./"))
21266
+ return;
21267
+ const suggestValue = "./" + value.replace(/^[/]+/, "");
21268
+ issues.push(createIssue(context.filePath, content, ctx.path, "publint/imports-value-invalid", `${formatPkgPath(ctx.path)} is "${value}" which is invalid as it does not start with "./". Use "${suggestValue}" instead.`, "error", 'Relative imports values must start with "./".'));
21269
+ });
21270
+ return issues;
21271
+ }
21272
+ };
21273
+ });
21274
+
21275
+ // src/rules/publint/imports-default-should-be-last.ts
21276
+ var importsDefaultShouldBeLast;
21277
+ var init_imports_default_should_be_last = __esm(() => {
21278
+ init_utils2();
21279
+ importsDefaultShouldBeLast = {
21280
+ meta: {
21281
+ docs: 'Ensure "default" condition is last in imports objects',
21282
+ recommended: true
21283
+ },
21284
+ check(content, context) {
21285
+ const pkg = parsePackageJson(content);
21286
+ if (!pkg)
21287
+ return [];
21288
+ const issues = [];
21289
+ const [importsValue, importsPath] = getPublishedField(pkg, "imports");
21290
+ if (importsValue == null || typeof importsValue !== "object")
21291
+ return [];
21292
+ crawlExportsOrImports(importsValue, importsPath, true, (value, ctx, objectKeys) => {
21293
+ if (!objectKeys || typeof value !== "object" || Array.isArray(value))
21294
+ return;
21295
+ if (!("default" in value))
21296
+ return;
21297
+ if (objectKeys[objectKeys.length - 1] !== "default") {
21298
+ issues.push(createIssue(context.filePath, content, ctx.path.concat("default"), "publint/imports-default-should-be-last", `${formatPkgPath(ctx.path.concat("default"))} should be the last condition in the object so it doesn't take precedence over the keys following it.`, "error", 'Conditions are resolved in order. "default" acts as a fallback and should come last.'));
21299
+ }
21300
+ });
21301
+ return issues;
21302
+ }
21303
+ };
21304
+ });
21305
+
21306
+ // src/rules/publint/imports-module-should-precede-require.ts
21307
+ var importsModuleShouldPrecedeRequire;
21308
+ var init_imports_module_should_precede_require = __esm(() => {
21309
+ init_utils2();
21310
+ importsModuleShouldPrecedeRequire = {
21311
+ meta: {
21312
+ docs: 'Ensure "module" condition precedes "require" in imports',
21313
+ recommended: true
21314
+ },
21315
+ check(content, context) {
21316
+ const pkg = parsePackageJson(content);
21317
+ if (!pkg)
21318
+ return [];
21319
+ const issues = [];
21320
+ const [importsValue, importsPath] = getPublishedField(pkg, "imports");
21321
+ if (importsValue == null || typeof importsValue !== "object")
21322
+ return [];
21323
+ crawlExportsOrImports(importsValue, importsPath, true, (value, ctx, objectKeys) => {
21324
+ if (!objectKeys || typeof value !== "object" || Array.isArray(value))
21325
+ return;
21326
+ if (!("module" in value) || !("require" in value))
21327
+ return;
21328
+ if (objectKeys.indexOf("module") > objectKeys.indexOf("require")) {
21329
+ issues.push(createIssue(context.filePath, content, ctx.path.concat("module"), "publint/imports-module-should-precede-require", `${formatPkgPath(ctx.path.concat("module"))} should come before the "require" condition so it can take precedence when used by a bundler.`, "error", 'Bundlers prioritize conditions by order. "module" should precede "require" to ensure ESM is preferred.'));
21330
+ }
21331
+ });
21332
+ return issues;
21333
+ }
21334
+ };
21335
+ });
21336
+
21337
+ // src/rules/publint/file-does-not-exist.ts
21338
+ import { existsSync as existsSync9 } from "fs";
21339
+ function checkFileRef(value, path2, issues, filePath, content, pkgDir) {
21340
+ const resolved = resolvePkgPath(pkgDir, value);
21341
+ if (!fileExistsWithFallbacks(resolved)) {
21342
+ issues.push(createIssue(filePath, content, path2, "publint/file-does-not-exist", `${formatPkgPath(path2)} is "${value}" but the file does not exist.`, "error", "The referenced file path cannot be found. Check the path for typos."));
21343
+ }
21344
+ }
21345
+ function fileExistsWithFallbacks(resolved) {
21346
+ if (existsSync9(resolved))
21347
+ return true;
21348
+ if (existsSync9(resolved + ".js"))
21349
+ return true;
21350
+ if (existsSync9(resolved + "/index.js"))
21351
+ return true;
21352
+ return false;
21353
+ }
21354
+ var fileDoesNotExist;
21355
+ var init_file_does_not_exist = __esm(() => {
21356
+ init_utils2();
21357
+ fileDoesNotExist = {
21358
+ meta: {
21359
+ docs: "Ensure files referenced in package.json exist",
21360
+ recommended: true
21361
+ },
21362
+ check(content, context) {
21363
+ const pkg = parsePackageJson(content);
21364
+ if (!pkg)
21365
+ return [];
21366
+ const issues = [];
21367
+ const pkgDir = getPkgDir(context.filePath);
21368
+ const stringFields = ["main", "module", "types", "typings", "unpkg", "jsdelivr"];
21369
+ for (const field of stringFields) {
21370
+ const [value, path2] = getPublishedField(pkg, field);
21371
+ if (value == null || typeof value !== "string")
21372
+ continue;
21373
+ const resolved = resolvePkgPath(pkgDir, value);
21374
+ if (!fileExistsWithFallbacks(resolved)) {
21375
+ issues.push(createIssue(context.filePath, content, path2, "publint/file-does-not-exist", `${formatPkgPath(path2)} is "${value}" but the file does not exist.`, "error", "The referenced file path cannot be found. Check the path for typos."));
21376
+ }
21377
+ }
21378
+ const [binValue, binPath] = getPublishedField(pkg, "bin");
21379
+ if (binValue != null) {
21380
+ if (typeof binValue === "string") {
21381
+ checkFileRef(binValue, binPath, issues, context.filePath, content, pkgDir);
21382
+ } else if (typeof binValue === "object") {
21383
+ for (const key of Object.keys(binValue)) {
21384
+ if (typeof binValue[key] === "string") {
21385
+ checkFileRef(binValue[key], binPath.concat(key), issues, context.filePath, content, pkgDir);
21386
+ }
21387
+ }
21388
+ }
21389
+ }
21390
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21391
+ if (exportsValue != null) {
21392
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx) => {
21393
+ if (typeof value !== "string")
21394
+ return;
21395
+ if (!value.startsWith("./") && !value.startsWith("../"))
21396
+ return;
21397
+ if (value.includes("*"))
21398
+ return;
21399
+ const resolved = resolvePkgPath(pkgDir, value);
21400
+ if (!existsSync9(resolved)) {
21401
+ issues.push(createIssue(context.filePath, content, ctx.path, "publint/file-does-not-exist", `${formatPkgPath(ctx.path)} is "${value}" but the file does not exist.`, "error", "The referenced file path cannot be found. Check the path for typos."));
21402
+ }
21403
+ });
21404
+ }
21405
+ return issues;
21406
+ }
21407
+ };
21408
+ });
21409
+
21410
+ // src/rules/publint/file-invalid-format.ts
21411
+ import { existsSync as existsSync11, readFileSync } from "fs";
21412
+ import { extname } from "path";
21413
+ function checkFileFormat(filePath, pkgPath, pkgDir, pkgType, issues, contextFilePath, content) {
21414
+ const resolved = resolvePkgPath(pkgDir, filePath);
21415
+ if (!existsSync11(resolved))
21416
+ return;
21417
+ if (!isLintableFilePath(filePath))
21418
+ return;
21419
+ let fileContent;
21420
+ try {
21421
+ fileContent = readFileSync(resolved, "utf8");
21422
+ } catch {
21423
+ return;
21424
+ }
21425
+ const actualFormat = getCodeFormat(fileContent);
21426
+ if (actualFormat === "unknown" || actualFormat === "mixed")
21427
+ return;
21428
+ const expectFormat = getFilePathFormat(resolved, pkgType);
21429
+ if (actualFormat === expectFormat)
21430
+ return;
21431
+ const actualExtension = extname(resolved);
21432
+ const expectExtension = getCodeFormatExtension(actualFormat);
21433
+ const code = isExplicitExtension(actualExtension) ? "FILE_INVALID_EXPLICIT_FORMAT" : "FILE_INVALID_FORMAT";
21434
+ const message = code === "FILE_INVALID_EXPLICIT_FORMAT" ? `${formatPkgPath(pkgPath)} is "${filePath}" and ends with the ${actualExtension} extension, but the code is written in ${actualFormat}. Consider using the ${expectExtension} extension.` : `${formatPkgPath(pkgPath)} is "${filePath}" and is written in ${actualFormat}, but is interpreted as ${expectFormat}. Consider using the ${expectExtension} extension.`;
21435
+ issues.push(createIssue(contextFilePath, content, pkgPath, "publint/file-invalid-format", message, "warning", `The file's code format (${actualFormat}) doesn't match the expected format (${expectFormat}) based on its extension and package type.`));
21436
+ }
21437
+ var fileInvalidFormat;
21438
+ var init_file_invalid_format = __esm(() => {
21439
+ init_utils2();
21440
+ fileInvalidFormat = {
21441
+ meta: {
21442
+ docs: "Ensure file code format matches its extension and package type",
21443
+ recommended: true
21444
+ },
21445
+ check(content, context) {
21446
+ const pkg = parsePackageJson(content);
21447
+ if (!pkg)
21448
+ return [];
21449
+ const issues = [];
21450
+ const pkgDir = getPkgDir(context.filePath);
21451
+ const pkgType = pkg.type;
21452
+ const [mainValue, mainPath] = getPublishedField(pkg, "main");
21453
+ if (mainValue != null && typeof mainValue === "string") {
21454
+ checkFileFormat(mainValue, mainPath, pkgDir, pkgType, issues, context.filePath, content);
21455
+ }
21456
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21457
+ if (exportsValue != null) {
21458
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx) => {
21459
+ if (typeof value !== "string")
21460
+ return;
21461
+ if (!value.startsWith("./") && !value.startsWith("../"))
21462
+ return;
21463
+ if (value.includes("*"))
21464
+ return;
21465
+ if (ctx.path.includes("browser"))
21466
+ return;
21467
+ checkFileFormat(value, ctx.path, pkgDir, pkgType, issues, context.filePath, content);
21468
+ });
21469
+ }
21470
+ return issues;
21471
+ }
21472
+ };
21473
+ });
21474
+
21475
+ // src/rules/publint/module-should-be-esm.ts
21476
+ import { existsSync as existsSync12, readFileSync as readFileSync2 } from "fs";
21477
+ var moduleShouldBeEsm;
21478
+ var init_module_should_be_esm = __esm(() => {
21479
+ init_utils2();
21480
+ moduleShouldBeEsm = {
21481
+ meta: {
21482
+ docs: 'Ensure the "module" field points to an ESM file',
21483
+ recommended: true
21484
+ },
21485
+ check(content, context) {
21486
+ const pkg = parsePackageJson(content);
21487
+ if (!pkg)
21488
+ return [];
21489
+ const [moduleValue, modulePath] = getPublishedField(pkg, "module");
21490
+ if (moduleValue == null || typeof moduleValue !== "string")
21491
+ return [];
21492
+ const pkgDir = getPkgDir(context.filePath);
21493
+ const resolved = resolvePkgPath(pkgDir, moduleValue);
21494
+ if (!existsSync12(resolved))
21495
+ return [];
21496
+ if (!isLintableFilePath(moduleValue))
21497
+ return [];
21498
+ let fileContent;
21499
+ try {
21500
+ fileContent = readFileSync2(resolved, "utf8");
21501
+ } catch {
21502
+ return [];
21503
+ }
21504
+ const actualFormat = getCodeFormat(fileContent);
21505
+ if (actualFormat === "CJS") {
21506
+ return [createIssue(context.filePath, content, modulePath, "publint/module-should-be-esm", `${formatPkgPath(modulePath)} should be ESM, but the code is written in CJS.`, "error", 'The "module" field is intended for ESM output used by bundlers. It should point to an ESM file.')];
21507
+ }
21508
+ return [];
20064
21509
  }
20065
21510
  };
20066
21511
  });
20067
21512
 
20068
- // src/plugins/node.ts
20069
- var nodePlugin;
20070
- var init_node = __esm(() => {
20071
- init_prefer_global_buffer();
20072
- init_prefer_global_process();
20073
- init_utils();
20074
- nodePlugin = {
20075
- name: "node",
20076
- rules: {
20077
- "prefer-global/buffer": codeOnly(preferGlobalBuffer),
20078
- "prefer-global/process": codeOnly(preferGlobalProcess)
21513
+ // src/rules/publint/bin-file-not-executable.ts
21514
+ import { existsSync as existsSync13, readFileSync as readFileSync3 } from "fs";
21515
+ function checkBin(filePath, pkgPath, pkgDir, issues, contextFilePath, content) {
21516
+ const resolved = resolvePkgPath(pkgDir, filePath);
21517
+ if (!existsSync13(resolved))
21518
+ return;
21519
+ if (!isLintableFilePath(filePath))
21520
+ return;
21521
+ let fileContent;
21522
+ try {
21523
+ fileContent = readFileSync3(resolved, "utf8");
21524
+ } catch {
21525
+ return;
21526
+ }
21527
+ if (!startsWithShebang(fileContent)) {
21528
+ issues.push(createIssue(contextFilePath, content, pkgPath, "publint/bin-file-not-executable", `${formatPkgPath(pkgPath)} is "${filePath}" but the file is not executable. It should start with a shebang, e.g. #!/usr/bin/env node.`, "error", "Bin files must start with a shebang (e.g. #!/usr/bin/env node) to be executable."));
21529
+ }
21530
+ }
21531
+ var binFileNotExecutable;
21532
+ var init_bin_file_not_executable = __esm(() => {
21533
+ init_utils2();
21534
+ binFileNotExecutable = {
21535
+ meta: {
21536
+ docs: "Ensure bin files have a shebang for executability",
21537
+ recommended: true
21538
+ },
21539
+ check(content, context) {
21540
+ const pkg = parsePackageJson(content);
21541
+ if (!pkg)
21542
+ return [];
21543
+ const issues = [];
21544
+ const pkgDir = getPkgDir(context.filePath);
21545
+ const [binValue, binPath] = getPublishedField(pkg, "bin");
21546
+ if (binValue == null)
21547
+ return [];
21548
+ if (typeof binValue === "string") {
21549
+ checkBin(binValue, binPath, pkgDir, issues, context.filePath, content);
21550
+ } else if (typeof binValue === "object") {
21551
+ for (const key of Object.keys(binValue)) {
21552
+ if (typeof binValue[key] === "string") {
21553
+ checkBin(binValue[key], binPath.concat(key), pkgDir, issues, context.filePath, content);
21554
+ }
21555
+ }
21556
+ }
21557
+ return issues;
20079
21558
  }
20080
21559
  };
20081
21560
  });
20082
21561
 
20083
- // src/rules/sort/imports.ts
20084
- var sortImportsRule;
20085
- var init_imports = __esm(() => {
20086
- init_format();
20087
- sortImportsRule = {
20088
- meta: { docs: "Enforce sorted imports (delegates to formatter check only)" },
20089
- check: (text, ctx) => {
20090
- const lines = text.split(/\r?\n/);
20091
- let idx = 0;
20092
- while (idx < lines.length && (/^\s*$/.test(lines[idx]) || /^\s*\/.\/.*/.test(lines[idx]) || /^\s*\/.\*/.test(lines[idx])))
20093
- idx++;
20094
- const start = idx;
20095
- const imports = [];
20096
- while (idx < lines.length && /^\s*import\b/.test(lines[idx])) {
20097
- imports.push(lines[idx].trim());
20098
- idx++;
20099
- }
20100
- if (imports.length === 0)
21562
+ // src/rules/publint/exports-module-should-be-esm.ts
21563
+ import { existsSync as existsSync14, readFileSync as readFileSync4 } from "fs";
21564
+ var exportsModuleShouldBeEsm;
21565
+ var init_exports_module_should_be_esm = __esm(() => {
21566
+ init_utils2();
21567
+ exportsModuleShouldBeEsm = {
21568
+ meta: {
21569
+ docs: 'Ensure files under "module" condition in exports are ESM',
21570
+ recommended: true
21571
+ },
21572
+ check(content, context) {
21573
+ const pkg = parsePackageJson(content);
21574
+ if (!pkg)
20101
21575
  return [];
20102
- const block = imports.join(`
20103
- `);
20104
- const rest = lines.slice(idx).join(`
20105
- `);
20106
- const reconstructed = `${block}
20107
- ${rest}`;
20108
- const formatted = formatImports(reconstructed);
20109
- if (formatted !== reconstructed) {
20110
- return [{ filePath: ctx.filePath, line: start + 1, column: 1, ruleId: "sort-imports", message: "Imports are not sorted/grouped consistently", severity: "warning" }];
21576
+ const issues = [];
21577
+ const pkgDir = getPkgDir(context.filePath);
21578
+ const [exportsValue, exportsPath] = getPublishedField(pkg, "exports");
21579
+ if (exportsValue == null)
21580
+ return [];
21581
+ crawlExportsOrImports(exportsValue, exportsPath, false, (value, ctx) => {
21582
+ if (typeof value !== "string")
21583
+ return;
21584
+ if (!ctx.path.includes("module"))
21585
+ return;
21586
+ if (!value.startsWith("./") && !value.startsWith("../"))
21587
+ return;
21588
+ if (value.includes("*"))
21589
+ return;
21590
+ const resolved = resolvePkgPath(pkgDir, value);
21591
+ if (!existsSync14(resolved))
21592
+ return;
21593
+ if (!isLintableFilePath(value))
21594
+ return;
21595
+ let fileContent;
21596
+ try {
21597
+ fileContent = readFileSync4(resolved, "utf8");
21598
+ } catch {
21599
+ return;
21600
+ }
21601
+ const actualFormat = getCodeFormat(fileContent);
21602
+ if (actualFormat === "CJS") {
21603
+ issues.push(createIssue(context.filePath, content, ctx.path, "publint/exports-module-should-be-esm", `${formatPkgPath(ctx.path)} should be ESM, but the code is written in CJS.`, "error", 'The "module" condition is only used by bundlers and must contain ESM code.'));
21604
+ }
21605
+ });
21606
+ const [importsValue, importsPath] = getPublishedField(pkg, "imports");
21607
+ if (importsValue != null && typeof importsValue === "object") {
21608
+ crawlExportsOrImports(importsValue, importsPath, true, (value, ctx) => {
21609
+ if (typeof value !== "string")
21610
+ return;
21611
+ if (!ctx.path.includes("module"))
21612
+ return;
21613
+ if (!value.startsWith("./"))
21614
+ return;
21615
+ if (value.includes("*"))
21616
+ return;
21617
+ const resolved = resolvePkgPath(pkgDir, value);
21618
+ if (!existsSync14(resolved))
21619
+ return;
21620
+ if (!isLintableFilePath(value))
21621
+ return;
21622
+ let fileContent;
21623
+ try {
21624
+ fileContent = readFileSync4(resolved, "utf8");
21625
+ } catch {
21626
+ return;
21627
+ }
21628
+ const actualFormat = getCodeFormat(fileContent);
21629
+ if (actualFormat === "CJS") {
21630
+ issues.push(createIssue(context.filePath, content, ctx.path, "publint/exports-module-should-be-esm", `${formatPkgPath(ctx.path)} should be ESM, but the code is written in CJS.`, "error", 'The "module" condition is only used by bundlers and must contain ESM code.'));
21631
+ }
21632
+ });
20111
21633
  }
20112
- return [];
20113
- },
20114
- fix: (text) => formatImports(text)
21634
+ return issues;
21635
+ }
20115
21636
  };
20116
21637
  });
20117
21638
 
20118
- // src/plugins/perfectionist.ts
20119
- var perfectionistPlugin;
20120
- var init_perfectionist = __esm(() => {
20121
- init_imports();
21639
+ // src/rules/publint/index.ts
21640
+ var init_publint = __esm(() => {
21641
+ init_use_type();
21642
+ init_local_dependency();
21643
+ init_deprecated_field_jsnext();
21644
+ init_has_module_but_no_exports();
21645
+ init_field_invalid_value_type();
21646
+ init_exports_types_should_be_first();
21647
+ init_exports_default_should_be_last();
21648
+ init_exports_module_should_precede_require();
21649
+ init_exports_value_invalid();
21650
+ init_exports_missing_root_entrypoint();
21651
+ init_exports_fallback_array_use();
21652
+ init_imports_key_invalid();
21653
+ init_imports_value_invalid();
21654
+ init_imports_default_should_be_last();
21655
+ init_imports_module_should_precede_require();
21656
+ init_file_does_not_exist();
21657
+ init_file_invalid_format();
21658
+ init_module_should_be_esm();
21659
+ init_bin_file_not_executable();
21660
+ init_exports_module_should_be_esm();
21661
+ });
21662
+
21663
+ // src/plugins/publint.ts
21664
+ var publintPlugin;
21665
+ var init_publint2 = __esm(() => {
21666
+ init_publint();
20122
21667
  init_utils();
20123
- perfectionistPlugin = {
20124
- name: "perfectionist",
21668
+ publintPlugin = {
21669
+ name: "publint",
20125
21670
  rules: {
20126
- "sort-imports": codeOnly(sortImportsRule)
21671
+ "use-type": packageJsonOnly(useType),
21672
+ "local-dependency": packageJsonOnly(localDependency),
21673
+ "deprecated-field-jsnext": packageJsonOnly(deprecatedFieldJsnext),
21674
+ "has-module-but-no-exports": packageJsonOnly(hasModuleButNoExports),
21675
+ "field-invalid-value-type": packageJsonOnly(fieldInvalidValueType),
21676
+ "exports-types-should-be-first": packageJsonOnly(exportsTypesShouldBeFirst),
21677
+ "exports-default-should-be-last": packageJsonOnly(exportsDefaultShouldBeLast),
21678
+ "exports-module-should-precede-require": packageJsonOnly(exportsModuleShouldPrecedeRequire),
21679
+ "exports-value-invalid": packageJsonOnly(exportsValueInvalid),
21680
+ "exports-missing-root-entrypoint": packageJsonOnly(exportsMissingRootEntrypoint),
21681
+ "exports-fallback-array-use": packageJsonOnly(exportsFallbackArrayUse),
21682
+ "imports-key-invalid": packageJsonOnly(importsKeyInvalid),
21683
+ "imports-value-invalid": packageJsonOnly(importsValueInvalid),
21684
+ "imports-default-should-be-last": packageJsonOnly(importsDefaultShouldBeLast),
21685
+ "imports-module-should-precede-require": packageJsonOnly(importsModuleShouldPrecedeRequire),
21686
+ "file-does-not-exist": packageJsonOnly(fileDoesNotExist),
21687
+ "file-invalid-format": packageJsonOnly(fileInvalidFormat),
21688
+ "module-should-be-esm": packageJsonOnly(moduleShouldBeEsm),
21689
+ "bin-file-not-executable": packageJsonOnly(binFileNotExecutable),
21690
+ "exports-module-should-be-esm": packageJsonOnly(exportsModuleShouldBeEsm)
20127
21691
  }
20128
21692
  };
20129
21693
  });
@@ -20199,8 +21763,8 @@ var init_import_dedupe = __esm(() => {
20199
21763
  });
20200
21764
 
20201
21765
  // src/rules/imports/named.ts
20202
- import { existsSync as existsSync9, readFileSync } from "fs";
20203
- import { dirname as dirname7, resolve as resolve11 } from "path";
21766
+ import { existsSync as existsSync15, readFileSync as readFileSync5 } from "fs";
21767
+ import { dirname as dirname8, resolve as resolve12 } from "path";
20204
21768
  var namedRule;
20205
21769
  var init_named = __esm(() => {
20206
21770
  namedRule = {
@@ -20211,7 +21775,7 @@ var init_named = __esm(() => {
20211
21775
  check: (text, ctx) => {
20212
21776
  const issues = [];
20213
21777
  const lines = text.split(/\r?\n/);
20214
- const currentDir = dirname7(ctx.filePath);
21778
+ const currentDir = dirname8(ctx.filePath);
20215
21779
  for (let i = 0;i < lines.length; i++) {
20216
21780
  const line = lines[i];
20217
21781
  const namedImportMatch = line.match(/\bimport\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
@@ -20224,9 +21788,9 @@ var init_named = __esm(() => {
20224
21788
  const extensions = [".ts", ".tsx", ".js", ".jsx", ""];
20225
21789
  let targetContent = "";
20226
21790
  for (const ext of extensions) {
20227
- const fullPath = resolve11(currentDir, importPath + ext);
20228
- if (existsSync9(fullPath)) {
20229
- targetContent = readFileSync(fullPath, "utf8");
21791
+ const fullPath = resolve12(currentDir, importPath + ext);
21792
+ if (existsSync15(fullPath)) {
21793
+ targetContent = readFileSync5(fullPath, "utf8");
20230
21794
  break;
20231
21795
  }
20232
21796
  }
@@ -20257,8 +21821,8 @@ var init_named = __esm(() => {
20257
21821
  });
20258
21822
 
20259
21823
  // src/rules/imports/no-cycle.ts
20260
- import { existsSync as existsSync11, readFileSync as readFileSync2 } from "fs";
20261
- import { dirname as dirname8, resolve as resolve12 } from "path";
21824
+ import { existsSync as existsSync16, readFileSync as readFileSync6 } from "fs";
21825
+ import { dirname as dirname9, resolve as resolve13 } from "path";
20262
21826
  var noCycleRule;
20263
21827
  var init_no_cycle = __esm(() => {
20264
21828
  noCycleRule = {
@@ -20282,8 +21846,8 @@ var init_no_cycle = __esm(() => {
20282
21846
  visited.add(filePath);
20283
21847
  stack.add(filePath);
20284
21848
  try {
20285
- const content = readFileSync2(filePath, "utf8");
20286
- const imports = extractImports(content, dirname8(filePath));
21849
+ const content = readFileSync6(filePath, "utf8");
21850
+ const imports = extractImports(content, dirname9(filePath));
20287
21851
  for (const imp of imports) {
20288
21852
  if (detectCycle(imp, [...importChain, imp])) {
20289
21853
  return true;
@@ -20302,8 +21866,8 @@ var init_no_cycle = __esm(() => {
20302
21866
  if (importPath.startsWith(".") || importPath.startsWith("/")) {
20303
21867
  const extensions = [".ts", ".tsx", ".js", ".jsx", ""];
20304
21868
  for (const ext of extensions) {
20305
- const fullPath = resolve12(baseDir, importPath + ext);
20306
- if (existsSync11(fullPath)) {
21869
+ const fullPath = resolve13(baseDir, importPath + ext);
21870
+ if (existsSync16(fullPath)) {
20307
21871
  imports.push(fullPath);
20308
21872
  break;
20309
21873
  }
@@ -20321,8 +21885,8 @@ var init_no_cycle = __esm(() => {
20321
21885
  const extensions = [".ts", ".tsx", ".js", ".jsx", ""];
20322
21886
  let resolvedPath = "";
20323
21887
  for (const ext of extensions) {
20324
- const fullPath = resolve12(dirname8(currentFile), importPath + ext);
20325
- if (existsSync11(fullPath)) {
21888
+ const fullPath = resolve13(dirname9(currentFile), importPath + ext);
21889
+ if (existsSync16(fullPath)) {
20326
21890
  resolvedPath = fullPath;
20327
21891
  break;
20328
21892
  }
@@ -20488,8 +22052,8 @@ var init_no_import_node_modules_by_path = __esm(() => {
20488
22052
  });
20489
22053
 
20490
22054
  // src/rules/imports/no-unresolved.ts
20491
- import { existsSync as existsSync12 } from "fs";
20492
- import { dirname as dirname9, join as join6, resolve as resolve13 } from "path";
22055
+ import { existsSync as existsSync17 } from "fs";
22056
+ import { dirname as dirname10, join as join6, resolve as resolve14 } from "path";
20493
22057
  var noUnresolvedRule;
20494
22058
  var init_no_unresolved = __esm(() => {
20495
22059
  noUnresolvedRule = {
@@ -20500,7 +22064,7 @@ var init_no_unresolved = __esm(() => {
20500
22064
  check: (text, ctx) => {
20501
22065
  const issues = [];
20502
22066
  const lines = text.split(/\r?\n/);
20503
- const currentDir = dirname9(ctx.filePath);
22067
+ const currentDir = dirname10(ctx.filePath);
20504
22068
  for (let i = 0;i < lines.length; i++) {
20505
22069
  const line = lines[i];
20506
22070
  const importMatches = [
@@ -20516,11 +22080,11 @@ var init_no_unresolved = __esm(() => {
20516
22080
  const possiblePaths = [];
20517
22081
  const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ""];
20518
22082
  for (const ext of extensions) {
20519
- const fullPath = resolve13(currentDir, importPath + ext);
22083
+ const fullPath = resolve14(currentDir, importPath + ext);
20520
22084
  possiblePaths.push(fullPath);
20521
22085
  possiblePaths.push(join6(fullPath, `index${ext}`));
20522
22086
  }
20523
- const resolved = possiblePaths.some((p) => existsSync12(p));
22087
+ const resolved = possiblePaths.some((p) => existsSync17(p));
20524
22088
  if (!resolved) {
20525
22089
  issues.push({
20526
22090
  filePath: ctx.filePath,
@@ -20693,7 +22257,7 @@ var init_heritage_clauses = __esm(() => {
20693
22257
  }
20694
22258
  return "unknown";
20695
22259
  };
20696
- const splitTopLevel = (src) => {
22260
+ const splitTopLevel2 = (src) => {
20697
22261
  const out = [];
20698
22262
  let depth = 0;
20699
22263
  let token = "";
@@ -20720,7 +22284,7 @@ var init_heritage_clauses = __esm(() => {
20720
22284
  return m ? m[1] : t;
20721
22285
  };
20722
22286
  const checkList = (lineNo, listSrc) => {
20723
- const items = splitTopLevel(listSrc);
22287
+ const items = splitTopLevel2(listSrc);
20724
22288
  if (items.length <= 1)
20725
22289
  return;
20726
22290
  const names = items.map(baseName);
@@ -20960,6 +22524,9 @@ var init_top_level_function = __esm(() => {
20960
22524
  // src/plugins/pickier.ts
20961
22525
  var pickierPlugin;
20962
22526
  var init_pickier = __esm(() => {
22527
+ init_no_unused_vars();
22528
+ init_prefer_const();
22529
+ init_prefer_template();
20963
22530
  init_first();
20964
22531
  init_import_dedupe();
20965
22532
  init_named();
@@ -20993,7 +22560,10 @@ var init_pickier = __esm(() => {
20993
22560
  "no-import-dist": codeOnly(noImportDistRule),
20994
22561
  "no-import-node-modules-by-path": codeOnly(noImportNodeModulesByPathRule),
20995
22562
  "no-duplicate-imports": codeOnly(noDuplicateImportsRule),
20996
- "top-level-function": codeOnly(topLevelFunctionRule)
22563
+ "top-level-function": codeOnly(topLevelFunctionRule),
22564
+ "prefer-const": codeOnly(preferConstRule),
22565
+ "prefer-template": codeOnly(preferTemplate),
22566
+ "no-unused-vars": codeOnly(noUnusedVarsRule)
20997
22567
  }
20998
22568
  };
20999
22569
  });
@@ -21094,7 +22664,7 @@ var init_no_super_linear_backtracking = __esm(() => {
21094
22664
  meta: { docs: "Detects potentially super-linear backtracking patterns in regex literals (heuristic)" },
21095
22665
  check: (text, ctx) => {
21096
22666
  const issues = [];
21097
- const regexLiteral = /\/[^/\\]*(?:\\.[^/\\]*)*\//g;
22667
+ const regexLiteral = /\/[^/\\\n]*(?:\\.[^/\\\n]*)*\//g;
21098
22668
  const mark = (idx, _len, msg) => {
21099
22669
  const before = text.slice(0, idx);
21100
22670
  const line = (before.match(/\n/g) || []).length + 1;
@@ -21102,10 +22672,34 @@ var init_no_super_linear_backtracking = __esm(() => {
21102
22672
  `);
21103
22673
  issues.push({ filePath: ctx.filePath, line, column: col, ruleId: "no-super-linear-backtracking", message: msg, severity: "error" });
21104
22674
  };
22675
+ const commentRanges = [];
22676
+ const blockComment = /\/\*[\s\S]*?\*\//g;
22677
+ let bc;
22678
+ while (bc = blockComment.exec(text)) {
22679
+ commentRanges.push({ start: bc.index, end: bc.index + bc[0].length });
22680
+ }
22681
+ const lineComment = /\/\/[^\n]*/g;
22682
+ let lc;
22683
+ while (lc = lineComment.exec(text)) {
22684
+ commentRanges.push({ start: lc.index, end: lc.index + lc[0].length });
22685
+ }
21105
22686
  let m;
21106
22687
  while (m = regexLiteral.exec(text)) {
21107
22688
  const literal = m[0];
21108
22689
  const idx = m.index;
22690
+ if (commentRanges.some((r) => idx >= r.start && idx < r.end)) {
22691
+ continue;
22692
+ }
22693
+ if (idx > 0) {
22694
+ const prevChar = text[idx - 1];
22695
+ if (prevChar === ")" || prevChar === "]" || prevChar === "." || /[\w$]/.test(prevChar)) {
22696
+ continue;
22697
+ }
22698
+ const inner = literal.slice(1, literal.lastIndexOf("/"));
22699
+ if (/^\s+\w/.test(inner) && /\w\s*\)/.test(inner)) {
22700
+ continue;
22701
+ }
22702
+ }
21109
22703
  const patt = literal.slice(1, literal.lastIndexOf("/"));
21110
22704
  const flat = patt.replace(/\[.*?\]/g, "");
21111
22705
  const exch = flat.includes(".+?\\s*") || flat.includes("\\s*.+?") || flat.includes(".*\\s*") || flat.includes("\\s*.*");
@@ -21118,7 +22712,7 @@ var init_no_super_linear_backtracking = __esm(() => {
21118
22712
  mark(idx, literal.length, "Multiple adjacent unlimited wildcard quantifiers can cause super-linear backtracking");
21119
22713
  continue;
21120
22714
  }
21121
- if (/\((?:\?:)?[^)]*?[+*][^)]*\)\s*[+*]/.test(flat)) {
22715
+ if (/\((?:\?:)?[^)]*?[+*][^)]*\)\s*[+*]/.test(flat.replace(/\\[()]/g, "_"))) {
21122
22716
  mark(idx, literal.length, "Nested unlimited quantifiers detected (e.g., (.+)+) which can cause catastrophic backtracking");
21123
22717
  continue;
21124
22718
  }
@@ -21259,6 +22853,43 @@ function areCapturesUsed(content, regexStart, regexEnd) {
21259
22853
  }
21260
22854
  return true;
21261
22855
  }
22856
+ function skipRegexLiteral(content, idx) {
22857
+ let prevIdx = idx - 1;
22858
+ while (prevIdx >= 0 && (content[prevIdx] === " " || content[prevIdx] === "\t"))
22859
+ prevIdx--;
22860
+ const prevChar = prevIdx >= 0 ? content[prevIdx] : "";
22861
+ const regexPrecedes = "=(<>!&|?:;,{[+(~^%*/";
22862
+ const isRegex = prevIdx < 0 || regexPrecedes.includes(prevChar) || /\b(?:return|typeof|void|delete|throw|new|in|of|case)\s*$/.test(content.slice(Math.max(0, prevIdx - 5), prevIdx + 1));
22863
+ if (!isRegex)
22864
+ return idx + 1;
22865
+ let i = idx + 1;
22866
+ let inClass = false;
22867
+ let escaped = false;
22868
+ while (i < content.length) {
22869
+ const c = content[i];
22870
+ if (escaped) {
22871
+ escaped = false;
22872
+ } else if (c === "\\") {
22873
+ escaped = true;
22874
+ } else if (c === "[") {
22875
+ if (!inClass)
22876
+ inClass = true;
22877
+ } else if (c === "]") {
22878
+ if (inClass)
22879
+ inClass = false;
22880
+ } else if (c === "/" && !inClass) {
22881
+ i++;
22882
+ while (i < content.length && /[gimsuy]/.test(content[i]))
22883
+ i++;
22884
+ return i;
22885
+ } else if (c === `
22886
+ `) {
22887
+ break;
22888
+ }
22889
+ i++;
22890
+ }
22891
+ return idx + 1;
22892
+ }
21262
22893
  function findIssues(content, ctx) {
21263
22894
  const issues = [];
21264
22895
  const filePath = ctx.filePath;
@@ -21297,7 +22928,12 @@ function findIssues(content, ctx) {
21297
22928
  if (ch === "/") {
21298
22929
  const info = parseRegexLiteral(content, idx);
21299
22930
  if (!info) {
21300
- idx++;
22931
+ const skipEnd = skipRegexLiteral(content, idx);
22932
+ if (skipEnd > idx + 1) {
22933
+ idx = skipEnd;
22934
+ } else {
22935
+ idx++;
22936
+ }
21301
22937
  continue;
21302
22938
  }
21303
22939
  if (!areCapturesUsed(content, info.patternStart, info.patternEnd)) {
@@ -21721,6 +23357,8 @@ var init_brace_style = __esm(() => {
21721
23357
  for (let i = 0;i < lines.length; i++) {
21722
23358
  const line = lines[i];
21723
23359
  const trimmed = line.trim();
23360
+ if (trimmed.startsWith("//") || trimmed.startsWith("/*"))
23361
+ continue;
21724
23362
  const closingBraceWithNext = /\}\s+(else|catch|finally)\b/;
21725
23363
  if (closingBraceWithNext.test(trimmed)) {
21726
23364
  issues.push({
@@ -21734,6 +23372,8 @@ var init_brace_style = __esm(() => {
21734
23372
  }
21735
23373
  if (trimmed === "{" && i > 0) {
21736
23374
  const prevLine = lines[i - 1].trim();
23375
+ if (!prevLine || prevLine.startsWith("//") || prevLine.startsWith("/*") || prevLine.endsWith("*/"))
23376
+ continue;
21737
23377
  if (prevLine && !prevLine.endsWith("{") && !prevLine.endsWith(",") && !prevLine.endsWith("(")) {
21738
23378
  if (!prevLine.match(/[=:]\s*$/) && !prevLine.endsWith("[")) {
21739
23379
  issues.push({
@@ -26629,7 +28269,7 @@ var init_prefer_nullish_coalescing = __esm(() => {
26629
28269
  for (const line of lines) {
26630
28270
  let fixedLine = line;
26631
28271
  fixedLine = fixedLine.replace(/(\b[\w$]+(?:\.[\w$]+)*)\s*\|\|\s*([^|&]+?)(?=\s*[;,)\]}]|$)/g, (match, left, right) => {
26632
- if (/\b(true|false)\b/.test(right.trim())) {
28272
+ if (/\b(?:true|false)\b/.test(right.trim())) {
26633
28273
  return match;
26634
28274
  }
26635
28275
  return `${left} ?? ${right}`;
@@ -27095,6 +28735,7 @@ function getAllPlugins() {
27095
28735
  markdownPlugin,
27096
28736
  nodePlugin,
27097
28737
  lockfilePlugin,
28738
+ publintPlugin,
27098
28739
  unusedImportsPlugin,
27099
28740
  perfectionistPlugin
27100
28741
  ];
@@ -27106,6 +28747,7 @@ var init_plugins = __esm(() => {
27106
28747
  init_markdown();
27107
28748
  init_node();
27108
28749
  init_perfectionist();
28750
+ init_publint2();
27109
28751
  init_pickier();
27110
28752
  init_quality();
27111
28753
  init_regexp();
@@ -27115,11 +28757,23 @@ var init_plugins = __esm(() => {
27115
28757
  });
27116
28758
 
27117
28759
  // src/utils.ts
27118
- import { readFileSync as readFileSync3 } from "fs";
27119
- import { extname, isAbsolute as isAbsolute3, resolve as resolve14 } from "path";
28760
+ import { readFileSync as readFileSync7 } from "fs";
28761
+ import { extname as extname2, isAbsolute as isAbsolute3, resolve as resolve15 } from "path";
27120
28762
  import process15 from "process";
27121
28763
  function getRuleSetting(rulesConfig, ruleId) {
27122
- const raw = rulesConfig[ruleId];
28764
+ let raw = rulesConfig[ruleId];
28765
+ if (raw === undefined) {
28766
+ const slash = ruleId.indexOf("/");
28767
+ if (slash > 0) {
28768
+ const prefix = ruleId.slice(0, slash);
28769
+ const name = ruleId.slice(slash + 1);
28770
+ const altPrefix = prefix === "general" ? "pickier" : prefix === "pickier" ? "general" : null;
28771
+ if (altPrefix)
28772
+ raw = rulesConfig[`${altPrefix}/${name}`];
28773
+ if (raw === undefined)
28774
+ raw = rulesConfig[name];
28775
+ }
28776
+ }
27123
28777
  let sev;
27124
28778
  let opts;
27125
28779
  if (typeof raw === "string") {
@@ -27184,10 +28838,10 @@ async function loadConfigFromPath(pathLike) {
27184
28838
  } catch {}
27185
28839
  return mergeConfig(defaultConfig3, {});
27186
28840
  }
27187
- const abs = isAbsolute3(pathLike) ? pathLike : resolve14(process15.cwd(), pathLike);
27188
- const ext = extname(abs).toLowerCase();
28841
+ const abs = isAbsolute3(pathLike) ? pathLike : resolve15(process15.cwd(), pathLike);
28842
+ const ext = extname2(abs).toLowerCase();
27189
28843
  if (ext === ".json") {
27190
- const raw = readFileSync3(abs, "utf8");
28844
+ const raw = readFileSync7(abs, "utf8");
27191
28845
  return mergeConfig(defaultConfig3, JSON.parse(raw));
27192
28846
  }
27193
28847
  const mod = await import(abs);
@@ -27245,7 +28899,7 @@ function shouldIgnorePath(absPath, ignoreGlobs) {
27245
28899
  return false;
27246
28900
  }
27247
28901
  var MAX_FIXER_PASSES = 5, ENV, UNIVERSAL_IGNORES, colors, _cachedDefaultConfig = null;
27248
- var init_utils2 = __esm(async () => {
28902
+ var init_utils3 = __esm(async () => {
27249
28903
  await init_config();
27250
28904
  ENV = {
27251
28905
  get TRACE() {
@@ -27319,7 +28973,7 @@ var init_utils2 = __esm(async () => {
27319
28973
  });
27320
28974
 
27321
28975
  // src/formatter.ts
27322
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync8 } from "fs";
28976
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
27323
28977
  import { relative as relative6 } from "path";
27324
28978
  import process16 from "process";
27325
28979
  function getLogger() {
@@ -27362,7 +29016,7 @@ ${colors.bold(`\x1B[4m${filePath}\x1B[24m`)}
27362
29016
  `;
27363
29017
  let fileContent;
27364
29018
  try {
27365
- fileContent = readFileSync4(filePath, "utf8");
29019
+ fileContent = readFileSync8(filePath, "utf8");
27366
29020
  } catch {
27367
29021
  for (const issue of fileIssues) {
27368
29022
  const sev = issue.severity === "error" ? colors.red("error") : colors.yellow("warn ");
@@ -27503,7 +29157,7 @@ async function runFormat(globs, options) {
27503
29157
  let checked = 0;
27504
29158
  for (const file of files) {
27505
29159
  trace("format start", relative6(process16.cwd(), file));
27506
- const src = readFileSync4(file, "utf8");
29160
+ const src = readFileSync8(file, "utf8");
27507
29161
  const fmt = formatCode(src, cfg, file);
27508
29162
  if (options.check) {
27509
29163
  if (fmt !== src) {
@@ -27538,7 +29192,7 @@ var init_formatter = __esm(async () => {
27538
29192
  init_plugins();
27539
29193
  await __promiseAll([
27540
29194
  init_src(),
27541
- init_utils2()
29195
+ init_utils3()
27542
29196
  ]);
27543
29197
  });
27544
29198
 
@@ -27577,6 +29231,9 @@ var init_yocto_queue = __esm(() => {
27577
29231
  }
27578
29232
  this.#head = this.#head.next;
27579
29233
  this.#size--;
29234
+ if (!this.#head) {
29235
+ this.#tail = undefined;
29236
+ }
27580
29237
  return current.value;
27581
29238
  }
27582
29239
  peek() {
@@ -27610,13 +29267,20 @@ var init_yocto_queue = __esm(() => {
27610
29267
 
27611
29268
  // ../../node_modules/p-limit/index.js
27612
29269
  function pLimit(concurrency) {
29270
+ let rejectOnClear = false;
29271
+ if (typeof concurrency === "object") {
29272
+ ({ concurrency, rejectOnClear = false } = concurrency);
29273
+ }
27613
29274
  validateConcurrency(concurrency);
29275
+ if (typeof rejectOnClear !== "boolean") {
29276
+ throw new TypeError("Expected `rejectOnClear` to be a boolean");
29277
+ }
27614
29278
  const queue = new Queue2;
27615
29279
  let activeCount = 0;
27616
29280
  const resumeNext = () => {
27617
29281
  if (activeCount < concurrency && queue.size > 0) {
27618
29282
  activeCount++;
27619
- queue.dequeue()();
29283
+ queue.dequeue().run();
27620
29284
  }
27621
29285
  };
27622
29286
  const next = () => {
@@ -27631,16 +29295,18 @@ function pLimit(concurrency) {
27631
29295
  } catch {}
27632
29296
  next();
27633
29297
  };
27634
- const enqueue = (function_, resolve9, arguments_) => {
29298
+ const enqueue = (function_, resolve9, reject, arguments_) => {
29299
+ const queueItem = { reject };
27635
29300
  new Promise((internalResolve) => {
27636
- queue.enqueue(internalResolve);
29301
+ queueItem.run = internalResolve;
29302
+ queue.enqueue(queueItem);
27637
29303
  }).then(run.bind(undefined, function_, resolve9, arguments_));
27638
29304
  if (activeCount < concurrency) {
27639
29305
  resumeNext();
27640
29306
  }
27641
29307
  };
27642
- const generator = (function_, ...arguments_) => new Promise((resolve9) => {
27643
- enqueue(function_, resolve9, arguments_);
29308
+ const generator = (function_, ...arguments_) => new Promise((resolve9, reject) => {
29309
+ enqueue(function_, resolve9, reject, arguments_);
27644
29310
  });
27645
29311
  Object.defineProperties(generator, {
27646
29312
  activeCount: {
@@ -27651,7 +29317,14 @@ function pLimit(concurrency) {
27651
29317
  },
27652
29318
  clearQueue: {
27653
29319
  value() {
27654
- queue.clear();
29320
+ if (!rejectOnClear) {
29321
+ queue.clear();
29322
+ return;
29323
+ }
29324
+ const abortError = AbortSignal.abort().reason;
29325
+ while (queue.size > 0) {
29326
+ queue.dequeue().reject(abortError);
29327
+ }
27655
29328
  }
27656
29329
  },
27657
29330
  concurrency: {
@@ -27694,8 +29367,8 @@ __export(exports_linter, {
27694
29367
  lintText: () => lintText,
27695
29368
  applyPlugins: () => applyPlugins
27696
29369
  });
27697
- import { readdirSync as readdirSync6, readFileSync as readFileSync5, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
27698
- import { isAbsolute as isAbsolute4, join as join7, relative as relative7, resolve as resolve15 } from "path";
29370
+ import { readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
29371
+ import { isAbsolute as isAbsolute4, join as join7, relative as relative7, resolve as resolve16 } from "path";
27699
29372
  import process17 from "process";
27700
29373
  function getLogger2() {
27701
29374
  if (!_logger2)
@@ -27753,7 +29426,7 @@ async function runLintProgrammatic(globs, options, signal) {
27753
29426
  const timeoutMs = ENV.TIMEOUT_MS;
27754
29427
  const isGlobbingOutsideProject = patterns.some((p) => {
27755
29428
  const base = p.replace(/\/?\*\*\/*\*\*?$/, "");
27756
- const absBase = isAbsolute4(base) ? base : resolve15(process17.cwd(), base);
29429
+ const absBase = isAbsolute4(base) ? base : resolve16(process17.cwd(), base);
27757
29430
  return !absBase.startsWith(process17.cwd());
27758
29431
  });
27759
29432
  const globIgnores = isGlobbingOutsideProject ? [...UNIVERSAL_IGNORES] : cfg.ignores;
@@ -27763,7 +29436,7 @@ async function runLintProgrammatic(globs, options, signal) {
27763
29436
  try {
27764
29437
  const st = statSync2(patterns[0]);
27765
29438
  if (st.isFile()) {
27766
- const abs = isAbsolute4(patterns[0]) ? patterns[0] : resolve15(process17.cwd(), patterns[0]);
29439
+ const abs = isAbsolute4(patterns[0]) ? patterns[0] : resolve16(process17.cwd(), patterns[0]);
27767
29440
  entries = [abs];
27768
29441
  }
27769
29442
  } catch {}
@@ -27771,7 +29444,7 @@ async function runLintProgrammatic(globs, options, signal) {
27771
29444
  const simpleDirPattern = patterns.length === 1 && /\*\*\/*\*$/.test(patterns[0]);
27772
29445
  if (!entries.length && simpleDirPattern) {
27773
29446
  const base = patterns[0].replace(/\/?\*\*\/*\*\*?$/, "");
27774
- const rootBase = isAbsolute4(base) ? base : resolve15(process17.cwd(), base);
29447
+ const rootBase = isAbsolute4(base) ? base : resolve16(process17.cwd(), base);
27775
29448
  try {
27776
29449
  const stack = [rootBase];
27777
29450
  while (stack.length) {
@@ -27838,7 +29511,7 @@ async function runLintProgrammatic(globs, options, signal) {
27838
29511
  const processFile = async (file) => {
27839
29512
  if (signal?.aborted)
27840
29513
  throw new Error("AbortError");
27841
- const src = readFileSync5(file, "utf8");
29514
+ const src = readFileSync9(file, "utf8");
27842
29515
  const suppress = parseDisableDirectives(src);
27843
29516
  const commentLines = getCommentLines(src);
27844
29517
  cfg._internalSkipPluginRulesInScan = true;
@@ -27863,6 +29536,16 @@ async function runLintProgrammatic(globs, options, signal) {
27863
29536
  });
27864
29537
  }
27865
29538
  } catch {}
29539
+ {
29540
+ const seen = new Set;
29541
+ issues = issues.filter((i) => {
29542
+ const key = `${i.line}:${i.column}:${i.ruleId}`;
29543
+ if (seen.has(key))
29544
+ return false;
29545
+ seen.add(key);
29546
+ return true;
29547
+ });
29548
+ }
27866
29549
  if (options.fix) {
27867
29550
  const dbgLine = /^\s*debugger\b/;
27868
29551
  let fixed = src;
@@ -28133,6 +29816,7 @@ async function applyPlugins(filePath, content, cfg) {
28133
29816
  }
28134
29817
  }
28135
29818
  const baseCtx = { filePath, config: cfg };
29819
+ const executedRules = new Set;
28136
29820
  for (const plugin of pluginDefs) {
28137
29821
  const r = plugin.rules;
28138
29822
  for (const ruleName in r) {
@@ -28140,6 +29824,10 @@ async function applyPlugins(filePath, content, cfg) {
28140
29824
  const setting = getRuleSetting(rulesConfig, fullRuleId);
28141
29825
  if (!setting.enabled)
28142
29826
  continue;
29827
+ if (executedRules.has(ruleName)) {
29828
+ continue;
29829
+ }
29830
+ executedRules.add(ruleName);
28143
29831
  const rule = r[ruleName];
28144
29832
  const overrideSeverity = setting.severity;
28145
29833
  if (!rule || typeof rule.check !== "function") {
@@ -28258,7 +29946,7 @@ function stripRegexLiterals(line) {
28258
29946
  }
28259
29947
  return result;
28260
29948
  }
28261
- function stripComments(line) {
29949
+ function stripComments2(line) {
28262
29950
  let result = "";
28263
29951
  let i = 0;
28264
29952
  let inString = null;
@@ -28349,17 +30037,92 @@ function getCommentLines(content) {
28349
30037
  }
28350
30038
  continue;
28351
30039
  }
28352
- if ((state === "string-single" || state === "string-double" || state === "string-template") && prev === "\\") {
28353
- continue;
28354
- }
28355
30040
  switch (state) {
28356
30041
  case "code":
28357
30042
  if (ch === "/" && next === "/") {
28358
30043
  state = "line-comment";
28359
30044
  i++;
28360
30045
  } else if (ch === "/" && next === "*") {
28361
- state = "block-comment";
28362
- i++;
30046
+ let isRegex = false;
30047
+ if (i > 0) {
30048
+ let k = i - 1;
30049
+ while (k > 0 && (content[k] === " " || content[k] === "\t"))
30050
+ k--;
30051
+ const bc = content[k];
30052
+ if ("=([{,;!&|?:~^%+-".includes(bc) && bc !== `
30053
+ `) {
30054
+ isRegex = true;
30055
+ } else if (/[a-z]/.test(bc)) {
30056
+ const slice = content.slice(Math.max(0, k - 6), k + 1);
30057
+ if (/\b(?:return|case|typeof|void|in|of|new|throw|delete)$/.test(slice)) {
30058
+ isRegex = true;
30059
+ }
30060
+ }
30061
+ }
30062
+ if (isRegex) {
30063
+ lineHasCode = true;
30064
+ i++;
30065
+ while (i < content.length) {
30066
+ if (content[i] === "\\") {
30067
+ i += 2;
30068
+ continue;
30069
+ }
30070
+ if (content[i] === "/") {
30071
+ i++;
30072
+ break;
30073
+ }
30074
+ if (content[i] === `
30075
+ `)
30076
+ break;
30077
+ i++;
30078
+ }
30079
+ while (i < content.length && /[gimsuy]/.test(content[i]))
30080
+ i++;
30081
+ i--;
30082
+ } else {
30083
+ state = "block-comment";
30084
+ i++;
30085
+ }
30086
+ } else if (ch === "/" && next !== undefined) {
30087
+ let isRegex = false;
30088
+ if (i > 0) {
30089
+ let k = i - 1;
30090
+ while (k > 0 && (content[k] === " " || content[k] === "\t"))
30091
+ k--;
30092
+ const bc = content[k];
30093
+ if ("=([{,;!&|?:~^%+-".includes(bc) || bc === `
30094
+ `) {
30095
+ isRegex = true;
30096
+ } else if (/[a-z]/.test(bc)) {
30097
+ const slice = content.slice(Math.max(0, k - 6), k + 1);
30098
+ if (/\b(?:return|case|typeof|void|in|of|new|throw|delete)$/.test(slice)) {
30099
+ isRegex = true;
30100
+ }
30101
+ }
30102
+ }
30103
+ if (isRegex) {
30104
+ lineHasCode = true;
30105
+ i++;
30106
+ while (i < content.length) {
30107
+ if (content[i] === "\\") {
30108
+ i += 2;
30109
+ continue;
30110
+ }
30111
+ if (content[i] === "/") {
30112
+ i++;
30113
+ break;
30114
+ }
30115
+ if (content[i] === `
30116
+ `)
30117
+ break;
30118
+ i++;
30119
+ }
30120
+ while (i < content.length && /[gimsuy]/.test(content[i]))
30121
+ i++;
30122
+ i--;
30123
+ } else {
30124
+ lineHasCode = true;
30125
+ }
28363
30126
  } else if (ch === "'") {
28364
30127
  state = "string-single";
28365
30128
  lineHasCode = true;
@@ -28374,19 +30137,28 @@ function getCommentLines(content) {
28374
30137
  }
28375
30138
  break;
28376
30139
  case "string-single":
28377
- if (ch === "'") {
28378
- state = "code";
30140
+ if (ch === "\\") {
30141
+ i++;
30142
+ break;
28379
30143
  }
30144
+ if (ch === "'")
30145
+ state = "code";
28380
30146
  break;
28381
30147
  case "string-double":
28382
- if (ch === '"') {
28383
- state = "code";
30148
+ if (ch === "\\") {
30149
+ i++;
30150
+ break;
28384
30151
  }
30152
+ if (ch === '"')
30153
+ state = "code";
28385
30154
  break;
28386
30155
  case "string-template":
28387
- if (ch === "`") {
28388
- state = "code";
30156
+ if (ch === "\\") {
30157
+ i++;
30158
+ break;
28389
30159
  }
30160
+ if (ch === "`")
30161
+ state = "code";
28390
30162
  break;
28391
30163
  case "line-comment":
28392
30164
  break;
@@ -28410,7 +30182,9 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
28410
30182
  const lines = content.split(/\r?\n/);
28411
30183
  const preferredQuotes = cfg.format.quotes;
28412
30184
  const fileExt = filePath.split(".").pop()?.toLowerCase() ?? "";
28413
- const skipQuotesCheck = fileExt === "json" || fileExt === "jsonc" || fileExt === "lock" || fileExt === "md" || fileExt === "yaml" || fileExt === "yml" || filePath.endsWith("bun.lock");
30185
+ const isMd = fileExt === "md";
30186
+ const skipQuotesCheck = fileExt === "json" || fileExt === "jsonc" || fileExt === "lock" || isMd || fileExt === "yaml" || fileExt === "yml" || filePath.endsWith("bun.lock");
30187
+ const skipCodeRules = isMd || fileExt === "yaml" || fileExt === "yml" || fileExt === "json" || fileExt === "jsonc";
28414
30188
  let quotesReported = false;
28415
30189
  const sevMap = (s) => s === "warn" ? "warning" : s === "error" ? "error" : undefined;
28416
30190
  const wantDebugger = sevMap(cfg.rules.noDebugger);
@@ -28423,7 +30197,7 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
28423
30197
  if (fileExt === "md") {
28424
30198
  let inFence = false;
28425
30199
  for (let li = 0;li < lines.length; li++) {
28426
- if (/^(`{3,}|~{3,})/.test(lines[li].trim())) {
30200
+ if (/^(?:`{3,}|~{3,})/.test(lines[li].trim())) {
28427
30201
  inFence = !inFence;
28428
30202
  continue;
28429
30203
  }
@@ -28468,7 +30242,7 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
28468
30242
  continue;
28469
30243
  }
28470
30244
  if (!skipQuotesCheck && !linesInTemplate.has(lineNo)) {
28471
- const strippedLine = stripComments(stripRegexLiterals(line));
30245
+ const strippedLine = stripComments2(stripRegexLiterals(line));
28472
30246
  const indices = detectQuoteIssues(strippedLine, preferredQuotes);
28473
30247
  if (indices.length > 0 && !quotesReported) {
28474
30248
  if (!isSuppressed("quotes", lineNo, suppress)) {
@@ -28483,11 +30257,11 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
28483
30257
  if (!isSuppressed("indent", lineNo, suppress))
28484
30258
  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` });
28485
30259
  }
28486
- if (wantDebugger && debuggerStmt.test(line)) {
30260
+ if (!skipCodeRules && wantDebugger && debuggerStmt.test(line)) {
28487
30261
  if (!isSuppressed("no-debugger", lineNo, suppress))
28488
30262
  issues.push({ filePath, line: lineNo, column: 1, ruleId: "no-debugger", message: "Unexpected debugger statement", severity: wantDebugger, help: "Remove debugger statements before committing code. Use breakpoints in your IDE instead, or run with --fix to auto-remove" });
28489
30263
  }
28490
- if (wantConsole && !linesInTemplate.has(lineNo) && consoleCall.test(line)) {
30264
+ if (!skipCodeRules && wantConsole && !linesInTemplate.has(lineNo) && consoleCall.test(line)) {
28491
30265
  const commentIdx = line.indexOf("//");
28492
30266
  const consoleIdx = line.indexOf("console.");
28493
30267
  if (commentIdx !== -1 && consoleIdx > commentIdx) {} else {
@@ -28552,9 +30326,9 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
28552
30326
  prev = ch;
28553
30327
  }
28554
30328
  }
28555
- if (wantNoCondAssign && !linesInTemplate.has(lineNo)) {
28556
- const strippedLine = stripComments(stripRegexLiterals(line));
28557
- const checkCond = (cond) => /[^=!<>]=(?![=>])/.test(cond);
30329
+ if (!skipCodeRules && wantNoCondAssign && !linesInTemplate.has(lineNo)) {
30330
+ const strippedLine = stripComments2(stripRegexLiterals(line));
30331
+ const checkCond = (cond) => /[^=!<>]=(?![=>])/.test(cond.replace(/'[^']*'|"[^"]*"/g, '""'));
28558
30332
  const m1 = strippedLine.match(/\b(?:if|while)\s*\(([^)]*)\)/);
28559
30333
  if (m1) {
28560
30334
  const cond = m1[1];
@@ -28620,12 +30394,16 @@ function scanContent(filePath, content, cfg) {
28620
30394
  return;
28621
30395
  };
28622
30396
  const ctx = { filePath, config: cfg };
30397
+ const executedRulesInScan = new Set;
28623
30398
  for (const plugin of pluginDefs) {
28624
30399
  const r = plugin.rules;
28625
30400
  for (const ruleName in r) {
28626
30401
  const fullRuleId = `${plugin.name}/${ruleName}`;
28627
30402
  if (!isRuleEnabled(fullRuleId))
28628
30403
  continue;
30404
+ if (executedRulesInScan.has(ruleName))
30405
+ continue;
30406
+ executedRulesInScan.add(ruleName);
28629
30407
  const rule = r[ruleName];
28630
30408
  if (!rule || typeof rule.check !== "function")
28631
30409
  continue;
@@ -28669,7 +30447,7 @@ async function runLint(globs, options) {
28669
30447
  getLogger2().info(`[pickier:diagnostics] Glob timeout: ${timeoutMs}ms`);
28670
30448
  const isGlobbingOutsideProject = patterns.some((p) => {
28671
30449
  const base = p.replace(/\/?\*\*\/*\*\*?$/, "");
28672
- const absBase = isAbsolute4(base) ? base : resolve15(process17.cwd(), base);
30450
+ const absBase = isAbsolute4(base) ? base : resolve16(process17.cwd(), base);
28673
30451
  return !absBase.startsWith(process17.cwd());
28674
30452
  });
28675
30453
  const globIgnores = isGlobbingOutsideProject ? [...UNIVERSAL_IGNORES] : cfg.ignores;
@@ -28686,7 +30464,7 @@ async function runLint(globs, options) {
28686
30464
  try {
28687
30465
  const st = statSync2(patterns[0]);
28688
30466
  if (st.isFile()) {
28689
- const abs = isAbsolute4(patterns[0]) ? patterns[0] : resolve15(process17.cwd(), patterns[0]);
30467
+ const abs = isAbsolute4(patterns[0]) ? patterns[0] : resolve16(process17.cwd(), patterns[0]);
28690
30468
  entries = [abs];
28691
30469
  }
28692
30470
  } catch {}
@@ -28697,7 +30475,7 @@ async function runLint(globs, options) {
28697
30475
  if (enableDiagnostics)
28698
30476
  getLogger2().info(`[pickier:diagnostics] Using fast directory scan for: ${base}`);
28699
30477
  try {
28700
- const rootBase = isAbsolute4(base) ? base : resolve15(process17.cwd(), base);
30478
+ const rootBase = isAbsolute4(base) ? base : resolve16(process17.cwd(), base);
28701
30479
  const stack = [rootBase];
28702
30480
  let dirCount = 0;
28703
30481
  while (stack.length) {
@@ -28813,7 +30591,7 @@ async function runLint(globs, options) {
28813
30591
  if (processedCount === 1 || processedCount % 10 === 0 || processedCount === files.length)
28814
30592
  getLogger2().info(`[pickier:diagnostics] Processing file ${processedCount}/${files.length}: ${relative7(process17.cwd(), file)}`);
28815
30593
  }
28816
- const src = readFileSync5(file, "utf8");
30594
+ const src = readFileSync9(file, "utf8");
28817
30595
  if (formatOnly) {
28818
30596
  const fixed = formatCode(src, cfg, file);
28819
30597
  if (fixed !== src && !options.dryRun) {
@@ -28822,7 +30600,8 @@ async function runLint(globs, options) {
28822
30600
  return [];
28823
30601
  }
28824
30602
  const suppress = parseDisableDirectives(src);
28825
- const commentLines = getCommentLines(src);
30603
+ const isCodeFileForComments = /\.(?:ts|js|tsx|jsx|mts|mjs|cts|cjs)$/.test(file);
30604
+ const commentLines = isCodeFileForComments ? getCommentLines(src) : new Set;
28826
30605
  cfg._internalSkipPluginRulesInScan = true;
28827
30606
  let issues = scanContentOptimized(file, src, cfg, suppress, commentLines);
28828
30607
  try {
@@ -28843,6 +30622,16 @@ async function runLint(globs, options) {
28843
30622
  });
28844
30623
  }
28845
30624
  } catch {}
30625
+ {
30626
+ const seen = new Set;
30627
+ issues = issues.filter((i) => {
30628
+ const key = `${i.line}:${i.column}:${i.ruleId}`;
30629
+ if (seen.has(key))
30630
+ return false;
30631
+ seen.add(key);
30632
+ return true;
30633
+ });
30634
+ }
28846
30635
  if (options.fix) {
28847
30636
  const dbgLine = /^\s*debugger\b/;
28848
30637
  let fixed = src;
@@ -28964,28 +30753,34 @@ var init_linter = __esm(async () => {
28964
30753
  await __promiseAll([
28965
30754
  init_src(),
28966
30755
  init_formatter(),
28967
- init_utils2()
30756
+ init_utils3()
28968
30757
  ]);
28969
30758
  });
28970
30759
 
30760
+ // src/index.ts
30761
+ await __promiseAll([
30762
+ init_config(),
30763
+ init_formatter(),
30764
+ init_linter()
30765
+ ]);
30766
+ init_format();
30767
+
28971
30768
  // src/run.ts
28972
- var exports_run = {};
28973
- __export(exports_run, {
28974
- runUnified: () => runUnified
28975
- });
28976
- import { readFileSync as readFileSync6, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
28977
- import { isAbsolute as isAbsolute5, resolve as resolve16 } from "path";
30769
+ init_format();
30770
+ await init_utils3();
30771
+ import { readFileSync as readFileSync10, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
30772
+ import { isAbsolute as isAbsolute5, resolve as resolve17 } from "path";
28978
30773
  import process18 from "process";
28979
30774
  async function runUnified(globs, options) {
28980
30775
  const mode = options.mode || "auto";
28981
30776
  if (mode === "format" && globs.length === 1 && !/[*?[\]{}()!]/.test(globs[0])) {
28982
30777
  try {
28983
30778
  const p = globs[0];
28984
- const filePath = isAbsolute5(p) ? p : resolve16(process18.cwd(), p);
30779
+ const filePath = isAbsolute5(p) ? p : resolve17(process18.cwd(), p);
28985
30780
  const st = statSync3(filePath);
28986
30781
  if (st.isFile()) {
28987
30782
  const cfg = await loadConfigFromPath(options.config);
28988
- const src = readFileSync6(filePath, "utf8");
30783
+ const src = readFileSync10(filePath, "utf8");
28989
30784
  const fmt = formatCode(src, cfg, filePath);
28990
30785
  if (options.write && fmt !== src) {
28991
30786
  writeFileSync10(filePath, fmt, "utf8");
@@ -29017,20 +30812,9 @@ async function runUnified(globs, options) {
29017
30812
  };
29018
30813
  return runLint2(globs, lintOpts);
29019
30814
  }
29020
- var init_run = __esm(async () => {
29021
- init_format();
29022
- await init_utils2();
29023
- });
29024
30815
 
29025
30816
  // src/index.ts
29026
- await __promiseAll([
29027
- init_config(),
29028
- init_formatter(),
29029
- init_linter(),
29030
- init_run()
29031
- ]);
29032
- init_format();
29033
- init_utils2();
30817
+ init_utils3();
29034
30818
  export {
29035
30819
  yellow5 as yellow,
29036
30820
  shouldIgnorePath,