eslint-plugin-markdown-preferences 0.26.0 → 0.27.0

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
@@ -58,17 +58,18 @@ Example **eslint.config.js**:
58
58
  import { defineConfig } from "eslint/config";
59
59
  // import markdown from "@eslint/markdown";
60
60
  import markdownPreferences from "eslint-plugin-markdown-preferences";
61
- export default [
61
+ export default defineConfig([
62
62
  // add more generic rule sets here, such as:
63
63
  // markdown.configs.recommended,
64
64
  markdownPreferences.configs.recommended,
65
65
  {
66
+ files: ["**/*.md", "*.md"],
66
67
  rules: {
67
68
  // override/add rules settings here, such as:
68
69
  // 'markdown-preferences/prefer-linked-words': 'error'
69
70
  },
70
71
  },
71
- ];
72
+ ]);
72
73
  ```
73
74
 
74
75
  This plugin provides configs:
@@ -78,6 +79,49 @@ This plugin provides configs:
78
79
 
79
80
  See [the rule list](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/) to get the `rules` that this plugin provides.
80
81
 
82
+ ##### Using Extended Syntax
83
+
84
+ This plugin experimentally supports some extended Markdown syntax.\
85
+ To use these extended syntaxes, set the `markdown-preferences/extended-syntax` language option.
86
+
87
+ ```js
88
+ import { defineConfig } from "eslint/config";
89
+ import markdownPreferences from "eslint-plugin-markdown-preferences";
90
+ export default defineConfig([
91
+ {
92
+ extends: [markdownPreferences.configs.recommended],
93
+ language: "markdown-preferences/extended-syntax",
94
+ },
95
+ ]);
96
+ ```
97
+
98
+ The following syntaxes are supported:
99
+
100
+ - [Custom Containers](https://vitepress.dev/guide/markdown#custom-containers)\
101
+ Example:
102
+
103
+ ```md
104
+ ::: warning
105
+ This is a warning box.
106
+ :::
107
+ ```
108
+
109
+ - [Mathematical Expressions](https://docs.github.com/get-started/writing-on-github/working-with-advanced-formatting/writing-mathematical-expressions)\
110
+ Example:
111
+
112
+ ```md
113
+ $$
114
+ E = mc^2
115
+ $$
116
+ ```
117
+
118
+ - [VitePress-style Import Code Snippets](https://vitepress.dev/guide/markdown#import-code-snippets) syntax using triple left angle brackets\
119
+ Example:
120
+
121
+ ```md
122
+ <<< @/filepath
123
+ ```
124
+
81
125
  #### Legacy Config (`.eslintrc`)
82
126
 
83
127
  Is not supported.
package/lib/index.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import markdown from "@eslint/markdown";
2
+ import * as math from "mdast-util-math";
2
3
  import * as _eslint_core0 from "@eslint/core";
3
- import { RuleDefinition } from "@eslint/core";
4
+ import { File, Language, OkParseResult, ParseResult, RuleDefinition, SourceLocation } from "@eslint/core";
4
5
  import { ESLint, Linter } from "eslint";
6
+ import * as eslint from "@eslint/markdown/types";
7
+ import { MarkdownLanguageContext, MarkdownLanguageOptions } from "@eslint/markdown/types";
8
+ import * as mdast from "mdast";
9
+ import { TextSourceCodeBase } from "@eslint/plugin-kit";
5
10
 
6
11
  //#region src/rule-types.d.ts
7
12
  declare module 'eslint' {
@@ -380,12 +385,12 @@ type MarkdownPreferencesOrderedListMarkerStyle = [] | [{
380
385
  }[];
381
386
  }];
382
387
  type MarkdownPreferencesPaddingLineBetweenBlocks = {
383
- prev: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"))[]] | {
384
- type: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"))[]]);
388
+ prev: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"))[]] | {
389
+ type: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"))[]]);
385
390
  in?: ("list" | "blockquote" | "footnote-definition");
386
391
  });
387
- next: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"))[]] | {
388
- type: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "*"))[]]);
392
+ next: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"))[]] | {
393
+ type: (("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*") | [("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"), ...(("blockquote" | "code" | "heading" | "html" | "list" | "paragraph" | "thematic-break" | "table" | "link-definition" | "footnote-definition" | "frontmatter" | "custom-container" | "math" | "import-code-snippet" | "*"))[]]);
389
394
  in?: ("list" | "blockquote" | "footnote-definition");
390
395
  });
391
396
  blankLine: ("any" | "never" | "always");
@@ -498,7 +503,161 @@ declare namespace meta_d_exports {
498
503
  export { name, version };
499
504
  }
500
505
  declare const name: "eslint-plugin-markdown-preferences";
501
- declare const version: "0.26.0";
506
+ declare const version: "0.27.0";
507
+ //#endregion
508
+ //#region src/language/ast-types.d.ts
509
+ type Node = mdast.Node;
510
+ type Root = ExtendChildren<mdast.Root, RootContent>;
511
+ type Blockquote = ExtendChildren<mdast.Blockquote, CustomContainer>;
512
+ type Break = mdast.Break;
513
+ type Code = mdast.Code;
514
+ type Definition = mdast.Definition;
515
+ type Delete = mdast.Delete;
516
+ type Emphasis = mdast.Emphasis;
517
+ type FootnoteDefinition = ExtendChildren<mdast.FootnoteDefinition, CustomContainer>;
518
+ type FootnoteReference = mdast.FootnoteReference;
519
+ type Heading = mdast.Heading;
520
+ type Html = mdast.Html;
521
+ type Image = mdast.Image;
522
+ type ImageReference = mdast.ImageReference;
523
+ type InlineCode = mdast.InlineCode;
524
+ type Link = mdast.Link;
525
+ type LinkReference = mdast.LinkReference;
526
+ type List = mdast.List;
527
+ type ListItem = ExtendChildren<mdast.ListItem, CustomContainer>;
528
+ type Paragraph = mdast.Paragraph;
529
+ type Strong = mdast.Strong;
530
+ type Table = mdast.Table;
531
+ type TableCell = mdast.TableCell;
532
+ type TableRow = mdast.TableRow;
533
+ type Text = mdast.Text;
534
+ type ThematicBreak = mdast.ThematicBreak;
535
+ type Yaml = mdast.Yaml;
536
+ type Math = math.Math;
537
+ type InlineMath = math.InlineMath;
538
+ type Toml = eslint.Toml;
539
+ type Json = eslint.Json;
540
+ interface CustomContainer extends ExtendChildren<mdast.Parent, CustomContainer | ImportCodeSnippet> {
541
+ /**
542
+ * Node type of mdast custom container.
543
+ */
544
+ type: "customContainer";
545
+ /**
546
+ * Info string (e.g., "warning" in ::: warning ... :::).
547
+ */
548
+ info: string | null;
549
+ /**
550
+ * Children of custom container.
551
+ */
552
+ children: (CustomContainer | BlockContent | DefinitionContent)[];
553
+ }
554
+ interface ImportCodeSnippet extends mdast.Literal {
555
+ /**
556
+ * Node type of mdast import code snippet.
557
+ */
558
+ type: "importCodeSnippet";
559
+ }
560
+ type RootContent = RootContentMap[keyof RootContentMap];
561
+ type BlockContent = RootContentMap[keyof mdast.BlockContentMap] | ImportCodeSnippet;
562
+ type DefinitionContent = RootContentMap[keyof mdast.DefinitionContentMap];
563
+ type ExtendChildren<N extends mdast.Node & {
564
+ children: mdast.Node[];
565
+ }, C extends mdast.Node> = Omit<N, "children"> & {
566
+ children: (MapChild<N["children"][number]> | C)[];
567
+ };
568
+ type MapChild<N extends mdast.Node> = N["type"] extends RootContent["type"] ? RootContentMap[N["type"]] : N;
569
+ interface RootContentMap {
570
+ blockquote: Blockquote;
571
+ break: Break;
572
+ code: Code;
573
+ definition: Definition;
574
+ delete: Delete;
575
+ emphasis: Emphasis;
576
+ footnoteDefinition: FootnoteDefinition;
577
+ footnoteReference: FootnoteReference;
578
+ heading: Heading;
579
+ html: Html;
580
+ image: Image;
581
+ imageReference: ImageReference;
582
+ inlineCode: InlineCode;
583
+ link: Link;
584
+ linkReference: LinkReference;
585
+ list: List;
586
+ listItem: ListItem;
587
+ paragraph: Paragraph;
588
+ strong: Strong;
589
+ table: Table;
590
+ tableCell: TableCell;
591
+ tableRow: TableRow;
592
+ text: Text;
593
+ thematicBreak: ThematicBreak;
594
+ yaml: Yaml;
595
+ toml: Toml;
596
+ json: Json;
597
+ customContainer: CustomContainer;
598
+ inlineMath: InlineMath;
599
+ math: Math;
600
+ importCodeSnippet: ImportCodeSnippet;
601
+ }
602
+ //#endregion
603
+ //#region src/language/extended-markdown-ianguage.d.ts
604
+ type ExtendedMarkdownSourceCode = TextSourceCodeBase<{
605
+ LangOptions: MarkdownLanguageOptions;
606
+ RootNode: Root;
607
+ SyntaxElementWithLoc: Node;
608
+ ConfigNode: {
609
+ value: string;
610
+ position: SourceLocation;
611
+ };
612
+ }>;
613
+ declare class ExtendedMarkdownLanguage implements Language {
614
+ /**
615
+ * The type of file to read.
616
+ * @type {"text"}
617
+ */
618
+ readonly fileType: "text";
619
+ /**
620
+ * The line number at which the parser starts counting.
621
+ * @type {0|1}
622
+ */
623
+ readonly lineStart: 0 | 1;
624
+ /**
625
+ * The column number at which the parser starts counting.
626
+ * @type {0|1}
627
+ */
628
+ readonly columnStart: 0 | 1;
629
+ /**
630
+ * The name of the key that holds the type of the node.
631
+ * @type {string}
632
+ */
633
+ readonly nodeTypeKey: string;
634
+ /**
635
+ * Default language options. User-defined options are merged with this object.
636
+ * @type {MarkdownLanguageOptions}
637
+ */
638
+ readonly defaultLanguageOptions: MarkdownLanguageOptions;
639
+ /**
640
+ * Validates the language options.
641
+ * @param {MarkdownLanguageOptions} languageOptions The language options to validate.
642
+ * @returns {void}
643
+ * @throws {Error} When the language options are invalid.
644
+ */
645
+ validateLanguageOptions(languageOptions: MarkdownLanguageOptions): void;
646
+ /**
647
+ * Parses the given file into an AST.
648
+ * @param {File} file The virtual file to parse.
649
+ * @param {MarkdownLanguageContext} _context The options to use for parsing.
650
+ * @returns {ParseResult<Root>} The result of parsing.
651
+ */
652
+ parse(file: File, _context: MarkdownLanguageContext): ParseResult<Root>;
653
+ /**
654
+ * Creates a new `MarkdownSourceCode` object from the given information.
655
+ * @param {File} file The virtual file to create a `MarkdownSourceCode` object from.
656
+ * @param {OkParseResult<Root>} parseResult The result returned from `parse()`.
657
+ * @returns {MarkdownSourceCode} The new `MarkdownSourceCode` object.
658
+ */
659
+ createSourceCode(file: File, parseResult: OkParseResult<Root>): ExtendedMarkdownSourceCode;
660
+ }
502
661
  //#endregion
503
662
  //#region src/index.d.ts
504
663
  declare const configs: {
@@ -510,8 +669,14 @@ declare const resources: {
510
669
  defaultPreserveWords: string[];
511
670
  defaultMinorWords: string[];
512
671
  };
672
+ declare const languages: {
673
+ "extended-syntax": ExtendedMarkdownLanguage;
674
+ };
513
675
  declare const _default: {
514
676
  meta: typeof meta_d_exports;
677
+ languages: {
678
+ "extended-syntax": ExtendedMarkdownLanguage;
679
+ };
515
680
  configs: {
516
681
  recommended: typeof recommended_d_exports;
517
682
  standard: typeof standard_d_exports;
@@ -523,4 +688,4 @@ declare const _default: {
523
688
  };
524
689
  };
525
690
  //#endregion
526
- export { RuleOptions, configs, _default as default, meta_d_exports as meta, resources, rules };
691
+ export { RuleOptions, configs, _default as default, languages, meta_d_exports as meta, resources, rules };
package/lib/index.js CHANGED
@@ -3,6 +3,16 @@ import stringWidth from "string-width";
3
3
  import emojiRegex from "emoji-regex-xs";
4
4
  import path from "node:path";
5
5
  import markdown from "@eslint/markdown";
6
+ import { fromMarkdown } from "mdast-util-from-markdown";
7
+ import { frontmatterFromMarkdown } from "mdast-util-frontmatter";
8
+ import { gfmFromMarkdown } from "mdast-util-gfm";
9
+ import { frontmatter } from "micromark-extension-frontmatter";
10
+ import { gfm } from "micromark-extension-gfm";
11
+ import { math } from "micromark-extension-math";
12
+ import { mathFromMarkdown } from "mdast-util-math";
13
+ import { markdownLineEnding, markdownSpace } from "micromark-util-character";
14
+ import { codes, constants, types } from "micromark-util-symbol";
15
+ import { factorySpace } from "micromark-factory-space";
6
16
 
7
17
  //#region src/utils/index.ts
8
18
  /**
@@ -957,14 +967,14 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
957
967
  if (node.ordered) return;
958
968
  checkBulletList(node);
959
969
  },
960
- "root, blockquote, listItem, footnoteDefinition"(node) {
970
+ "root, blockquote, listItem, footnoteDefinition, customContainer"(node) {
961
971
  containerStack = {
962
972
  node,
963
973
  level: node.type === "listItem" ? containerStack.level + 1 : 1,
964
974
  upper: containerStack
965
975
  };
966
976
  },
967
- "root, blockquote, listItem, footnoteDefinition:exit"() {
977
+ "root, blockquote, listItem, footnoteDefinition, customContainer:exit"() {
968
978
  containerStack = containerStack.upper;
969
979
  }
970
980
  };
@@ -1117,10 +1127,10 @@ var canonical_code_block_language_default = createRule("canonical-code-block-lan
1117
1127
  },
1118
1128
  create(context) {
1119
1129
  const sourceCode = context.sourceCode;
1120
- const languages = context.options[0]?.languages || DEFAULT_LANGUAGES;
1130
+ const languages$1 = context.options[0]?.languages || DEFAULT_LANGUAGES;
1121
1131
  return { code(node) {
1122
- if (!node.lang || !languages[node.lang]) return;
1123
- const canonical = languages[node.lang];
1132
+ if (!node.lang || !languages$1[node.lang]) return;
1133
+ const canonical = languages$1[node.lang];
1124
1134
  const current = node.lang;
1125
1135
  if (current === canonical) return;
1126
1136
  const parsed = parseFencedCodeBlock(sourceCode, node);
@@ -1542,7 +1552,12 @@ var definitions_last_default = createRule("definitions-last", {
1542
1552
  if (c === "\n") lineFeeds++;
1543
1553
  }
1544
1554
  yield fixer.removeRange([rangeStart, range[1]]);
1545
- let insertText = sourceCode.text.slice(rangeStart, lineStart) + sourceCode.text.slice(...range);
1555
+ const startColumnOffset = loc.start.column - 1;
1556
+ const insertLines = sourceCode.lines.slice(loc.start.line - 1, loc.end.line).map((lineText, i, arr) => {
1557
+ if (i === arr.length - 1) return lineText.slice(startColumnOffset, loc.end.column - 1);
1558
+ return lineText.slice(startColumnOffset);
1559
+ });
1560
+ let insertText = sourceCode.text.slice(rangeStart, lineStart) + insertLines.join(/\r?\n/u.exec(sourceCode.text)?.[0] ?? "\n");
1546
1561
  if (prev.type === "footnoteDefinition" && node.type !== "footnoteDefinition" && lineFeeds <= 1) insertText = `\n${insertText}`;
1547
1562
  if (next && node.type === "footnoteDefinition" && next.type !== "footnoteDefinition") {
1548
1563
  const prevLoc = sourceCode.getLoc(prev);
@@ -5291,6 +5306,108 @@ function parseListItem(sourceCode, node) {
5291
5306
  };
5292
5307
  }
5293
5308
 
5309
+ //#endregion
5310
+ //#region src/utils/math-block.ts
5311
+ /**
5312
+ * Parse the math block.
5313
+ */
5314
+ function parseMathBlock(sourceCode, node) {
5315
+ const range = sourceCode.getRange(node);
5316
+ const text = sourceCode.text.slice(...range);
5317
+ const parsedOpening = parseMathOpeningSequenceFromText(text);
5318
+ if (parsedOpening === null) return null;
5319
+ const openingSequenceRange = [range[0], range[0] + parsedOpening.openingSequence.length];
5320
+ const openingSequence = {
5321
+ text: parsedOpening.openingSequence,
5322
+ range: openingSequenceRange,
5323
+ loc: getSourceLocationFromRange(sourceCode, node, openingSequenceRange)
5324
+ };
5325
+ const parsedClosing = parseMathClosingSequenceFromText(text);
5326
+ if (parsedClosing == null) return null;
5327
+ const spaceAfterClosingRange = [range[1] - parsedClosing.after.length, range[1]];
5328
+ const spaceAfterClosing = {
5329
+ text: parsedClosing.after,
5330
+ range: spaceAfterClosingRange,
5331
+ loc: getSourceLocationFromRange(sourceCode, node, spaceAfterClosingRange)
5332
+ };
5333
+ const closingSequenceRange = [spaceAfterClosing.range[0] - parsedClosing.closingSequence.length, spaceAfterClosing.range[0]];
5334
+ const closingSequence = {
5335
+ text: parsedClosing.closingSequence,
5336
+ range: closingSequenceRange,
5337
+ loc: getSourceLocationFromRange(sourceCode, node, closingSequenceRange)
5338
+ };
5339
+ const contentRange = [openingSequence.range[1] + parsedOpening.after.length, closingSequence.range[0] - parsedClosing.before.length];
5340
+ const contentText = sourceCode.text.slice(...contentRange);
5341
+ return {
5342
+ openingSequence,
5343
+ content: {
5344
+ text: contentText,
5345
+ range: contentRange,
5346
+ loc: getSourceLocationFromRange(sourceCode, node, contentRange)
5347
+ },
5348
+ closingSequence,
5349
+ after: spaceAfterClosing.range[0] < spaceAfterClosing.range[1] ? spaceAfterClosing : null
5350
+ };
5351
+ }
5352
+ /**
5353
+ * Parse the opening sequence from a text string.
5354
+ */
5355
+ function parseMathOpeningSequenceFromText(text) {
5356
+ if (!text.startsWith("$")) return null;
5357
+ let openingSequenceAfterOffset = 1;
5358
+ while (openingSequenceAfterOffset < text.length && text[openingSequenceAfterOffset] === "$") openingSequenceAfterOffset++;
5359
+ const afterOffset = skipWhitespace(openingSequenceAfterOffset);
5360
+ if (afterOffset === openingSequenceAfterOffset || afterOffset >= text.length) return null;
5361
+ return {
5362
+ openingSequence: text.slice(0, openingSequenceAfterOffset),
5363
+ after: text.slice(openingSequenceAfterOffset, afterOffset)
5364
+ };
5365
+ /**
5366
+ * Skip whitespace characters at the start of the text.
5367
+ */
5368
+ function skipWhitespace(index) {
5369
+ let inIndent = false;
5370
+ let result = index;
5371
+ let c;
5372
+ while (result < text.length && (c = text[result]) && (isWhitespace(c) || inIndent && c === ">")) {
5373
+ result++;
5374
+ if (c === "\n") inIndent = true;
5375
+ else if (inIndent) inIndent = isWhitespace(c) || c === ">";
5376
+ }
5377
+ return result;
5378
+ }
5379
+ }
5380
+ /**
5381
+ * Parse the closing sequence from a text string.
5382
+ */
5383
+ function parseMathClosingSequenceFromText(text) {
5384
+ const trimmedEndOffset = skipEndWhitespace(text.length - 1) + 1;
5385
+ if (trimmedEndOffset <= 0 || text[trimmedEndOffset - 1] !== "$") return null;
5386
+ let closingSequenceBeforeOffset = trimmedEndOffset - 2;
5387
+ while (closingSequenceBeforeOffset >= 0 && text[closingSequenceBeforeOffset] === "$") closingSequenceBeforeOffset--;
5388
+ const beforeOffset = skipEndWhitespace(closingSequenceBeforeOffset);
5389
+ if (beforeOffset === closingSequenceBeforeOffset || beforeOffset < 0) return null;
5390
+ return {
5391
+ before: text.slice(beforeOffset + 1, closingSequenceBeforeOffset + 1),
5392
+ closingSequence: text.slice(closingSequenceBeforeOffset + 1, trimmedEndOffset),
5393
+ after: text.slice(trimmedEndOffset)
5394
+ };
5395
+ /**
5396
+ * Skip whitespace characters at the end of the text.
5397
+ */
5398
+ function skipEndWhitespace(index) {
5399
+ let result = index;
5400
+ let c;
5401
+ while (result >= 0 && (c = text[result]) && isWhitespace(c)) result--;
5402
+ if (c === ">") {
5403
+ let index2 = result - 1;
5404
+ while (index2 >= 0 && (c = text[index2]) && (isSpaceOrTab(c) || c === ">")) index2--;
5405
+ if (c === "\n") return skipEndWhitespace(index2);
5406
+ }
5407
+ return result;
5408
+ }
5409
+ }
5410
+
5294
5411
  //#endregion
5295
5412
  //#region src/rules/indent.ts
5296
5413
  /**
@@ -5570,7 +5687,10 @@ var indent_default = createRule("indent", {
5570
5687
  definition: verifyLinkDefinition,
5571
5688
  table: verifyTable,
5572
5689
  list: verifyList,
5573
- inlineCode: verifyInlineCode,
5690
+ customContainer: verifyCustomContainer,
5691
+ math: verifyMathBlock,
5692
+ importCodeSnippet: verifyImportCodeSnippet,
5693
+ inlineCode: verifyInlineCodeOrInlineMath,
5574
5694
  emphasis: verifyEmphasisOrStrongOrDelete,
5575
5695
  strong: verifyEmphasisOrStrongOrDelete,
5576
5696
  delete: verifyEmphasisOrStrongOrDelete,
@@ -5579,6 +5699,7 @@ var indent_default = createRule("indent", {
5579
5699
  footnoteReference: verifyInline,
5580
5700
  break: verifyInline,
5581
5701
  text: verifyText,
5702
+ inlineMath: verifyInlineCodeOrInlineMath,
5582
5703
  blockquote(node) {
5583
5704
  verifyBlockquote(node);
5584
5705
  blockStack = new BlockquoteStack(node);
@@ -5635,32 +5756,9 @@ var indent_default = createRule("indent", {
5635
5756
  verifyLinesIndent([loc.start.line, loc.end.line], (lineNumber) => blockStack.getExpectedIndent({
5636
5757
  lineNumber,
5637
5758
  block: true
5638
- }), additionalFixes);
5639
- /**
5640
- * Additional fixes for code block content lines.
5641
- */
5642
- function* additionalFixes(fixer, info) {
5643
- if (info.loc.start.line !== loc.start.line) return;
5644
- for (let lineNumber = loc.start.line + 1; lineNumber < loc.end.line; lineNumber++) {
5645
- const line = getParsedLines(sourceCode).get(lineNumber);
5646
- if (!line) continue;
5647
- if (info.expectedIndentWidth > info.actualIndentWidth) {
5648
- const before = sliceWidth(line.text, 0, info.actualIndentWidth);
5649
- const after = sliceWidth(line.text, info.actualIndentWidth);
5650
- const diffWidth = info.expectedIndentWidth - info.actualIndentWidth;
5651
- yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + " ".repeat(diffWidth) + after);
5652
- } else {
5653
- let before = sliceWidth(line.text, 0, info.expectedIndentWidth);
5654
- let between = sliceWidth(line.text, info.expectedIndentWidth, info.actualIndentWidth);
5655
- const after = sliceWidth(line.text, info.actualIndentWidth);
5656
- while (between && !isSpaceOrTab(between)) {
5657
- before += between[0];
5658
- between = between.slice(1);
5659
- }
5660
- yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + after);
5661
- }
5662
- }
5663
- }
5759
+ }), (fixer, info) => {
5760
+ return additionalFixesForCodeBlock(node, fixer, info, loc.start.line + 1, loc.end.line - 1);
5761
+ });
5664
5762
  }
5665
5763
  /**
5666
5764
  * Verify an HTML node.
@@ -5777,6 +5875,41 @@ var indent_default = createRule("indent", {
5777
5875
  }
5778
5876
  }
5779
5877
  /**
5878
+ * Verify a custom container node.
5879
+ */
5880
+ function verifyCustomContainer(node) {
5881
+ const loc = sourceCode.getLoc(node);
5882
+ verifyLinesIndent([loc.start.line, loc.end.line], (lineNumber) => blockStack.getExpectedIndent({
5883
+ lineNumber,
5884
+ block: true
5885
+ }));
5886
+ }
5887
+ /**
5888
+ * Verify a math node.
5889
+ */
5890
+ function verifyMathBlock(node) {
5891
+ const parsed = parseMathBlock(sourceCode, node);
5892
+ if (!parsed) return;
5893
+ const loc = sourceCode.getLoc(node);
5894
+ const endLineToBeChecked = inlineToBeChecked(parsed.closingSequence.loc.start);
5895
+ verifyLinesIndent(endLineToBeChecked ? [loc.start.line, loc.end.line] : [loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
5896
+ lineNumber,
5897
+ block: true
5898
+ }), (fixer, info) => {
5899
+ return additionalFixesForCodeBlock(node, fixer, info, loc.start.line + 1, endLineToBeChecked ? loc.end.line - 1 : loc.end.line);
5900
+ });
5901
+ }
5902
+ /**
5903
+ * Verify an import code snippet node.
5904
+ */
5905
+ function verifyImportCodeSnippet(node) {
5906
+ const loc = sourceCode.getLoc(node);
5907
+ verifyLinesIndent(lineNumbersFromRange(loc.start.line, loc.end.line), (lineNumber) => blockStack.getExpectedIndent({
5908
+ lineNumber,
5909
+ block: true
5910
+ }));
5911
+ }
5912
+ /**
5780
5913
  * Verify a footnote definition node.
5781
5914
  */
5782
5915
  function verifyFootnoteDefinition(node) {
@@ -5787,9 +5920,9 @@ var indent_default = createRule("indent", {
5787
5920
  }));
5788
5921
  }
5789
5922
  /**
5790
- * Verify an inline code node.
5923
+ * Verify an inline code/math node.
5791
5924
  */
5792
- function verifyInlineCode(node) {
5925
+ function verifyInlineCodeOrInlineMath(node) {
5793
5926
  const loc = sourceCode.getLoc(node);
5794
5927
  if (!inlineToBeChecked(loc.start)) return;
5795
5928
  verifyLinesIndent([loc.start.line], (lineNumber) => blockStack.getExpectedIndent({
@@ -6171,6 +6304,32 @@ var indent_default = createRule("indent", {
6171
6304
  actualIndentWidth
6172
6305
  };
6173
6306
  }
6307
+ /**
6308
+ * Additional fixes for code/math block content lines.
6309
+ */
6310
+ function* additionalFixesForCodeBlock(node, fixer, info, fixStartLine, fixEndLine) {
6311
+ const loc = sourceCode.getLoc(node);
6312
+ if (info.loc.start.line !== loc.start.line) return;
6313
+ for (let lineNumber = fixStartLine; lineNumber <= fixEndLine; lineNumber++) {
6314
+ const line = getParsedLines(sourceCode).get(lineNumber);
6315
+ if (!line) continue;
6316
+ if (info.expectedIndentWidth > info.actualIndentWidth) {
6317
+ const before = sliceWidth(line.text, 0, info.actualIndentWidth);
6318
+ const after = sliceWidth(line.text, info.actualIndentWidth);
6319
+ const diffWidth = info.expectedIndentWidth - info.actualIndentWidth;
6320
+ yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + " ".repeat(diffWidth) + after);
6321
+ } else {
6322
+ let before = sliceWidth(line.text, 0, info.expectedIndentWidth);
6323
+ let between = sliceWidth(line.text, info.expectedIndentWidth, info.actualIndentWidth);
6324
+ const after = sliceWidth(line.text, info.actualIndentWidth);
6325
+ while (between && !isSpaceOrTab(between)) {
6326
+ before += between[0];
6327
+ between = between.slice(1);
6328
+ }
6329
+ yield fixer.replaceTextRange([line.range[0], line.range[0] + line.text.length], before + after);
6330
+ }
6331
+ }
6332
+ }
6174
6333
  }
6175
6334
  });
6176
6335
 
@@ -8307,14 +8466,14 @@ var ordered_list_marker_sequence_default = createRule("ordered-list-marker-seque
8307
8466
  }
8308
8467
  }
8309
8468
  return {
8310
- "blockquote, listItem, footnoteDefinition"(node) {
8469
+ "blockquote, listItem, footnoteDefinition, customContainer"(node) {
8311
8470
  scope = {
8312
8471
  node,
8313
8472
  upper: scope,
8314
8473
  last: null
8315
8474
  };
8316
8475
  },
8317
- "blockquote, listItem, footnoteDefinition:exit"(node) {
8476
+ "blockquote, listItem, footnoteDefinition, customContainer:exit"(node) {
8318
8477
  if (scope.node === node) scope = scope.upper;
8319
8478
  },
8320
8479
  heading() {
@@ -8396,14 +8555,14 @@ var ordered_list_marker_start_default = createRule("ordered-list-marker-start",
8396
8555
  });
8397
8556
  }
8398
8557
  return {
8399
- "blockquote, listItem, footnoteDefinition"(node) {
8558
+ "blockquote, listItem, footnoteDefinition, customContainer"(node) {
8400
8559
  scope = {
8401
8560
  node,
8402
8561
  upper: scope,
8403
8562
  last: null
8404
8563
  };
8405
8564
  },
8406
- "blockquote, listItem, footnoteDefinition:exit"(node) {
8565
+ "blockquote, listItem, footnoteDefinition, customContainer:exit"(node) {
8407
8566
  if (scope.node === node) scope = scope.upper;
8408
8567
  },
8409
8568
  heading() {
@@ -8576,14 +8735,14 @@ var ordered_list_marker_style_default = createRule("ordered-list-marker-style",
8576
8735
  if (!node.ordered) return;
8577
8736
  checkOrderedList(node);
8578
8737
  },
8579
- "root, blockquote, listItem, footnoteDefinition"(node) {
8738
+ "root, blockquote, listItem, footnoteDefinition, customContainer"(node) {
8580
8739
  containerStack = {
8581
8740
  node,
8582
8741
  level: node.type === "listItem" ? containerStack.level + 1 : 1,
8583
8742
  upper: containerStack
8584
8743
  };
8585
8744
  },
8586
- "root, blockquote, listItem, footnoteDefinition:exit"() {
8745
+ "root, blockquote, listItem, footnoteDefinition, customContainer:exit"() {
8587
8746
  containerStack = containerStack.upper;
8588
8747
  }
8589
8748
  };
@@ -8631,6 +8790,9 @@ const BLOCK_TYPES = [
8631
8790
  "link-definition",
8632
8791
  "footnote-definition",
8633
8792
  "frontmatter",
8793
+ "custom-container",
8794
+ "math",
8795
+ "import-code-snippet",
8634
8796
  "*"
8635
8797
  ];
8636
8798
  const BLOCK_TYPE_SCHEMAS = [{
@@ -8670,7 +8832,10 @@ const BLOCK_TYPE_MAP = {
8670
8832
  footnoteDefinition: "footnote-definition",
8671
8833
  json: "frontmatter",
8672
8834
  toml: "frontmatter",
8673
- yaml: "frontmatter"
8835
+ yaml: "frontmatter",
8836
+ customContainer: "custom-container",
8837
+ math: "math",
8838
+ importCodeSnippet: "import-code-snippet"
8674
8839
  };
8675
8840
  /**
8676
8841
  * Get the block type of a node
@@ -8866,10 +9031,10 @@ var padding_line_between_blocks_default = createRule("padding-line-between-block
8866
9031
  }
8867
9032
  }
8868
9033
  return {
8869
- "root, blockquote, listItem, footnoteDefinition"(node) {
9034
+ "root, blockquote, listItem, footnoteDefinition, customContainer"(node) {
8870
9035
  containerStack.unshift(node);
8871
9036
  },
8872
- "root, blockquote, listItem, footnoteDefinition:exit"(node) {
9037
+ "root, blockquote, listItem, footnoteDefinition, customContainer:exit"(node) {
8873
9038
  checkBlockPadding(node);
8874
9039
  containerStack.shift();
8875
9040
  }
@@ -11219,7 +11384,440 @@ var meta_exports = /* @__PURE__ */ __export({
11219
11384
  version: () => version
11220
11385
  });
11221
11386
  const name = "eslint-plugin-markdown-preferences";
11222
- const version = "0.26.0";
11387
+ const version = "0.27.0";
11388
+
11389
+ //#endregion
11390
+ //#region src/language/extensions/micromark-custom-container.ts
11391
+ /**
11392
+ * Micromark extension to support custom containers (e.g., ::: warning ... :::).
11393
+ */
11394
+ function customContainer() {
11395
+ const customContainerOpenConstruct = {
11396
+ name: "customContainer",
11397
+ continuation: {
11398
+ name: "customContainerContinuation",
11399
+ tokenize(effects, ok, nok) {
11400
+ return tokenizeCustomContainerContinuation(this, effects, ok, nok);
11401
+ }
11402
+ },
11403
+ exit(effects) {
11404
+ effects.exit("customContainer");
11405
+ },
11406
+ tokenize(effects, ok, nok) {
11407
+ return tokenizeCustomContainerOpen(this, effects, ok, nok);
11408
+ }
11409
+ };
11410
+ const customContainerCloseConstruct = {
11411
+ name: "customContainerCloseConstruct",
11412
+ tokenize(effects, ok, nok) {
11413
+ return tokenizeCustomContainerClose(this, effects, ok, nok);
11414
+ }
11415
+ };
11416
+ return { document: { [codes.colon]: customContainerOpenConstruct } };
11417
+ /**
11418
+ * Continuation tokenizer for the opening marker of the custom container.
11419
+ */
11420
+ function tokenizeCustomContainerOpen(self, effects, ok, nok) {
11421
+ let size = 0;
11422
+ let openToken;
11423
+ const infoCodes = [];
11424
+ return start;
11425
+ /**
11426
+ * Process start for the opening marker of the custom container.
11427
+ */
11428
+ function start(code) {
11429
+ if (code !== codes.colon) return nok(code);
11430
+ size = 0;
11431
+ openToken = effects.enter("customContainer", { _container: true });
11432
+ effects.enter("customContainerFence");
11433
+ effects.enter("customContainerFenceSequence");
11434
+ return sequence;
11435
+ }
11436
+ /**
11437
+ * Process sequence for the opening marker of the custom container.
11438
+ *
11439
+ * ```markdown
11440
+ * > | ::: info
11441
+ * ^
11442
+ * ```
11443
+ */
11444
+ function sequence(code) {
11445
+ if (code === codes.colon) {
11446
+ size++;
11447
+ effects.consume(code);
11448
+ return sequence;
11449
+ }
11450
+ if (size < 3) return nok(code);
11451
+ effects.exit("customContainerFenceSequence");
11452
+ return markdownSpace(code) ? factorySpace(effects, infoBefore, types.whitespace)(code) : infoBefore(code);
11453
+ }
11454
+ /**
11455
+ * Process before info for the opening marker of the custom container.
11456
+ *
11457
+ * ```markdown
11458
+ * > | ::: info
11459
+ * ^
11460
+ * ```
11461
+ */
11462
+ function infoBefore(code) {
11463
+ if (code === codes.eof || markdownLineEnding(code)) return nok(code);
11464
+ openToken._customContainer = { size };
11465
+ self.containerState._customContainer = { open: openToken };
11466
+ effects.enter("customContainerFenceInfo");
11467
+ effects.enter(types.chunkString, { contentType: constants.contentTypeString });
11468
+ infoCodes.length = 0;
11469
+ return info(code);
11470
+ }
11471
+ /**
11472
+ * Process info for the opening marker of the custom container.
11473
+ *
11474
+ * ```markdown
11475
+ * > | ::: info
11476
+ * ^
11477
+ * ```
11478
+ *
11479
+ * @type {State}
11480
+ */
11481
+ function info(code) {
11482
+ if (code === codes.eof || markdownLineEnding(code)) {
11483
+ effects.exit(types.chunkString);
11484
+ effects.exit("customContainerFenceInfo");
11485
+ effects.exit("customContainerFence");
11486
+ openToken._customContainer.info = String.fromCharCode(...infoCodes);
11487
+ return ok(code);
11488
+ }
11489
+ infoCodes.push(code);
11490
+ effects.consume(code);
11491
+ return info;
11492
+ }
11493
+ }
11494
+ /**
11495
+ * Continuation tokenizer for the closing marker of the custom container.
11496
+ */
11497
+ function tokenizeCustomContainerClose(self, effects, ok, nok) {
11498
+ let size = 0;
11499
+ return start;
11500
+ /**
11501
+ * Process start for the closing marker of the custom container.
11502
+ */
11503
+ function start(code) {
11504
+ if (code !== codes.colon) return nok(code);
11505
+ size = 0;
11506
+ effects.enter("customContainerFence");
11507
+ effects.enter("customContainerFenceSequence");
11508
+ return sequence;
11509
+ }
11510
+ /**
11511
+ * Process sequence for the closing marker of the custom container.
11512
+ *
11513
+ * ```markdown
11514
+ * > | :::
11515
+ * ```
11516
+ */
11517
+ function sequence(code) {
11518
+ if (code === codes.colon) {
11519
+ size++;
11520
+ effects.consume(code);
11521
+ return sequence;
11522
+ }
11523
+ if (size < 3) return nok(code);
11524
+ effects.exit("customContainerFenceSequence");
11525
+ return markdownSpace(code) ? factorySpace(effects, after, types.whitespace)(code) : after(code);
11526
+ }
11527
+ /**
11528
+ * Process after for the closing marker of the custom container.
11529
+ *
11530
+ * ```markdown
11531
+ * > | :::
11532
+ * ^
11533
+ * ```
11534
+ */
11535
+ function after(code) {
11536
+ if (code === codes.eof || markdownLineEnding(code)) {
11537
+ if (!self.containerState) throw new Error("containerState is undefined");
11538
+ const openToken = self.containerState._customContainer?.open;
11539
+ if (openToken?._customContainer?.size === size) {
11540
+ openToken._customContainer.closed = true;
11541
+ self.containerState._closeFlow = true;
11542
+ effects.exit("customContainerFence");
11543
+ return ok(code);
11544
+ }
11545
+ }
11546
+ return nok(code);
11547
+ }
11548
+ }
11549
+ /**
11550
+ * Continuation tokenizer for the custom container.
11551
+ */
11552
+ function tokenizeCustomContainerContinuation(self, effects, ok, nok) {
11553
+ if (self.containerState?._customContainer?.open?._customContainer?.closed) {
11554
+ self.containerState._closeFlow = true;
11555
+ return nok;
11556
+ }
11557
+ return start;
11558
+ /**
11559
+ * Process start for the custom container.
11560
+ */
11561
+ function start(code) {
11562
+ if (code !== codes.colon) return ok(code);
11563
+ return effects.attempt(customContainerCloseConstruct, () => {
11564
+ self.containerState._closeFlow = true;
11565
+ return ok;
11566
+ }, ok);
11567
+ }
11568
+ }
11569
+ }
11570
+
11571
+ //#endregion
11572
+ //#region src/language/extensions/mdast-custom-container.ts
11573
+ /**
11574
+ * Mdast extension to support custom containers (e.g., ::: warning ... :::).
11575
+ */
11576
+ function customContainerFromMarkdown() {
11577
+ return {
11578
+ enter: {
11579
+ customContainer(token) {
11580
+ this.enter({
11581
+ type: "customContainer",
11582
+ children: [],
11583
+ info: null
11584
+ }, token);
11585
+ },
11586
+ customContainerFenceInfo() {
11587
+ this.buffer();
11588
+ }
11589
+ },
11590
+ exit: {
11591
+ customContainer(token) {
11592
+ this.exit(token);
11593
+ },
11594
+ customContainerFenceInfo() {
11595
+ const data = this.resume();
11596
+ const node = this.stack[this.stack.length - 1];
11597
+ node.info = data;
11598
+ }
11599
+ }
11600
+ };
11601
+ }
11602
+
11603
+ //#endregion
11604
+ //#region src/language/extensions/micromark-import-code-snippet.ts
11605
+ /**
11606
+ * Micromark extension to support [VitePress-style Import Code Snippets](https://vitepress.dev/guide/markdown#import-code-snippets) syntax using triple left angle brackets.
11607
+ */
11608
+ function importCodeSnippet() {
11609
+ const importCodeSnippetConstruct = {
11610
+ name: "importCodeSnippet",
11611
+ tokenize(effects, ok, nok) {
11612
+ return tokenizeImportCodeSnippet(this, effects, ok, nok);
11613
+ }
11614
+ };
11615
+ return { flow: { [codes.lessThan]: importCodeSnippetConstruct } };
11616
+ /**
11617
+ * Tokenizer for the import code snippet.
11618
+ */
11619
+ function tokenizeImportCodeSnippet(_self, effects, ok, nok) {
11620
+ let size = 0;
11621
+ return start;
11622
+ /**
11623
+ * Process start for the opening marker of the import code snippet.
11624
+ */
11625
+ function start(code) {
11626
+ if (code !== codes.lessThan) return nok(code);
11627
+ size = 0;
11628
+ effects.enter("importCodeSnippet");
11629
+ effects.enter("importCodeSnippetMarkerSequence");
11630
+ return sequence(code);
11631
+ }
11632
+ /**
11633
+ * Process the sequence of opening markers for the import code snippet.
11634
+ *
11635
+ * ```markdown
11636
+ * > | <<< ./path/to/code.ts
11637
+ * ^^^
11638
+ * ```
11639
+ */
11640
+ function sequence(code) {
11641
+ if (code === codes.lessThan) {
11642
+ size++;
11643
+ effects.consume(code);
11644
+ return sequence;
11645
+ }
11646
+ if (size < 3) return nok(code);
11647
+ effects.exit("importCodeSnippetMarkerSequence");
11648
+ return markdownSpace(code) ? factorySpace(effects, afterMarker, types.whitespace)(code) : afterMarker(code);
11649
+ }
11650
+ /**
11651
+ * Process after the opening marker of the import code snippet.
11652
+ *
11653
+ * ```markdown
11654
+ * > | <<< ./path/to/code.ts
11655
+ * ^
11656
+ * ```
11657
+ */
11658
+ function afterMarker(code) {
11659
+ if (code === codes.eof || markdownLineEnding(code)) return nok(code);
11660
+ effects.enter("importCodeSnippetPath");
11661
+ effects.enter(types.chunkString, { contentType: constants.contentTypeString });
11662
+ return importPath(code);
11663
+ }
11664
+ /**
11665
+ * Process the path of the import code snippet.
11666
+ *
11667
+ * ```markdown
11668
+ * > | <<< ./path/to/code.ts
11669
+ * ^^^^^^^^^^^^^^^^^
11670
+ * ```
11671
+ */
11672
+ function importPath(code) {
11673
+ if (code === codes.eof || markdownLineEnding(code) || markdownSpace(code)) {
11674
+ effects.exit(types.chunkString);
11675
+ effects.exit("importCodeSnippetPath");
11676
+ effects.exit("importCodeSnippet");
11677
+ return afterPath(code);
11678
+ }
11679
+ effects.consume(code);
11680
+ return importPath;
11681
+ }
11682
+ /**
11683
+ * Process after the path of the import code snippet.
11684
+ *
11685
+ * ```markdown
11686
+ * > | <<< ./path/to/code.ts
11687
+ * ^
11688
+ * ```
11689
+ */
11690
+ function afterPath(code) {
11691
+ if (code === codes.eof || markdownLineEnding(code)) return ok(code);
11692
+ if (markdownSpace(code)) return factorySpace(effects, afterPath, types.whitespace)(code);
11693
+ return nok(code);
11694
+ }
11695
+ }
11696
+ }
11697
+
11698
+ //#endregion
11699
+ //#region src/language/extensions/mdast-import-code-snippet.ts
11700
+ /**
11701
+ * Mdast extension to support [VitePress-style Import Code Snippets](https://vitepress.dev/guide/markdown#import-code-snippets) syntax using triple left angle brackets.
11702
+ */
11703
+ function importCodeSnippetFromMarkdown() {
11704
+ return {
11705
+ enter: {
11706
+ importCodeSnippet(token) {
11707
+ this.enter({
11708
+ type: "importCodeSnippet",
11709
+ value: ""
11710
+ }, token);
11711
+ },
11712
+ importCodeSnippetPath() {
11713
+ this.buffer();
11714
+ }
11715
+ },
11716
+ exit: {
11717
+ importCodeSnippet(token) {
11718
+ this.exit(token);
11719
+ },
11720
+ importCodeSnippetPath() {
11721
+ const data = this.resume();
11722
+ const node = this.stack[this.stack.length - 1];
11723
+ node.value = data;
11724
+ }
11725
+ }
11726
+ };
11727
+ }
11728
+
11729
+ //#endregion
11730
+ //#region src/language/parser.ts
11731
+ /**
11732
+ * Parse Extended Markdown to MDAST.
11733
+ */
11734
+ function parseExtendedMarkdown(code) {
11735
+ const options = {
11736
+ extensions: [
11737
+ gfm(),
11738
+ frontmatter(["yaml", "toml"]),
11739
+ math(),
11740
+ customContainer(),
11741
+ importCodeSnippet()
11742
+ ],
11743
+ mdastExtensions: [
11744
+ gfmFromMarkdown(),
11745
+ frontmatterFromMarkdown(["yaml", "toml"]),
11746
+ mathFromMarkdown(),
11747
+ customContainerFromMarkdown(),
11748
+ importCodeSnippetFromMarkdown()
11749
+ ]
11750
+ };
11751
+ return fromMarkdown(code, options);
11752
+ }
11753
+
11754
+ //#endregion
11755
+ //#region src/language/extended-markdown-ianguage.ts
11756
+ var ExtendedMarkdownLanguage = class {
11757
+ /**
11758
+ * The type of file to read.
11759
+ * @type {"text"}
11760
+ */
11761
+ fileType = markdown.languages.gfm.fileType;
11762
+ /**
11763
+ * The line number at which the parser starts counting.
11764
+ * @type {0|1}
11765
+ */
11766
+ lineStart = markdown.languages.gfm.lineStart;
11767
+ /**
11768
+ * The column number at which the parser starts counting.
11769
+ * @type {0|1}
11770
+ */
11771
+ columnStart = markdown.languages.gfm.columnStart;
11772
+ /**
11773
+ * The name of the key that holds the type of the node.
11774
+ * @type {string}
11775
+ */
11776
+ nodeTypeKey = markdown.languages.gfm.nodeTypeKey;
11777
+ /**
11778
+ * Default language options. User-defined options are merged with this object.
11779
+ * @type {MarkdownLanguageOptions}
11780
+ */
11781
+ defaultLanguageOptions = markdown.languages.gfm.defaultLanguageOptions;
11782
+ /**
11783
+ * Validates the language options.
11784
+ * @param {MarkdownLanguageOptions} languageOptions The language options to validate.
11785
+ * @returns {void}
11786
+ * @throws {Error} When the language options are invalid.
11787
+ */
11788
+ validateLanguageOptions(languageOptions$2) {
11789
+ return markdown.languages.gfm.validateLanguageOptions(languageOptions$2);
11790
+ }
11791
+ /**
11792
+ * Parses the given file into an AST.
11793
+ * @param {File} file The virtual file to parse.
11794
+ * @param {MarkdownLanguageContext} _context The options to use for parsing.
11795
+ * @returns {ParseResult<Root>} The result of parsing.
11796
+ */
11797
+ parse(file, _context) {
11798
+ const text = file.body;
11799
+ try {
11800
+ return {
11801
+ ok: true,
11802
+ ast: parseExtendedMarkdown(text)
11803
+ };
11804
+ } catch (ex) {
11805
+ return {
11806
+ ok: false,
11807
+ errors: [ex]
11808
+ };
11809
+ }
11810
+ }
11811
+ /**
11812
+ * Creates a new `MarkdownSourceCode` object from the given information.
11813
+ * @param {File} file The virtual file to create a `MarkdownSourceCode` object from.
11814
+ * @param {OkParseResult<Root>} parseResult The result returned from `parse()`.
11815
+ * @returns {MarkdownSourceCode} The new `MarkdownSourceCode` object.
11816
+ */
11817
+ createSourceCode(file, parseResult) {
11818
+ return markdown.languages.gfm.createSourceCode(file, parseResult);
11819
+ }
11820
+ };
11223
11821
 
11224
11822
  //#endregion
11225
11823
  //#region src/index.ts
@@ -11235,12 +11833,14 @@ const resources = {
11235
11833
  defaultPreserveWords,
11236
11834
  defaultMinorWords
11237
11835
  };
11836
+ const languages = { "extended-syntax": new ExtendedMarkdownLanguage() };
11238
11837
  var src_default = {
11239
11838
  meta: meta_exports,
11839
+ languages,
11240
11840
  configs,
11241
11841
  rules,
11242
11842
  resources
11243
11843
  };
11244
11844
 
11245
11845
  //#endregion
11246
- export { configs, src_default as default, meta_exports as meta, resources, rules };
11846
+ export { configs, src_default as default, languages, meta_exports as meta, resources, rules };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-markdown-preferences",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "ESLint plugin that enforces our markdown preferences",
5
5
  "type": "module",
6
6
  "exports": {
@@ -63,6 +63,16 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "emoji-regex-xs": "^2.0.1",
66
+ "mdast-util-from-markdown": "^2.0.2",
67
+ "mdast-util-frontmatter": "^2.0.1",
68
+ "mdast-util-gfm": "^3.1.0",
69
+ "mdast-util-math": "^3.0.0",
70
+ "micromark-extension-frontmatter": "^2.0.0",
71
+ "micromark-extension-gfm": "^3.0.0",
72
+ "micromark-extension-math": "^3.1.0",
73
+ "micromark-factory-space": "^2.0.1",
74
+ "micromark-util-character": "^2.1.1",
75
+ "micromark-util-symbol": "^2.0.1",
66
76
  "string-width": "^8.0.0"
67
77
  },
68
78
  "devDependencies": {