pickier 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/cli.js CHANGED
@@ -4635,15 +4635,16 @@ async function getConfig() {
4635
4635
  if (!_config) {
4636
4636
  _config = await loadConfig({
4637
4637
  name: "pickier",
4638
- alias: "code-style",
4638
+ alias: [...CONFIG_ALIASES],
4639
4639
  defaultConfig: defaultConfig2
4640
4640
  });
4641
4641
  }
4642
4642
  return _config;
4643
4643
  }
4644
- var defaultConfig2, _config = null, config3;
4644
+ var CONFIG_ALIASES, defaultConfig2, _config = null, config3;
4645
4645
  var init_config2 = __esm(() => {
4646
4646
  init_dist();
4647
+ CONFIG_ALIASES = ["code-style", "lint"];
4647
4648
  defaultConfig2 = {
4648
4649
  ignores: [
4649
4650
  "**/node_modules/**",
@@ -15555,6 +15556,9 @@ var init_no_unused_vars = __esm(() => {
15555
15556
  if (ctx.filePath.endsWith("/no-unused-vars.ts")) {
15556
15557
  return [];
15557
15558
  }
15559
+ if (ctx.filePath.endsWith(".d.ts")) {
15560
+ return [];
15561
+ }
15558
15562
  const issues = [];
15559
15563
  const opts = ctx.options || {};
15560
15564
  const varsIgnorePattern = typeof opts.varsIgnorePattern === "string" ? opts.varsIgnorePattern : "^_";
@@ -16437,6 +16441,10 @@ var init_no_unused_vars = __esm(() => {
16437
16441
  if (afterFunc.startsWith(":")) {
16438
16442
  continue;
16439
16443
  }
16444
+ const beforeFunc = codeClean.slice(0, m.index).trimEnd();
16445
+ if (beforeFunc.endsWith(".") || beforeFunc.endsWith("?.")) {
16446
+ continue;
16447
+ }
16440
16448
  const funcIdx = m.index;
16441
16449
  const openParenIdx = line.indexOf("(", funcIdx);
16442
16450
  if (openParenIdx === -1)
@@ -16536,6 +16544,7 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
16536
16544
  if (openParenIdx !== -1) {
16537
16545
  let isTypeSignature = false;
16538
16546
  let angleDepthBack = 0;
16547
+ let parenDepthBack = 0;
16539
16548
  for (let k = openParenIdx - 1;k >= 0; k--) {
16540
16549
  const ch = line[k];
16541
16550
  if (ch === ">") {
@@ -16547,10 +16556,13 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
16547
16556
  angleDepthBack--;
16548
16557
  continue;
16549
16558
  }
16550
- isTypeSignature = true;
16551
- break;
16559
+ if (parenDepthBack === 0) {
16560
+ isTypeSignature = true;
16561
+ break;
16562
+ }
16563
+ continue;
16552
16564
  }
16553
- if (ch === ":" && angleDepthBack === 0) {
16565
+ if (ch === ":" && angleDepthBack === 0 && parenDepthBack === 0) {
16554
16566
  isTypeSignature = true;
16555
16567
  break;
16556
16568
  }
@@ -16558,11 +16570,34 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
16558
16570
  continue;
16559
16571
  if (ch === ",")
16560
16572
  continue;
16561
- if (ch === "=" || ch === "(" || ch === "{" || ch === "[") {
16573
+ if (ch === ")") {
16574
+ parenDepthBack++;
16575
+ continue;
16576
+ }
16577
+ if (ch === "(") {
16578
+ parenDepthBack--;
16579
+ continue;
16580
+ }
16581
+ if (ch === "|" || ch === "&") {
16582
+ isTypeSignature = true;
16562
16583
  break;
16563
16584
  }
16585
+ if (ch === "=" || ch === "{" || ch === "[") {
16586
+ if (parenDepthBack >= 0)
16587
+ break;
16588
+ continue;
16589
+ }
16564
16590
  if (ch !== " " && ch !== "\t" && !/[\w.]/.test(ch)) {
16565
- break;
16591
+ if (parenDepthBack >= 0)
16592
+ break;
16593
+ }
16594
+ }
16595
+ if (!isTypeSignature) {
16596
+ const beforeParen = line.slice(0, openParenIdx);
16597
+ if (/^\s*(?:export\s+)?(?:declare\s+)?type\s+\w[\w$]*\s*(?:<[^>]*>)?\s*=\s*$/.test(beforeParen)) {
16598
+ isTypeSignature = true;
16599
+ } else if (/\bas\s+$/.test(beforeParen)) {
16600
+ isTypeSignature = true;
16566
16601
  }
16567
16602
  }
16568
16603
  if (isTypeSignature) {
@@ -19392,6 +19427,42 @@ var init_link_image_reference_definitions = __esm(() => {
19392
19427
  }
19393
19428
  }
19394
19429
  return issues;
19430
+ },
19431
+ fix: (text) => {
19432
+ const lines = text.split(/\r?\n/);
19433
+ const inCode = getCodeBlockLines(lines);
19434
+ const defLines = new Map;
19435
+ for (let i = 0;i < lines.length; i++) {
19436
+ if (inCode.has(i))
19437
+ continue;
19438
+ const m = lines[i].match(/^\s*\[([^\]]+)\]:\s*\S+/);
19439
+ if (m)
19440
+ defLines.set(i, m[1].toLowerCase());
19441
+ }
19442
+ if (defLines.size === 0)
19443
+ return text;
19444
+ const usages = new Set;
19445
+ for (let i = 0;i < lines.length; i++) {
19446
+ if (inCode.has(i))
19447
+ continue;
19448
+ if (defLines.has(i))
19449
+ continue;
19450
+ const line = lines[i];
19451
+ const refMatches = line.matchAll(/\[([^\]]+)\](?:\[([^\]]*)\])?(?!\()/g);
19452
+ for (const m of refMatches) {
19453
+ const label = (m[2] && m[2].length > 0 ? m[2] : m[1]).toLowerCase();
19454
+ usages.add(label);
19455
+ }
19456
+ }
19457
+ const toRemove = new Set;
19458
+ for (const [idx, label] of defLines) {
19459
+ if (!usages.has(label))
19460
+ toRemove.add(idx);
19461
+ }
19462
+ if (toRemove.size === 0)
19463
+ return text;
19464
+ return lines.filter((_, idx) => !toRemove.has(idx)).join(`
19465
+ `);
19395
19466
  }
19396
19467
  };
19397
19468
  });
@@ -19409,19 +19480,39 @@ var init_link_image_style = __esm(() => {
19409
19480
  check: (text, ctx) => {
19410
19481
  const issues = [];
19411
19482
  const lines = text.split(/\r?\n/);
19483
+ const inCode = getCodeBlockLines(lines);
19412
19484
  const options = ctx.options || {};
19413
19485
  const style = options.style || "consistent";
19414
- let detectedStyle = null;
19415
- let inFence = false;
19486
+ let target = style === "consistent" ? null : style;
19487
+ if (target === null) {
19488
+ let inlineCount = 0;
19489
+ let refCount = 0;
19490
+ let inHtmlCommentScan = false;
19491
+ for (let i = 0;i < lines.length; i++) {
19492
+ if (inCode.has(i))
19493
+ continue;
19494
+ const line = lines[i];
19495
+ if (line.includes("<!--"))
19496
+ inHtmlCommentScan = true;
19497
+ if (line.includes("-->")) {
19498
+ inHtmlCommentScan = false;
19499
+ continue;
19500
+ }
19501
+ if (inHtmlCommentScan)
19502
+ continue;
19503
+ if (/^\s*\[(?:[^\]]+)\]:\s*\S+/.test(line))
19504
+ continue;
19505
+ const scrubbed = stripInlineCode(line);
19506
+ inlineCount += (scrubbed.match(/\[[^\]]+\]\([^)]+\)/g) || []).length;
19507
+ refCount += (scrubbed.match(/\[[^\]]+\]\[(?:[^\]]*)\]/g) || []).length;
19508
+ }
19509
+ target = refCount > inlineCount ? "reference" : "inline";
19510
+ }
19416
19511
  let inHtmlComment = false;
19417
19512
  for (let i = 0;i < lines.length; i++) {
19418
- const line = lines[i];
19419
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
19420
- inFence = !inFence;
19421
- continue;
19422
- }
19423
- if (inFence)
19513
+ if (inCode.has(i))
19424
19514
  continue;
19515
+ const line = lines[i];
19425
19516
  if (line.includes("<!--"))
19426
19517
  inHtmlComment = true;
19427
19518
  if (line.includes("-->")) {
@@ -19430,64 +19521,104 @@ var init_link_image_style = __esm(() => {
19430
19521
  }
19431
19522
  if (inHtmlComment)
19432
19523
  continue;
19433
- if (line.match(/^\[(?:[^\]]+)\]:\s*\S+/)) {
19524
+ if (line.match(/^\[(?:[^\]]+)\]:\s*\S+/))
19434
19525
  continue;
19435
- }
19436
19526
  const scrubbed = stripInlineCode(line);
19437
19527
  const inlineMatches = scrubbed.matchAll(/\[[^\]]+\]\([^)]+\)/g);
19438
19528
  for (const match of inlineMatches) {
19439
- if (style === "reference") {
19529
+ if (target === "reference") {
19440
19530
  issues.push({
19441
19531
  filePath: ctx.filePath,
19442
19532
  line: i + 1,
19443
19533
  column: match.index + 1,
19444
19534
  ruleId: "markdown/link-image-style",
19445
- message: "Expected reference style link",
19446
- severity: "error"
19535
+ message: style === "consistent" ? "Link style should be consistent throughout document" : "Expected reference style link",
19536
+ severity: style === "consistent" ? "warning" : "error"
19447
19537
  });
19448
- } else if (style === "consistent") {
19449
- if (detectedStyle === null) {
19450
- detectedStyle = "inline";
19451
- } else if (detectedStyle === "reference") {
19452
- issues.push({
19453
- filePath: ctx.filePath,
19454
- line: i + 1,
19455
- column: match.index + 1,
19456
- ruleId: "markdown/link-image-style",
19457
- message: "Link style should be consistent throughout document",
19458
- severity: "warning"
19459
- });
19460
- }
19461
19538
  }
19462
19539
  }
19463
19540
  const refMatches = scrubbed.matchAll(/\[[^\]]+\]\[(?:[^\]]+)\]/g);
19464
19541
  for (const match of refMatches) {
19465
- if (style === "inline") {
19542
+ if (target === "inline") {
19466
19543
  issues.push({
19467
19544
  filePath: ctx.filePath,
19468
19545
  line: i + 1,
19469
19546
  column: match.index + 1,
19470
19547
  ruleId: "markdown/link-image-style",
19471
- message: "Expected inline style link",
19472
- severity: "error"
19548
+ message: style === "consistent" ? "Link style should be consistent throughout document" : "Expected inline style link",
19549
+ severity: style === "consistent" ? "warning" : "error"
19473
19550
  });
19474
- } else if (style === "consistent") {
19475
- if (detectedStyle === null) {
19476
- detectedStyle = "reference";
19477
- } else if (detectedStyle === "inline") {
19478
- issues.push({
19479
- filePath: ctx.filePath,
19480
- line: i + 1,
19481
- column: match.index + 1,
19482
- ruleId: "markdown/link-image-style",
19483
- message: "Link style should be consistent throughout document",
19484
- severity: "warning"
19485
- });
19486
- }
19487
19551
  }
19488
19552
  }
19489
19553
  }
19490
19554
  return issues;
19555
+ },
19556
+ fix: (text, ctx) => {
19557
+ const options = ctx.options || {};
19558
+ const style = options.style || "consistent";
19559
+ const lines = text.split(/\r?\n/);
19560
+ const inCode = getCodeBlockLines(lines);
19561
+ const defs = new Map;
19562
+ for (let i = 0;i < lines.length; i++) {
19563
+ if (inCode.has(i))
19564
+ continue;
19565
+ const m = lines[i].match(/^\s*\[([^\]]+)\]:\s*(\S+)(?:\s+(?:"([^"]*)"|'([^']*)'|\(([^)]*)\)))?\s*$/);
19566
+ if (m) {
19567
+ const label = m[1].toLowerCase();
19568
+ const url = m[2];
19569
+ const title = m[3] ?? m[4] ?? m[5];
19570
+ if (!defs.has(label))
19571
+ defs.set(label, { url, title });
19572
+ }
19573
+ }
19574
+ if (defs.size === 0)
19575
+ return text;
19576
+ let target = style === "reference" ? "reference" : "inline";
19577
+ if (style === "consistent") {
19578
+ let inlineCount = 0;
19579
+ let refCount = 0;
19580
+ for (let i = 0;i < lines.length; i++) {
19581
+ if (inCode.has(i))
19582
+ continue;
19583
+ const line = lines[i];
19584
+ if (/^\s*\[(?:[^\]]+)\]:\s*\S+/.test(line))
19585
+ continue;
19586
+ const scrubbed = stripInlineCode(line);
19587
+ inlineCount += (scrubbed.match(/\[[^\]]+\]\([^)]+\)/g) || []).length;
19588
+ refCount += (scrubbed.match(/\[[^\]]+\]\[(?:[^\]]*)\]/g) || []).length;
19589
+ }
19590
+ target = refCount > inlineCount ? "reference" : "inline";
19591
+ }
19592
+ if (target !== "inline")
19593
+ return text;
19594
+ let changed = false;
19595
+ for (let i = 0;i < lines.length; i++) {
19596
+ if (inCode.has(i))
19597
+ continue;
19598
+ const original = lines[i];
19599
+ if (/^\s*\[(?:[^\]]+)\]:\s*\S+/.test(original))
19600
+ continue;
19601
+ let rewritten = original;
19602
+ for (let pass = 0;pass < 8; pass++) {
19603
+ const next = rewritten.replace(/(!?)\[((?:[^[\]]|\[[^\]]*\]\([^)]*\))+)\]\[([^\]]*)\]/g, (whole, bang, textPart, labelPart) => {
19604
+ const labelKey = (labelPart.trim() === "" ? textPart : labelPart).toLowerCase();
19605
+ const def = defs.get(labelKey);
19606
+ if (!def)
19607
+ return whole;
19608
+ const titlePart = def.title ? ` "${def.title}"` : "";
19609
+ return `${bang}[${textPart}](${def.url}${titlePart})`;
19610
+ });
19611
+ if (next === rewritten)
19612
+ break;
19613
+ rewritten = next;
19614
+ }
19615
+ if (rewritten !== original) {
19616
+ lines[i] = rewritten;
19617
+ changed = true;
19618
+ }
19619
+ }
19620
+ return changed ? lines.join(`
19621
+ `) : text;
19491
19622
  }
19492
19623
  };
19493
19624
  });
@@ -20891,24 +21022,18 @@ var init_single_title = __esm(() => {
20891
21022
  check: (text, ctx) => {
20892
21023
  const issues = [];
20893
21024
  const lines = text.split(/\r?\n/);
21025
+ const inCode = getCodeBlockLines(lines);
20894
21026
  let firstH1Line = -1;
20895
- let inFencedCodeBlock = false;
20896
21027
  for (let i = 0;i < lines.length; i++) {
21028
+ if (inCode.has(i))
21029
+ continue;
20897
21030
  const line = lines[i];
20898
21031
  const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
20899
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
20900
- inFencedCodeBlock = !inFencedCodeBlock;
20901
- continue;
20902
- }
20903
- if (inFencedCodeBlock)
20904
- continue;
20905
21032
  let isH1 = false;
20906
- if (/^#\s/.test(line)) {
21033
+ if (/^#\s/.test(line))
20907
21034
  isH1 = true;
20908
- }
20909
- if (/^=+\s*$/.test(nextLine) && line.trim().length > 0) {
21035
+ if (/^=+\s*$/.test(nextLine) && line.trim().length > 0 && !inCode.has(i + 1))
20910
21036
  isH1 = true;
20911
- }
20912
21037
  if (isH1) {
20913
21038
  if (firstH1Line === -1) {
20914
21039
  firstH1Line = i + 1;
@@ -20925,6 +21050,47 @@ var init_single_title = __esm(() => {
20925
21050
  }
20926
21051
  }
20927
21052
  return issues;
21053
+ },
21054
+ fix: (text) => {
21055
+ const lines = text.split(/\r?\n/);
21056
+ const inCode = getCodeBlockLines(lines);
21057
+ const result = [];
21058
+ let seenH1 = false;
21059
+ let changed = false;
21060
+ for (let i = 0;i < lines.length; i++) {
21061
+ if (inCode.has(i)) {
21062
+ result.push(lines[i]);
21063
+ continue;
21064
+ }
21065
+ const line = lines[i];
21066
+ const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
21067
+ const atxH1 = /^#\s/.test(line);
21068
+ const setextH1 = /^=+\s*$/.test(nextLine) && line.trim().length > 0 && !inCode.has(i + 1);
21069
+ if (atxH1) {
21070
+ if (!seenH1) {
21071
+ seenH1 = true;
21072
+ result.push(line);
21073
+ } else {
21074
+ result.push(`#${line}`);
21075
+ changed = true;
21076
+ }
21077
+ continue;
21078
+ }
21079
+ if (setextH1) {
21080
+ if (!seenH1) {
21081
+ seenH1 = true;
21082
+ result.push(line);
21083
+ continue;
21084
+ }
21085
+ result.push(`## ${line.trim()}`);
21086
+ i++;
21087
+ changed = true;
21088
+ continue;
21089
+ }
21090
+ result.push(line);
21091
+ }
21092
+ return changed ? result.join(`
21093
+ `) : text;
20928
21094
  }
20929
21095
  };
20930
21096
  });
@@ -21525,6 +21691,13 @@ function splitFrontmatter(content) {
21525
21691
  }
21526
21692
  return { header: null, body: content };
21527
21693
  }
21694
+ function markdownOnlyWholeFile(rule) {
21695
+ return {
21696
+ meta: rule.meta,
21697
+ check: (content, context) => context.filePath.endsWith(".md") ? rule.check(content, context) : [],
21698
+ fix: rule.fix ? (content, context) => context.filePath.endsWith(".md") ? rule.fix(content, context) : content : undefined
21699
+ };
21700
+ }
21528
21701
  function markdownOnly(rule) {
21529
21702
  return {
21530
21703
  meta: rule.meta,
@@ -21632,7 +21805,7 @@ var init_markdown = __esm(() => {
21632
21805
  "no-multiple-space-blockquote": markdownOnly(noMultipleSpaceBlockquoteRule),
21633
21806
  "no-blanks-blockquote": markdownOnly(noBlanksBlockquoteRule),
21634
21807
  "blanks-around-fences": markdownOnly(blanksAroundFencesRule),
21635
- "single-trailing-newline": markdownOnly(singleTrailingNewlineRule),
21808
+ "single-trailing-newline": markdownOnlyWholeFile(singleTrailingNewlineRule),
21636
21809
  "blanks-around-tables": markdownOnly(blanksAroundTablesRule),
21637
21810
  "no-reversed-links": markdownOnly(noReversedLinksRule),
21638
21811
  "no-bare-urls": markdownOnly(noBareUrlsRule),
@@ -33457,7 +33630,9 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
33457
33630
  const fileExt = filePath.split(".").pop()?.toLowerCase() ?? "";
33458
33631
  const isMd = fileExt === "md";
33459
33632
  const isShell = fileExt === "sh" || fileExt === "bash" || fileExt === "zsh" || fileExt === "ksh" || fileExt === "dash" || /^#!\s*(?:\/usr\/bin\/env\s+)?(?:ba|z|k|da)?sh\b/.test(content);
33460
- const skipQuotesCheck = fileExt === "json" || fileExt === "jsonc" || fileExt === "lock" || isMd || fileExt === "yaml" || fileExt === "yml" || isShell || filePath.endsWith("bun.lock");
33633
+ const quotesRuleSetting = cfg.rules?.quotes ?? cfg.pluginRules?.quotes ?? cfg.pluginRules?.["style/quotes"];
33634
+ const quotesDisabled = quotesRuleSetting === "off";
33635
+ const skipQuotesCheck = quotesDisabled || fileExt === "json" || fileExt === "jsonc" || fileExt === "lock" || isMd || fileExt === "yaml" || fileExt === "yml" || fileExt === "stx" || fileExt === "html" || fileExt === "htm" || fileExt === "vue" || isShell || filePath.endsWith("bun.lock");
33461
33636
  const skipCodeRules = isMd || fileExt === "yaml" || fileExt === "yml" || fileExt === "json" || fileExt === "jsonc" || isShell;
33462
33637
  let quotesReported = false;
33463
33638
  const sevMap = (s) => s === "warn" ? "warning" : s === "error" ? "error" : undefined;
@@ -33525,9 +33700,12 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
33525
33700
  quotesReported = true;
33526
33701
  }
33527
33702
  }
33703
+ const indentRuleSetting = cfg.rules?.indent ?? cfg.pluginRules?.indent ?? cfg.pluginRules?.["style/indent"];
33704
+ const indentDisabled = indentRuleSetting === "off";
33705
+ const isTemplate = fileExt === "stx" || fileExt === "html" || fileExt === "htm" || fileExt === "vue";
33528
33706
  const leadingMatch = line.match(/^[ \t]*/);
33529
33707
  const leading = leadingMatch ? leadingMatch[0] : "";
33530
- if (!isMd && leading.length > 0 && !linesInFencedCodeBlock.has(lineNo) && hasIndentIssue(leading, cfg.format.indent, cfg.format.indentStyle, line)) {
33708
+ if (!indentDisabled && !isMd && !isTemplate && leading.length > 0 && !linesInFencedCodeBlock.has(lineNo) && hasIndentIssue(leading, cfg.format.indent, cfg.format.indentStyle, line)) {
33531
33709
  if (!isSuppressed("indent", lineNo, suppress))
33532
33710
  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` });
33533
33711
  }
@@ -34102,7 +34280,8 @@ async function runUnified(globs, options) {
34102
34280
  };
34103
34281
  return runLint2(globs, lintOpts2);
34104
34282
  }
34105
- if (options.fix != null || options.reporter != null || options.maxWarnings != null || options.dryRun != null)
34283
+ const wantsFix = options.fix === true || options.write === true || options.check === true;
34284
+ if (!wantsFix)
34106
34285
  return runLint2(globs, options);
34107
34286
  const lintOpts = {
34108
34287
  ...options,
@@ -37920,7 +38099,7 @@ var require_package = __commonJS((exports, module) => {
37920
38099
  module.exports = {
37921
38100
  name: "pickier",
37922
38101
  type: "module",
37923
- version: "0.1.24",
38102
+ version: "0.1.26",
37924
38103
  description: "Format, lint and more in a fraction of seconds.",
37925
38104
  author: "Chris Breuer <chris@stacksjs.org>",
37926
38105
  license: "MIT",
package/dist/src/index.js CHANGED
@@ -3648,15 +3648,16 @@ async function getConfig() {
3648
3648
  if (!_config) {
3649
3649
  _config = await loadConfig({
3650
3650
  name: "pickier",
3651
- alias: "code-style",
3651
+ alias: [...CONFIG_ALIASES],
3652
3652
  defaultConfig: defaultConfig2
3653
3653
  });
3654
3654
  }
3655
3655
  return _config;
3656
3656
  }
3657
- var defaultConfig2, _config = null, config3;
3657
+ var CONFIG_ALIASES, defaultConfig2, _config = null, config3;
3658
3658
  var init_config2 = __esm(() => {
3659
3659
  init_dist();
3660
+ CONFIG_ALIASES = ["code-style", "lint"];
3660
3661
  defaultConfig2 = {
3661
3662
  ignores: [
3662
3663
  "**/node_modules/**",
@@ -15172,6 +15173,9 @@ var init_no_unused_vars = __esm(() => {
15172
15173
  if (ctx.filePath.endsWith("/no-unused-vars.ts")) {
15173
15174
  return [];
15174
15175
  }
15176
+ if (ctx.filePath.endsWith(".d.ts")) {
15177
+ return [];
15178
+ }
15175
15179
  const issues = [];
15176
15180
  const opts = ctx.options || {};
15177
15181
  const varsIgnorePattern = typeof opts.varsIgnorePattern === "string" ? opts.varsIgnorePattern : "^_";
@@ -16054,6 +16058,10 @@ var init_no_unused_vars = __esm(() => {
16054
16058
  if (afterFunc.startsWith(":")) {
16055
16059
  continue;
16056
16060
  }
16061
+ const beforeFunc = codeClean.slice(0, m.index).trimEnd();
16062
+ if (beforeFunc.endsWith(".") || beforeFunc.endsWith("?.")) {
16063
+ continue;
16064
+ }
16057
16065
  const funcIdx = m.index;
16058
16066
  const openParenIdx = line.indexOf("(", funcIdx);
16059
16067
  if (openParenIdx === -1)
@@ -16153,6 +16161,7 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
16153
16161
  if (openParenIdx !== -1) {
16154
16162
  let isTypeSignature = false;
16155
16163
  let angleDepthBack = 0;
16164
+ let parenDepthBack = 0;
16156
16165
  for (let k = openParenIdx - 1;k >= 0; k--) {
16157
16166
  const ch = line[k];
16158
16167
  if (ch === ">") {
@@ -16164,10 +16173,13 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
16164
16173
  angleDepthBack--;
16165
16174
  continue;
16166
16175
  }
16167
- isTypeSignature = true;
16168
- break;
16176
+ if (parenDepthBack === 0) {
16177
+ isTypeSignature = true;
16178
+ break;
16179
+ }
16180
+ continue;
16169
16181
  }
16170
- if (ch === ":" && angleDepthBack === 0) {
16182
+ if (ch === ":" && angleDepthBack === 0 && parenDepthBack === 0) {
16171
16183
  isTypeSignature = true;
16172
16184
  break;
16173
16185
  }
@@ -16175,11 +16187,34 @@ ${lines.slice(bodyRange.from + 1, bodyRange.to + 1).join(`
16175
16187
  continue;
16176
16188
  if (ch === ",")
16177
16189
  continue;
16178
- if (ch === "=" || ch === "(" || ch === "{" || ch === "[") {
16190
+ if (ch === ")") {
16191
+ parenDepthBack++;
16192
+ continue;
16193
+ }
16194
+ if (ch === "(") {
16195
+ parenDepthBack--;
16196
+ continue;
16197
+ }
16198
+ if (ch === "|" || ch === "&") {
16199
+ isTypeSignature = true;
16179
16200
  break;
16180
16201
  }
16202
+ if (ch === "=" || ch === "{" || ch === "[") {
16203
+ if (parenDepthBack >= 0)
16204
+ break;
16205
+ continue;
16206
+ }
16181
16207
  if (ch !== " " && ch !== "\t" && !/[\w.]/.test(ch)) {
16182
- break;
16208
+ if (parenDepthBack >= 0)
16209
+ break;
16210
+ }
16211
+ }
16212
+ if (!isTypeSignature) {
16213
+ const beforeParen = line.slice(0, openParenIdx);
16214
+ if (/^\s*(?:export\s+)?(?:declare\s+)?type\s+\w[\w$]*\s*(?:<[^>]*>)?\s*=\s*$/.test(beforeParen)) {
16215
+ isTypeSignature = true;
16216
+ } else if (/\bas\s+$/.test(beforeParen)) {
16217
+ isTypeSignature = true;
16183
16218
  }
16184
16219
  }
16185
16220
  if (isTypeSignature) {
@@ -19009,6 +19044,42 @@ var init_link_image_reference_definitions = __esm(() => {
19009
19044
  }
19010
19045
  }
19011
19046
  return issues;
19047
+ },
19048
+ fix: (text) => {
19049
+ const lines = text.split(/\r?\n/);
19050
+ const inCode = getCodeBlockLines(lines);
19051
+ const defLines = new Map;
19052
+ for (let i = 0;i < lines.length; i++) {
19053
+ if (inCode.has(i))
19054
+ continue;
19055
+ const m = lines[i].match(/^\s*\[([^\]]+)\]:\s*\S+/);
19056
+ if (m)
19057
+ defLines.set(i, m[1].toLowerCase());
19058
+ }
19059
+ if (defLines.size === 0)
19060
+ return text;
19061
+ const usages = new Set;
19062
+ for (let i = 0;i < lines.length; i++) {
19063
+ if (inCode.has(i))
19064
+ continue;
19065
+ if (defLines.has(i))
19066
+ continue;
19067
+ const line = lines[i];
19068
+ const refMatches = line.matchAll(/\[([^\]]+)\](?:\[([^\]]*)\])?(?!\()/g);
19069
+ for (const m of refMatches) {
19070
+ const label = (m[2] && m[2].length > 0 ? m[2] : m[1]).toLowerCase();
19071
+ usages.add(label);
19072
+ }
19073
+ }
19074
+ const toRemove = new Set;
19075
+ for (const [idx, label] of defLines) {
19076
+ if (!usages.has(label))
19077
+ toRemove.add(idx);
19078
+ }
19079
+ if (toRemove.size === 0)
19080
+ return text;
19081
+ return lines.filter((_, idx) => !toRemove.has(idx)).join(`
19082
+ `);
19012
19083
  }
19013
19084
  };
19014
19085
  });
@@ -19026,19 +19097,39 @@ var init_link_image_style = __esm(() => {
19026
19097
  check: (text, ctx) => {
19027
19098
  const issues = [];
19028
19099
  const lines = text.split(/\r?\n/);
19100
+ const inCode = getCodeBlockLines(lines);
19029
19101
  const options = ctx.options || {};
19030
19102
  const style = options.style || "consistent";
19031
- let detectedStyle = null;
19032
- let inFence = false;
19103
+ let target = style === "consistent" ? null : style;
19104
+ if (target === null) {
19105
+ let inlineCount = 0;
19106
+ let refCount = 0;
19107
+ let inHtmlCommentScan = false;
19108
+ for (let i = 0;i < lines.length; i++) {
19109
+ if (inCode.has(i))
19110
+ continue;
19111
+ const line = lines[i];
19112
+ if (line.includes("<!--"))
19113
+ inHtmlCommentScan = true;
19114
+ if (line.includes("-->")) {
19115
+ inHtmlCommentScan = false;
19116
+ continue;
19117
+ }
19118
+ if (inHtmlCommentScan)
19119
+ continue;
19120
+ if (/^\s*\[(?:[^\]]+)\]:\s*\S+/.test(line))
19121
+ continue;
19122
+ const scrubbed = stripInlineCode(line);
19123
+ inlineCount += (scrubbed.match(/\[[^\]]+\]\([^)]+\)/g) || []).length;
19124
+ refCount += (scrubbed.match(/\[[^\]]+\]\[(?:[^\]]*)\]/g) || []).length;
19125
+ }
19126
+ target = refCount > inlineCount ? "reference" : "inline";
19127
+ }
19033
19128
  let inHtmlComment = false;
19034
19129
  for (let i = 0;i < lines.length; i++) {
19035
- const line = lines[i];
19036
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
19037
- inFence = !inFence;
19038
- continue;
19039
- }
19040
- if (inFence)
19130
+ if (inCode.has(i))
19041
19131
  continue;
19132
+ const line = lines[i];
19042
19133
  if (line.includes("<!--"))
19043
19134
  inHtmlComment = true;
19044
19135
  if (line.includes("-->")) {
@@ -19047,64 +19138,104 @@ var init_link_image_style = __esm(() => {
19047
19138
  }
19048
19139
  if (inHtmlComment)
19049
19140
  continue;
19050
- if (line.match(/^\[(?:[^\]]+)\]:\s*\S+/)) {
19141
+ if (line.match(/^\[(?:[^\]]+)\]:\s*\S+/))
19051
19142
  continue;
19052
- }
19053
19143
  const scrubbed = stripInlineCode(line);
19054
19144
  const inlineMatches = scrubbed.matchAll(/\[[^\]]+\]\([^)]+\)/g);
19055
19145
  for (const match of inlineMatches) {
19056
- if (style === "reference") {
19146
+ if (target === "reference") {
19057
19147
  issues.push({
19058
19148
  filePath: ctx.filePath,
19059
19149
  line: i + 1,
19060
19150
  column: match.index + 1,
19061
19151
  ruleId: "markdown/link-image-style",
19062
- message: "Expected reference style link",
19063
- severity: "error"
19152
+ message: style === "consistent" ? "Link style should be consistent throughout document" : "Expected reference style link",
19153
+ severity: style === "consistent" ? "warning" : "error"
19064
19154
  });
19065
- } else if (style === "consistent") {
19066
- if (detectedStyle === null) {
19067
- detectedStyle = "inline";
19068
- } else if (detectedStyle === "reference") {
19069
- issues.push({
19070
- filePath: ctx.filePath,
19071
- line: i + 1,
19072
- column: match.index + 1,
19073
- ruleId: "markdown/link-image-style",
19074
- message: "Link style should be consistent throughout document",
19075
- severity: "warning"
19076
- });
19077
- }
19078
19155
  }
19079
19156
  }
19080
19157
  const refMatches = scrubbed.matchAll(/\[[^\]]+\]\[(?:[^\]]+)\]/g);
19081
19158
  for (const match of refMatches) {
19082
- if (style === "inline") {
19159
+ if (target === "inline") {
19083
19160
  issues.push({
19084
19161
  filePath: ctx.filePath,
19085
19162
  line: i + 1,
19086
19163
  column: match.index + 1,
19087
19164
  ruleId: "markdown/link-image-style",
19088
- message: "Expected inline style link",
19089
- severity: "error"
19165
+ message: style === "consistent" ? "Link style should be consistent throughout document" : "Expected inline style link",
19166
+ severity: style === "consistent" ? "warning" : "error"
19090
19167
  });
19091
- } else if (style === "consistent") {
19092
- if (detectedStyle === null) {
19093
- detectedStyle = "reference";
19094
- } else if (detectedStyle === "inline") {
19095
- issues.push({
19096
- filePath: ctx.filePath,
19097
- line: i + 1,
19098
- column: match.index + 1,
19099
- ruleId: "markdown/link-image-style",
19100
- message: "Link style should be consistent throughout document",
19101
- severity: "warning"
19102
- });
19103
- }
19104
19168
  }
19105
19169
  }
19106
19170
  }
19107
19171
  return issues;
19172
+ },
19173
+ fix: (text, ctx) => {
19174
+ const options = ctx.options || {};
19175
+ const style = options.style || "consistent";
19176
+ const lines = text.split(/\r?\n/);
19177
+ const inCode = getCodeBlockLines(lines);
19178
+ const defs = new Map;
19179
+ for (let i = 0;i < lines.length; i++) {
19180
+ if (inCode.has(i))
19181
+ continue;
19182
+ const m = lines[i].match(/^\s*\[([^\]]+)\]:\s*(\S+)(?:\s+(?:"([^"]*)"|'([^']*)'|\(([^)]*)\)))?\s*$/);
19183
+ if (m) {
19184
+ const label = m[1].toLowerCase();
19185
+ const url = m[2];
19186
+ const title = m[3] ?? m[4] ?? m[5];
19187
+ if (!defs.has(label))
19188
+ defs.set(label, { url, title });
19189
+ }
19190
+ }
19191
+ if (defs.size === 0)
19192
+ return text;
19193
+ let target = style === "reference" ? "reference" : "inline";
19194
+ if (style === "consistent") {
19195
+ let inlineCount = 0;
19196
+ let refCount = 0;
19197
+ for (let i = 0;i < lines.length; i++) {
19198
+ if (inCode.has(i))
19199
+ continue;
19200
+ const line = lines[i];
19201
+ if (/^\s*\[(?:[^\]]+)\]:\s*\S+/.test(line))
19202
+ continue;
19203
+ const scrubbed = stripInlineCode(line);
19204
+ inlineCount += (scrubbed.match(/\[[^\]]+\]\([^)]+\)/g) || []).length;
19205
+ refCount += (scrubbed.match(/\[[^\]]+\]\[(?:[^\]]*)\]/g) || []).length;
19206
+ }
19207
+ target = refCount > inlineCount ? "reference" : "inline";
19208
+ }
19209
+ if (target !== "inline")
19210
+ return text;
19211
+ let changed = false;
19212
+ for (let i = 0;i < lines.length; i++) {
19213
+ if (inCode.has(i))
19214
+ continue;
19215
+ const original = lines[i];
19216
+ if (/^\s*\[(?:[^\]]+)\]:\s*\S+/.test(original))
19217
+ continue;
19218
+ let rewritten = original;
19219
+ for (let pass = 0;pass < 8; pass++) {
19220
+ const next = rewritten.replace(/(!?)\[((?:[^[\]]|\[[^\]]*\]\([^)]*\))+)\]\[([^\]]*)\]/g, (whole, bang, textPart, labelPart) => {
19221
+ const labelKey = (labelPart.trim() === "" ? textPart : labelPart).toLowerCase();
19222
+ const def = defs.get(labelKey);
19223
+ if (!def)
19224
+ return whole;
19225
+ const titlePart = def.title ? ` "${def.title}"` : "";
19226
+ return `${bang}[${textPart}](${def.url}${titlePart})`;
19227
+ });
19228
+ if (next === rewritten)
19229
+ break;
19230
+ rewritten = next;
19231
+ }
19232
+ if (rewritten !== original) {
19233
+ lines[i] = rewritten;
19234
+ changed = true;
19235
+ }
19236
+ }
19237
+ return changed ? lines.join(`
19238
+ `) : text;
19108
19239
  }
19109
19240
  };
19110
19241
  });
@@ -20508,24 +20639,18 @@ var init_single_title = __esm(() => {
20508
20639
  check: (text, ctx) => {
20509
20640
  const issues = [];
20510
20641
  const lines = text.split(/\r?\n/);
20642
+ const inCode = getCodeBlockLines(lines);
20511
20643
  let firstH1Line = -1;
20512
- let inFencedCodeBlock = false;
20513
20644
  for (let i = 0;i < lines.length; i++) {
20645
+ if (inCode.has(i))
20646
+ continue;
20514
20647
  const line = lines[i];
20515
20648
  const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
20516
- if (/^(?:`{3,}|~{3,})/.test(line.trim())) {
20517
- inFencedCodeBlock = !inFencedCodeBlock;
20518
- continue;
20519
- }
20520
- if (inFencedCodeBlock)
20521
- continue;
20522
20649
  let isH1 = false;
20523
- if (/^#\s/.test(line)) {
20650
+ if (/^#\s/.test(line))
20524
20651
  isH1 = true;
20525
- }
20526
- if (/^=+\s*$/.test(nextLine) && line.trim().length > 0) {
20652
+ if (/^=+\s*$/.test(nextLine) && line.trim().length > 0 && !inCode.has(i + 1))
20527
20653
  isH1 = true;
20528
- }
20529
20654
  if (isH1) {
20530
20655
  if (firstH1Line === -1) {
20531
20656
  firstH1Line = i + 1;
@@ -20542,6 +20667,47 @@ var init_single_title = __esm(() => {
20542
20667
  }
20543
20668
  }
20544
20669
  return issues;
20670
+ },
20671
+ fix: (text) => {
20672
+ const lines = text.split(/\r?\n/);
20673
+ const inCode = getCodeBlockLines(lines);
20674
+ const result = [];
20675
+ let seenH1 = false;
20676
+ let changed = false;
20677
+ for (let i = 0;i < lines.length; i++) {
20678
+ if (inCode.has(i)) {
20679
+ result.push(lines[i]);
20680
+ continue;
20681
+ }
20682
+ const line = lines[i];
20683
+ const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
20684
+ const atxH1 = /^#\s/.test(line);
20685
+ const setextH1 = /^=+\s*$/.test(nextLine) && line.trim().length > 0 && !inCode.has(i + 1);
20686
+ if (atxH1) {
20687
+ if (!seenH1) {
20688
+ seenH1 = true;
20689
+ result.push(line);
20690
+ } else {
20691
+ result.push(`#${line}`);
20692
+ changed = true;
20693
+ }
20694
+ continue;
20695
+ }
20696
+ if (setextH1) {
20697
+ if (!seenH1) {
20698
+ seenH1 = true;
20699
+ result.push(line);
20700
+ continue;
20701
+ }
20702
+ result.push(`## ${line.trim()}`);
20703
+ i++;
20704
+ changed = true;
20705
+ continue;
20706
+ }
20707
+ result.push(line);
20708
+ }
20709
+ return changed ? result.join(`
20710
+ `) : text;
20545
20711
  }
20546
20712
  };
20547
20713
  });
@@ -21142,6 +21308,13 @@ function splitFrontmatter(content) {
21142
21308
  }
21143
21309
  return { header: null, body: content };
21144
21310
  }
21311
+ function markdownOnlyWholeFile(rule) {
21312
+ return {
21313
+ meta: rule.meta,
21314
+ check: (content, context) => context.filePath.endsWith(".md") ? rule.check(content, context) : [],
21315
+ fix: rule.fix ? (content, context) => context.filePath.endsWith(".md") ? rule.fix(content, context) : content : undefined
21316
+ };
21317
+ }
21145
21318
  function markdownOnly(rule) {
21146
21319
  return {
21147
21320
  meta: rule.meta,
@@ -21249,7 +21422,7 @@ var init_markdown = __esm(() => {
21249
21422
  "no-multiple-space-blockquote": markdownOnly(noMultipleSpaceBlockquoteRule),
21250
21423
  "no-blanks-blockquote": markdownOnly(noBlanksBlockquoteRule),
21251
21424
  "blanks-around-fences": markdownOnly(blanksAroundFencesRule),
21252
- "single-trailing-newline": markdownOnly(singleTrailingNewlineRule),
21425
+ "single-trailing-newline": markdownOnlyWholeFile(singleTrailingNewlineRule),
21253
21426
  "blanks-around-tables": markdownOnly(blanksAroundTablesRule),
21254
21427
  "no-reversed-links": markdownOnly(noReversedLinksRule),
21255
21428
  "no-bare-urls": markdownOnly(noBareUrlsRule),
@@ -33456,7 +33629,9 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
33456
33629
  const fileExt = filePath.split(".").pop()?.toLowerCase() ?? "";
33457
33630
  const isMd = fileExt === "md";
33458
33631
  const isShell = fileExt === "sh" || fileExt === "bash" || fileExt === "zsh" || fileExt === "ksh" || fileExt === "dash" || /^#!\s*(?:\/usr\/bin\/env\s+)?(?:ba|z|k|da)?sh\b/.test(content);
33459
- const skipQuotesCheck = fileExt === "json" || fileExt === "jsonc" || fileExt === "lock" || isMd || fileExt === "yaml" || fileExt === "yml" || isShell || filePath.endsWith("bun.lock");
33632
+ const quotesRuleSetting = cfg.rules?.quotes ?? cfg.pluginRules?.quotes ?? cfg.pluginRules?.["style/quotes"];
33633
+ const quotesDisabled = quotesRuleSetting === "off";
33634
+ const skipQuotesCheck = quotesDisabled || fileExt === "json" || fileExt === "jsonc" || fileExt === "lock" || isMd || fileExt === "yaml" || fileExt === "yml" || fileExt === "stx" || fileExt === "html" || fileExt === "htm" || fileExt === "vue" || isShell || filePath.endsWith("bun.lock");
33460
33635
  const skipCodeRules = isMd || fileExt === "yaml" || fileExt === "yml" || fileExt === "json" || fileExt === "jsonc" || isShell;
33461
33636
  let quotesReported = false;
33462
33637
  const sevMap = (s) => s === "warn" ? "warning" : s === "error" ? "error" : undefined;
@@ -33524,9 +33699,12 @@ function scanContentOptimized(filePath, content, cfg, suppress, commentLines) {
33524
33699
  quotesReported = true;
33525
33700
  }
33526
33701
  }
33702
+ const indentRuleSetting = cfg.rules?.indent ?? cfg.pluginRules?.indent ?? cfg.pluginRules?.["style/indent"];
33703
+ const indentDisabled = indentRuleSetting === "off";
33704
+ const isTemplate = fileExt === "stx" || fileExt === "html" || fileExt === "htm" || fileExt === "vue";
33527
33705
  const leadingMatch = line.match(/^[ \t]*/);
33528
33706
  const leading = leadingMatch ? leadingMatch[0] : "";
33529
- if (!isMd && leading.length > 0 && !linesInFencedCodeBlock.has(lineNo) && hasIndentIssue(leading, cfg.format.indent, cfg.format.indentStyle, line)) {
33707
+ if (!indentDisabled && !isMd && !isTemplate && leading.length > 0 && !linesInFencedCodeBlock.has(lineNo) && hasIndentIssue(leading, cfg.format.indent, cfg.format.indentStyle, line)) {
33530
33708
  if (!isSuppressed("indent", lineNo, suppress))
33531
33709
  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` });
33532
33710
  }
@@ -34101,7 +34279,8 @@ async function runUnified(globs, options) {
34101
34279
  };
34102
34280
  return runLint2(globs, lintOpts2);
34103
34281
  }
34104
- if (options.fix != null || options.reporter != null || options.maxWarnings != null || options.dryRun != null)
34282
+ const wantsFix = options.fix === true || options.write === true || options.check === true;
34283
+ if (!wantsFix)
34105
34284
  return runLint2(globs, options);
34106
34285
  const lintOpts = {
34107
34286
  ...options,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pickier",
3
3
  "type": "module",
4
- "version": "0.1.24",
4
+ "version": "0.1.26",
5
5
  "description": "Format, lint and more in a fraction of seconds.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",