eslint-plugin-markdown-preferences 0.29.0 β†’ 0.29.1

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/README.md CHANGED
@@ -13,17 +13,15 @@ A specialized ESLint plugin that helps enforce consistent writing style and form
13
13
 
14
14
  ## πŸ“› Features
15
15
 
16
- - πŸ“ **Comprehensive style enforcement**\
17
- Unifies document expression and description style: heading casing, table header casing, inline code/link usage, emoji notation, and more.
18
- - 🧩 **Notation and formatting consistency**\
19
- Standardizes Markdown notation: list markers, code fences, link/reference style, thematic breaks, and table formatting.
20
- - 🎨 **Whitespace and decorative rules**\
21
- Controls indentation, spacing, line breaks, trailing spaces, and decorative elements for clean, readable Markdown.
22
- - πŸ”§ **Auto-fix support**\
23
- Most rules support ESLint's `--fix` option for effortless formatting and correction.
24
- - βš™οΈ **Flexible configuration**\
25
- Provides both "recommended" and "standard" configs, and allows you to finely customize formatting and rules to suit your preferences and Markdown style.
26
- - 🌐 **Live demo & documentation**\
16
+ - πŸ“ **Comprehensive style enforcement**
17
+ Enforces consistent heading casing, table header casing, inline code/link usage, emoji notation, and more for unified document style.
18
+ - 🧩 **Powerful formatting consistency**
19
+ Strongly standardizes Markdown formatting: whitespace, indentation, spacing, line breaks, list markers, code fences, links, references, thematic breaks, tables, and decorative elementsβ€”ensuring documents are clean and uniform.
20
+ - πŸš€ **Extended Markdown syntax support**
21
+ Supports custom containers, mathematical expressions, and other extended syntax for high compatibility with VitePress.
22
+ - πŸ”§ **Auto-fix support**
23
+ Most rules support ESLint's `--fix` option for automatic correction.
24
+ - 🌐 **Online Demo & Documentation**
27
25
  Try it instantly in the [Online Demo](https://eslint-online-playground.netlify.app/#eslint-plugin-markdown-preferences) and see [full documentation][documentation site].
28
26
 
29
27
  <!--DOCS_IGNORE_START-->
package/lib/index.d.ts CHANGED
@@ -516,7 +516,7 @@ declare namespace meta_d_exports {
516
516
  export { name, version };
517
517
  }
518
518
  declare const name: "eslint-plugin-markdown-preferences";
519
- declare const version: "0.29.0";
519
+ declare const version: "0.29.1";
520
520
  //#endregion
521
521
  //#region src/language/ast-types.d.ts
522
522
  type Node = mdast.Node;
package/lib/index.js CHANGED
@@ -10696,257 +10696,16 @@ var table_leading_trailing_pipes_default = createRule("table-leading-trailing-pi
10696
10696
  });
10697
10697
 
10698
10698
  //#endregion
10699
- //#region src/rules/table-pipe-alignment.ts
10700
- var table_pipe_alignment_default = createRule("table-pipe-alignment", {
10701
- meta: {
10702
- type: "layout",
10703
- docs: {
10704
- description: "enforce consistent alignment of table pipes",
10705
- categories: ["standard"],
10706
- listCategory: "Decorative"
10707
- },
10708
- fixable: "code",
10709
- hasSuggestions: false,
10710
- schema: [{
10711
- type: "object",
10712
- properties: { column: { enum: ["minimum", "consistent"] } },
10713
- additionalProperties: false
10714
- }],
10715
- messages: {
10716
- addSpaces: "Table pipe should be aligned at column {{expected}} (add {{count}} character{{plural}}).",
10717
- removeSpaces: "Table pipe should be aligned at column {{expected}} (remove {{count}} character{{plural}})."
10718
- }
10719
- },
10720
- create(context) {
10721
- const sourceCode = context.sourceCode;
10722
- const columnOption = (context.options[0] || {}).column || "minimum";
10723
- class TableContext {
10724
- rows;
10725
- columnCount;
10726
- _cacheHasSpaceBetweenContentAndTrailingPipe = /* @__PURE__ */ new Map();
10727
- _cacheExpectedPipePosition = /* @__PURE__ */ new Map();
10728
- constructor(parsed) {
10729
- const rows = [parsedTableRowToRowData(parsed.headerRow), parsedTableDelimiterRowToRowData(parsed.delimiterRow)];
10730
- for (const bodyRow of parsed.bodyRows) rows.push(parsedTableRowToRowData(bodyRow));
10731
- this.rows = rows;
10732
- let columnCount = 0;
10733
- for (const row of rows) columnCount = Math.max(columnCount, row.cells.length);
10734
- this.columnCount = columnCount;
10735
- }
10736
- /**
10737
- * Get the expected pipe position for the index
10738
- */
10739
- getExpectedPipePosition(pipeIndex) {
10740
- let v = this._cacheExpectedPipePosition.get(pipeIndex);
10741
- if (v !== void 0) return v;
10742
- v = this._computeExpectedPipePositionWithoutCache(pipeIndex);
10743
- this._cacheExpectedPipePosition.set(pipeIndex, v);
10744
- return v;
10745
- }
10746
- /**
10747
- * Check if there is at least one space between content and trailing pipe
10748
- * for the index
10749
- *
10750
- * This is used to determine if the pipe should be aligned with a space before it.
10751
- */
10752
- hasSpaceBetweenContentAndTrailingPipe(pipeIndex) {
10753
- if (pipeIndex === 0) return false;
10754
- let v = this._cacheHasSpaceBetweenContentAndTrailingPipe.get(pipeIndex);
10755
- if (v != null) return v;
10756
- v = this._hasSpaceBetweenContentAndTrailingPipeWithoutCache(pipeIndex);
10757
- this._cacheHasSpaceBetweenContentAndTrailingPipe.set(pipeIndex, v);
10758
- return v;
10759
- }
10760
- /**
10761
- * Get the expected pipe position for the index
10762
- */
10763
- _computeExpectedPipePositionWithoutCache(pipeIndex) {
10764
- if (pipeIndex === 0) {
10765
- const firstCell = this.rows[0].cells[0];
10766
- const firstToken = firstCell.leadingPipe ?? firstCell.content;
10767
- if (!firstToken) return null;
10768
- return getTextWidth(sourceCode.lines[firstToken.loc.start.line - 1].slice(0, firstToken.loc.start.column - 1));
10769
- }
10770
- if (columnOption === "minimum") return this.getMinimumPipePosition(pipeIndex);
10771
- else if (columnOption === "consistent") {
10772
- const columnIndex = pipeIndex - 1;
10773
- for (const row of this.rows) {
10774
- if (row.cells.length <= columnIndex) continue;
10775
- const cell = row.cells[columnIndex];
10776
- if (cell.type === "delimiter" || !cell.trailingPipe) continue;
10777
- const width = getTextWidth(sourceCode.lines[cell.trailingPipe.loc.start.line - 1].slice(0, cell.trailingPipe.loc.start.column - 1));
10778
- return Math.max(width, this.getMinimumPipePosition(pipeIndex) || 0);
10779
- }
10780
- }
10781
- return null;
10782
- }
10783
- /**
10784
- * Get the minimum pipe position for the index
10785
- */
10786
- getMinimumPipePosition(pipeIndex) {
10787
- const needSpaceBeforePipe = this.hasSpaceBetweenContentAndTrailingPipe(pipeIndex);
10788
- let maxWidth = 0;
10789
- const columnIndex = pipeIndex - 1;
10790
- for (const row of this.rows) {
10791
- if (row.cells.length <= columnIndex) continue;
10792
- const cell = row.cells[columnIndex];
10793
- let width;
10794
- if (cell.type === "delimiter") {
10795
- const minimumDelimiterLength = getMinimumDelimiterLength(cell.align);
10796
- width = getTextWidth(sourceCode.lines[cell.delimiter.loc.start.line - 1].slice(0, cell.delimiter.loc.start.column - 1)) + minimumDelimiterLength;
10797
- } else {
10798
- if (!cell.content) continue;
10799
- width = getTextWidth(sourceCode.lines[cell.content.loc.end.line - 1].slice(0, cell.content.loc.end.column - 1));
10800
- }
10801
- if (needSpaceBeforePipe) width += 1;
10802
- maxWidth = Math.max(maxWidth, width);
10803
- }
10804
- return maxWidth;
10805
- }
10806
- /**
10807
- * Check if there is at least one space between content and trailing pipe
10808
- */
10809
- _hasSpaceBetweenContentAndTrailingPipeWithoutCache(pipeIndex) {
10810
- const columnIndex = pipeIndex - 1;
10811
- for (const row of this.rows) {
10812
- if (row.cells.length <= columnIndex) continue;
10813
- const cell = row.cells[columnIndex];
10814
- if (!cell.trailingPipe) continue;
10815
- let content;
10816
- if (cell.type === "delimiter") content = cell.delimiter;
10817
- else {
10818
- if (!cell.content) continue;
10819
- content = cell.content;
10820
- }
10821
- if (content.range[1] < cell.trailingPipe.range[0]) continue;
10822
- return false;
10823
- }
10824
- return true;
10825
- }
10826
- }
10827
- /**
10828
- * Verify the table pipes
10829
- */
10830
- function verifyTablePipes(table) {
10831
- const targetRows = [...table.rows];
10832
- for (const row of targetRows) for (let pipeIndex = 0; pipeIndex <= table.columnCount; pipeIndex++) if (!verifyRowPipe(row, pipeIndex, table)) break;
10833
- }
10834
- /**
10835
- * Verify the pipe in the row
10836
- */
10837
- function verifyRowPipe(row, pipeIndex, table) {
10838
- let cellIndex;
10839
- let pipe;
10840
- if (pipeIndex === 0) {
10841
- cellIndex = 0;
10842
- pipe = "leadingPipe";
10843
- } else {
10844
- cellIndex = pipeIndex - 1;
10845
- pipe = "trailingPipe";
10846
- }
10847
- if (row.cells.length <= cellIndex) return true;
10848
- const cell = row.cells[cellIndex];
10849
- const pipeToken = cell[pipe];
10850
- if (!pipeToken) return true;
10851
- return verifyPipe(pipeToken, pipeIndex, table, cell);
10852
- }
10853
- /**
10854
- * Verify the pipe position
10855
- */
10856
- function verifyPipe(pipe, pipeIndex, table, cell) {
10857
- const expected = table.getExpectedPipePosition(pipeIndex);
10858
- if (expected == null) return true;
10859
- const actual = getTextWidth(sourceCode.lines[pipe.loc.start.line - 1].slice(0, pipe.loc.start.column - 1));
10860
- const diff = expected - actual;
10861
- if (diff === 0) return true;
10862
- context.report({
10863
- loc: pipe.loc,
10864
- messageId: diff > 0 ? "addSpaces" : "removeSpaces",
10865
- data: {
10866
- expected: String(expected),
10867
- count: String(Math.abs(diff)),
10868
- plural: Math.abs(diff) === 1 ? "" : "s"
10869
- },
10870
- fix(fixer) {
10871
- if (diff > 0) {
10872
- if (pipeIndex === 0 || cell.type === "cell") return fixer.insertTextBeforeRange(pipe.range, " ".repeat(diff));
10873
- return fixer.insertTextAfterRange([cell.delimiter.range[0], cell.delimiter.range[0] + 1], "-".repeat(diff));
10874
- }
10875
- const baseEdit = fixRemoveSpaces();
10876
- if (baseEdit) return baseEdit;
10877
- if (pipeIndex === 0 || cell.type === "cell") return null;
10878
- const beforeDelimiter = sourceCode.lines[cell.delimiter.loc.start.line - 1].slice(0, cell.delimiter.loc.start.column - 1);
10879
- const widthBeforeDelimiter = getTextWidth(beforeDelimiter);
10880
- const newLength = expected - widthBeforeDelimiter;
10881
- const minimumDelimiterLength = getMinimumDelimiterLength(cell.align);
10882
- const spaceAfter = table.hasSpaceBetweenContentAndTrailingPipe(pipeIndex) ? " " : "";
10883
- if (newLength < minimumDelimiterLength + spaceAfter.length) return null;
10884
- const delimiterPrefix = cell.align === "left" || cell.align === "center" ? ":" : "";
10885
- const delimiterSuffix = (cell.align === "right" || cell.align === "center" ? ":" : "") + spaceAfter;
10886
- const newDelimiter = "-".repeat(newLength - delimiterPrefix.length - delimiterSuffix.length);
10887
- return fixer.replaceTextRange([cell.delimiter.range[0], pipe.range[0]], delimiterPrefix + newDelimiter + delimiterSuffix);
10888
- /**
10889
- * Fixer to remove spaces before the pipe
10890
- */
10891
- function fixRemoveSpaces() {
10892
- const beforePipe = sourceCode.lines[pipe.loc.start.line - 1].slice(0, pipe.loc.start.column - 1);
10893
- const trimmedBeforePipe = beforePipe.trimEnd();
10894
- const spacesBeforePipeLength = beforePipe.length - trimmedBeforePipe.length;
10895
- const widthBeforePipe = getTextWidth(trimmedBeforePipe);
10896
- const newSpacesLength = expected - widthBeforePipe;
10897
- if (newSpacesLength < (table.hasSpaceBetweenContentAndTrailingPipe(pipeIndex) ? 1 : 0)) return null;
10898
- return fixer.replaceTextRange([pipe.range[0] - spacesBeforePipeLength, pipe.range[0]], " ".repeat(newSpacesLength));
10899
- }
10900
- }
10901
- });
10902
- return false;
10903
- }
10904
- /**
10905
- * Get the minimum delimiter length based on alignment
10906
- */
10907
- function getMinimumDelimiterLength(align) {
10908
- return align === "none" ? 1 : align === "center" ? 3 : 2;
10909
- }
10910
- return { table(node) {
10911
- const parsed = parseTable(sourceCode, node);
10912
- if (!parsed) return;
10913
- verifyTablePipes(new TableContext(parsed));
10914
- } };
10915
- }
10916
- });
10699
+ //#region src/rules/table-pipe-spacing.ts
10700
+ const currentOption = /* @__PURE__ */ new WeakMap();
10917
10701
  /**
10918
- * Convert a parsed table row to row data
10702
+ * Get the current options for the given source code.
10703
+ * This is a method that allows you to access the configuration of this rule from another rule.
10919
10704
  */
10920
- function parsedTableRowToRowData(parsedRow) {
10921
- return { cells: parsedRow.cells.map((cell, index) => {
10922
- const nextCell = index + 1 < parsedRow.cells.length ? parsedRow.cells[index + 1] : null;
10923
- return {
10924
- type: "cell",
10925
- leadingPipe: cell.leadingPipe,
10926
- content: cell.cell,
10927
- trailingPipe: nextCell ? nextCell.leadingPipe : parsedRow.trailingPipe
10928
- };
10929
- }) };
10705
+ function getCurrentTablePipeSpacingOption(sourceCode) {
10706
+ return currentOption.get(sourceCode) ?? null;
10930
10707
  }
10931
10708
  /**
10932
- * Convert a parsed table delimiter row to row data
10933
- */
10934
- function parsedTableDelimiterRowToRowData(parsedDelimiterRow) {
10935
- return { cells: parsedDelimiterRow.delimiters.map((cell, index) => {
10936
- const nextCell = index + 1 < parsedDelimiterRow.delimiters.length ? parsedDelimiterRow.delimiters[index + 1] : null;
10937
- return {
10938
- type: "delimiter",
10939
- leadingPipe: cell.leadingPipe,
10940
- delimiter: cell.delimiter,
10941
- align: cell.delimiter.align,
10942
- trailingPipe: nextCell ? nextCell.leadingPipe : parsedDelimiterRow.trailingPipe
10943
- };
10944
- }) };
10945
- }
10946
-
10947
- //#endregion
10948
- //#region src/rules/table-pipe-spacing.ts
10949
- /**
10950
10709
  * Parsed options
10951
10710
  */
10952
10711
  function parseOptions(options) {
@@ -11060,6 +10819,7 @@ var table_pipe_spacing_default = createRule("table-pipe-spacing", {
11060
10819
  create(context) {
11061
10820
  const sourceCode = context.sourceCode;
11062
10821
  const options = parseOptions(context.options[0]);
10822
+ currentOption.set(sourceCode, options);
11063
10823
  /**
11064
10824
  * Verify for the leading pipe.
11065
10825
  */
@@ -11317,6 +11077,256 @@ var table_pipe_spacing_default = createRule("table-pipe-spacing", {
11317
11077
  }
11318
11078
  });
11319
11079
 
11080
+ //#endregion
11081
+ //#region src/rules/table-pipe-alignment.ts
11082
+ var table_pipe_alignment_default = createRule("table-pipe-alignment", {
11083
+ meta: {
11084
+ type: "layout",
11085
+ docs: {
11086
+ description: "enforce consistent alignment of table pipes",
11087
+ categories: ["standard"],
11088
+ listCategory: "Decorative"
11089
+ },
11090
+ fixable: "code",
11091
+ hasSuggestions: false,
11092
+ schema: [{
11093
+ type: "object",
11094
+ properties: { column: { enum: ["minimum", "consistent"] } },
11095
+ additionalProperties: false
11096
+ }],
11097
+ messages: {
11098
+ addSpaces: "Table pipe should be aligned at column {{expected}} (add {{count}} character{{plural}}).",
11099
+ removeSpaces: "Table pipe should be aligned at column {{expected}} (remove {{count}} character{{plural}})."
11100
+ }
11101
+ },
11102
+ create(context) {
11103
+ const sourceCode = context.sourceCode;
11104
+ const columnOption = (context.options[0] || {}).column || "minimum";
11105
+ class TableContext {
11106
+ rows;
11107
+ columnCount;
11108
+ _cacheHasSpaceBetweenContentAndTrailingPipe = /* @__PURE__ */ new Map();
11109
+ _cacheExpectedPipePosition = /* @__PURE__ */ new Map();
11110
+ constructor(parsed) {
11111
+ const rows = [parsedTableRowToRowData(parsed.headerRow), parsedTableDelimiterRowToRowData(parsed.delimiterRow)];
11112
+ for (const bodyRow of parsed.bodyRows) rows.push(parsedTableRowToRowData(bodyRow));
11113
+ this.rows = rows;
11114
+ let columnCount = 0;
11115
+ for (const row of rows) columnCount = Math.max(columnCount, row.cells.length);
11116
+ this.columnCount = columnCount;
11117
+ }
11118
+ /**
11119
+ * Get the expected pipe position for the index
11120
+ */
11121
+ getExpectedPipePosition(pipeIndex) {
11122
+ let v = this._cacheExpectedPipePosition.get(pipeIndex);
11123
+ if (v !== void 0) return v;
11124
+ v = this._computeExpectedPipePositionWithoutCache(pipeIndex);
11125
+ this._cacheExpectedPipePosition.set(pipeIndex, v);
11126
+ return v;
11127
+ }
11128
+ /**
11129
+ * Check if there is at least one space between content and trailing pipe
11130
+ * for the index
11131
+ *
11132
+ * This is used to determine if the pipe should be aligned with a space before it.
11133
+ */
11134
+ hasSpaceBetweenContentAndTrailingPipe(pipeIndex) {
11135
+ if (pipeIndex === 0) return false;
11136
+ let v = this._cacheHasSpaceBetweenContentAndTrailingPipe.get(pipeIndex);
11137
+ if (v != null) return v;
11138
+ v = this._hasSpaceBetweenContentAndTrailingPipeWithoutCache(pipeIndex);
11139
+ this._cacheHasSpaceBetweenContentAndTrailingPipe.set(pipeIndex, v);
11140
+ return v;
11141
+ }
11142
+ /**
11143
+ * Get the expected pipe position for the index
11144
+ */
11145
+ _computeExpectedPipePositionWithoutCache(pipeIndex) {
11146
+ if (pipeIndex === 0) {
11147
+ const firstCell = this.rows[0].cells[0];
11148
+ const firstToken = firstCell.leadingPipe ?? firstCell.content;
11149
+ if (!firstToken) return null;
11150
+ return getTextWidth(sourceCode.lines[firstToken.loc.start.line - 1].slice(0, firstToken.loc.start.column - 1));
11151
+ }
11152
+ if (columnOption === "minimum") return this.getMinimumPipePosition(pipeIndex);
11153
+ else if (columnOption === "consistent") {
11154
+ const columnIndex = pipeIndex - 1;
11155
+ for (const row of this.rows) {
11156
+ if (row.cells.length <= columnIndex) continue;
11157
+ const cell = row.cells[columnIndex];
11158
+ if (cell.type === "delimiter" || !cell.trailingPipe) continue;
11159
+ const width = getTextWidth(sourceCode.lines[cell.trailingPipe.loc.start.line - 1].slice(0, cell.trailingPipe.loc.start.column - 1));
11160
+ return Math.max(width, this.getMinimumPipePosition(pipeIndex) || 0);
11161
+ }
11162
+ }
11163
+ return null;
11164
+ }
11165
+ /**
11166
+ * Get the minimum pipe position for the index
11167
+ */
11168
+ getMinimumPipePosition(pipeIndex) {
11169
+ const spacingRuleOptions = getCurrentTablePipeSpacingOption(sourceCode);
11170
+ const needSpaceBeforePipe = spacingRuleOptions ? spacingRuleOptions.trailingSpace === "always" : this.hasSpaceBetweenContentAndTrailingPipe(pipeIndex);
11171
+ let maxWidth = 0;
11172
+ const columnIndex = pipeIndex - 1;
11173
+ for (const row of this.rows) {
11174
+ if (row.cells.length <= columnIndex) continue;
11175
+ const cell = row.cells[columnIndex];
11176
+ let width;
11177
+ if (cell.type === "delimiter") {
11178
+ const minimumDelimiterLength = getMinimumDelimiterLength(cell.align);
11179
+ width = getTextWidth(sourceCode.lines[cell.delimiter.loc.start.line - 1].slice(0, cell.delimiter.loc.start.column - 1)) + minimumDelimiterLength;
11180
+ } else {
11181
+ if (!cell.content) continue;
11182
+ width = getTextWidth(sourceCode.lines[cell.content.loc.end.line - 1].slice(0, cell.content.loc.end.column - 1));
11183
+ }
11184
+ if (needSpaceBeforePipe) width += 1;
11185
+ maxWidth = Math.max(maxWidth, width);
11186
+ }
11187
+ return maxWidth;
11188
+ }
11189
+ /**
11190
+ * Check if there is at least one space between content and trailing pipe
11191
+ */
11192
+ _hasSpaceBetweenContentAndTrailingPipeWithoutCache(pipeIndex) {
11193
+ const columnIndex = pipeIndex - 1;
11194
+ for (const row of this.rows) {
11195
+ if (row.cells.length <= columnIndex) continue;
11196
+ const cell = row.cells[columnIndex];
11197
+ if (!cell.trailingPipe) continue;
11198
+ let content;
11199
+ if (cell.type === "delimiter") content = cell.delimiter;
11200
+ else {
11201
+ if (!cell.content) continue;
11202
+ content = cell.content;
11203
+ }
11204
+ if (content.range[1] < cell.trailingPipe.range[0]) continue;
11205
+ return false;
11206
+ }
11207
+ return true;
11208
+ }
11209
+ }
11210
+ /**
11211
+ * Verify the table pipes
11212
+ */
11213
+ function verifyTablePipes(table) {
11214
+ const targetRows = [...table.rows];
11215
+ for (const row of targetRows) for (let pipeIndex = 0; pipeIndex <= table.columnCount; pipeIndex++) if (!verifyRowPipe(row, pipeIndex, table)) break;
11216
+ }
11217
+ /**
11218
+ * Verify the pipe in the row
11219
+ */
11220
+ function verifyRowPipe(row, pipeIndex, table) {
11221
+ let cellIndex;
11222
+ let pipe;
11223
+ if (pipeIndex === 0) {
11224
+ cellIndex = 0;
11225
+ pipe = "leadingPipe";
11226
+ } else {
11227
+ cellIndex = pipeIndex - 1;
11228
+ pipe = "trailingPipe";
11229
+ }
11230
+ if (row.cells.length <= cellIndex) return true;
11231
+ const cell = row.cells[cellIndex];
11232
+ const pipeToken = cell[pipe];
11233
+ if (!pipeToken) return true;
11234
+ return verifyPipe(pipeToken, pipeIndex, table, cell);
11235
+ }
11236
+ /**
11237
+ * Verify the pipe position
11238
+ */
11239
+ function verifyPipe(pipe, pipeIndex, table, cell) {
11240
+ const expected = table.getExpectedPipePosition(pipeIndex);
11241
+ if (expected == null) return true;
11242
+ const actual = getTextWidth(sourceCode.lines[pipe.loc.start.line - 1].slice(0, pipe.loc.start.column - 1));
11243
+ const diff = expected - actual;
11244
+ if (diff === 0) return true;
11245
+ context.report({
11246
+ loc: pipe.loc,
11247
+ messageId: diff > 0 ? "addSpaces" : "removeSpaces",
11248
+ data: {
11249
+ expected: String(expected),
11250
+ count: String(Math.abs(diff)),
11251
+ plural: Math.abs(diff) === 1 ? "" : "s"
11252
+ },
11253
+ fix(fixer) {
11254
+ if (diff > 0) {
11255
+ if (pipeIndex === 0 || cell.type === "cell") return fixer.insertTextBeforeRange(pipe.range, " ".repeat(diff));
11256
+ return fixer.insertTextAfterRange([cell.delimiter.range[0], cell.delimiter.range[0] + 1], "-".repeat(diff));
11257
+ }
11258
+ const baseEdit = fixRemoveSpaces();
11259
+ if (baseEdit) return baseEdit;
11260
+ if (pipeIndex === 0 || cell.type === "cell") return null;
11261
+ const beforeDelimiter = sourceCode.lines[cell.delimiter.loc.start.line - 1].slice(0, cell.delimiter.loc.start.column - 1);
11262
+ const widthBeforeDelimiter = getTextWidth(beforeDelimiter);
11263
+ const newLength = expected - widthBeforeDelimiter;
11264
+ const minimumDelimiterLength = getMinimumDelimiterLength(cell.align);
11265
+ const spaceAfter = table.hasSpaceBetweenContentAndTrailingPipe(pipeIndex) ? " " : "";
11266
+ if (newLength < minimumDelimiterLength + spaceAfter.length) return null;
11267
+ const delimiterPrefix = cell.align === "left" || cell.align === "center" ? ":" : "";
11268
+ const delimiterSuffix = (cell.align === "right" || cell.align === "center" ? ":" : "") + spaceAfter;
11269
+ const newDelimiter = "-".repeat(newLength - delimiterPrefix.length - delimiterSuffix.length);
11270
+ return fixer.replaceTextRange([cell.delimiter.range[0], pipe.range[0]], delimiterPrefix + newDelimiter + delimiterSuffix);
11271
+ /**
11272
+ * Fixer to remove spaces before the pipe
11273
+ */
11274
+ function fixRemoveSpaces() {
11275
+ const beforePipe = sourceCode.lines[pipe.loc.start.line - 1].slice(0, pipe.loc.start.column - 1);
11276
+ const trimmedBeforePipe = beforePipe.trimEnd();
11277
+ const spacesBeforePipeLength = beforePipe.length - trimmedBeforePipe.length;
11278
+ const widthBeforePipe = getTextWidth(trimmedBeforePipe);
11279
+ const newSpacesLength = expected - widthBeforePipe;
11280
+ if (newSpacesLength < (table.hasSpaceBetweenContentAndTrailingPipe(pipeIndex) ? 1 : 0)) return null;
11281
+ return fixer.replaceTextRange([pipe.range[0] - spacesBeforePipeLength, pipe.range[0]], " ".repeat(newSpacesLength));
11282
+ }
11283
+ }
11284
+ });
11285
+ return false;
11286
+ }
11287
+ /**
11288
+ * Get the minimum delimiter length based on alignment
11289
+ */
11290
+ function getMinimumDelimiterLength(align) {
11291
+ return align === "none" ? 1 : align === "center" ? 3 : 2;
11292
+ }
11293
+ return { table(node) {
11294
+ const parsed = parseTable(sourceCode, node);
11295
+ if (!parsed) return;
11296
+ verifyTablePipes(new TableContext(parsed));
11297
+ } };
11298
+ }
11299
+ });
11300
+ /**
11301
+ * Convert a parsed table row to row data
11302
+ */
11303
+ function parsedTableRowToRowData(parsedRow) {
11304
+ return { cells: parsedRow.cells.map((cell, index) => {
11305
+ const nextCell = index + 1 < parsedRow.cells.length ? parsedRow.cells[index + 1] : null;
11306
+ return {
11307
+ type: "cell",
11308
+ leadingPipe: cell.leadingPipe,
11309
+ content: cell.cell,
11310
+ trailingPipe: nextCell ? nextCell.leadingPipe : parsedRow.trailingPipe
11311
+ };
11312
+ }) };
11313
+ }
11314
+ /**
11315
+ * Convert a parsed table delimiter row to row data
11316
+ */
11317
+ function parsedTableDelimiterRowToRowData(parsedDelimiterRow) {
11318
+ return { cells: parsedDelimiterRow.delimiters.map((cell, index) => {
11319
+ const nextCell = index + 1 < parsedDelimiterRow.delimiters.length ? parsedDelimiterRow.delimiters[index + 1] : null;
11320
+ return {
11321
+ type: "delimiter",
11322
+ leadingPipe: cell.leadingPipe,
11323
+ delimiter: cell.delimiter,
11324
+ align: cell.delimiter.align,
11325
+ trailingPipe: nextCell ? nextCell.leadingPipe : parsedDelimiterRow.trailingPipe
11326
+ };
11327
+ }) };
11328
+ }
11329
+
11320
11330
  //#endregion
11321
11331
  //#region src/rules/thematic-break-character-style.ts
11322
11332
  var thematic_break_character_style_default = createRule("thematic-break-character-style", {
@@ -11678,7 +11688,7 @@ var meta_exports = /* @__PURE__ */ __export({
11678
11688
  version: () => version
11679
11689
  });
11680
11690
  const name = "eslint-plugin-markdown-preferences";
11681
- const version = "0.29.0";
11691
+ const version = "0.29.1";
11682
11692
 
11683
11693
  //#endregion
11684
11694
  //#region src/language/extensions/micromark-custom-container.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-markdown-preferences",
3
- "version": "0.29.0",
3
+ "version": "0.29.1",
4
4
  "description": "ESLint plugin that enforces our markdown preferences",
5
5
  "type": "module",
6
6
  "exports": {
@@ -25,7 +25,7 @@
25
25
  "markdownlint": "npx -y markdownlint-cli2 .",
26
26
  "test": "npm run mocha -- \"tests/src/**/*.ts\" --reporter=dot --timeout=60000",
27
27
  "test:debug": "node --experimental-strip-types --experimental-transform-types ./node_modules/mocha/bin/mocha.js \"tests/src/**/*.ts\" --reporter=dot --timeout=60000",
28
- "cover": "c8 --reporter=lcov npm run test",
28
+ "cover": "c8 --reporter=lcov --reporter=text npm run test:debug",
29
29
  "test:update": "npm run mocha -- \"tests/src/**/*.ts\" --reporter=dot --update",
30
30
  "update": "npm run ts -- ./tools/update.ts && npm run eslint-fix",
31
31
  "update:resources": "npm run ts -- ./tools/update-resources.ts && npm run eslint-fix",