@zinkawaii/eslint-plugin-markdown 0.0.0 → 8.0.3-patch.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.
@@ -0,0 +1,2357 @@
1
+ import { createParser } from "satorigear";
2
+ import { ConfigCommentParser, Directive, TextSourceCodeBase, VisitNodeStep } from "@eslint/plugin-kit";
3
+ import { normalizeIdentifier } from "micromark-util-normalize-identifier";
4
+ import GithubSlugger from "github-slugger";
5
+ // vendor/src/processor.js
6
+ /**
7
+ * @fileoverview Processes Markdown files for consumption by ESLint.
8
+ * @author Brandon Mills
9
+ */
10
+ /**
11
+ * @import { LintMessage, RuleTextEdit, SourceRange } from "@eslint/core";
12
+ * @import { Node, Parent, Code, Html } from "mdast";
13
+ * @import { Block, RangeMap } from "./types.js";
14
+ */
15
+ const UNSATISFIABLE_RULES = /* @__PURE__ */ new Set(["eol-last", "unicode-bom"]);
16
+ const SUPPORTS_AUTOFIX = true;
17
+ const BOM = "";
18
+ /**
19
+ * @type {Map<string, Block[]>}
20
+ */
21
+ const blocksCache = /* @__PURE__ */ new Map();
22
+ /**
23
+ * Performs a depth-first traversal of the Markdown AST.
24
+ * @param {Node} node A Markdown AST node.
25
+ * @param {{[key: string]: (() => void) | ((node: Code) => void) | ((node: Html) => void)}} callbacks A map of node types to callbacks.
26
+ * @returns {void}
27
+ */
28
+ function traverse(node, callbacks) {
29
+ if (callbacks[node.type])
30
+ /** @type {(node: Node) => void} */ callbacks[node.type](node);
31
+ else
32
+ /** @type {() => void} */ callbacks["*"]();
33
+ const parent = node;
34
+ if (typeof parent.children !== "undefined") for (let i = 0; i < parent.children.length; i++) traverse(parent.children[i], callbacks);
35
+ }
36
+ /**
37
+ * Extracts `eslint-*` or `global` comments from HTML comments if present.
38
+ * @param {string} html The text content of an HTML AST node.
39
+ * @returns {string} The comment's text without the opening and closing tags or
40
+ * an empty string if the text is not an ESLint HTML comment.
41
+ */
42
+ function getComment(html) {
43
+ const commentStart = "<!--";
44
+ const commentEnd = "-->";
45
+ const regex = /^(?:eslint\b|global\s)/u;
46
+ if (html.slice(0, 4) !== commentStart || html.slice(-3) !== commentEnd) return "";
47
+ const comment = html.slice(4, -3);
48
+ if (!regex.test(comment.trim())) return "";
49
+ return comment;
50
+ }
51
+ const leadingWhitespaceRegex = /^[>\s]*/u;
52
+ /**
53
+ * Gets the offset for the first column of the node's first line in the
54
+ * original source text.
55
+ * @param {Code} node A Markdown code block AST node.
56
+ * @returns {number} The offset for the first column of the node's first line.
57
+ */
58
+ function getBeginningOfLineOffset(node) {
59
+ return node.position.start.offset - node.position.start.column + 1;
60
+ }
61
+ /**
62
+ * Gets the leading text, typically whitespace with possible blockquote chars,
63
+ * used to indent a code block.
64
+ * @param {string} text The text of the file.
65
+ * @param {Code} node A Markdown code block AST node.
66
+ * @returns {string} The text from the start of the first line to the opening
67
+ * fence of the code block.
68
+ */
69
+ function getIndentText(text, node) {
70
+ return leadingWhitespaceRegex.exec(text.slice(getBeginningOfLineOffset(node)))[0];
71
+ }
72
+ /**
73
+ * When applying fixes, the postprocess step needs to know how to map fix ranges
74
+ * from their location in the linted JS to the original offset in the Markdown.
75
+ * Configuration comments and indentation trimming both complicate this process.
76
+ *
77
+ * Configuration comments appear in the linted JS but not in the Markdown code
78
+ * block. Fixes to configuration comments would cause undefined behavior and
79
+ * should be ignored during postprocessing. Fixes to actual code after
80
+ * configuration comments need to be mapped back to the code block after
81
+ * removing any offset due to configuration comments.
82
+ *
83
+ * Fenced code blocks can be indented by up to three spaces at the opening
84
+ * fence. Inside of a list, for example, this indent can be in addition to the
85
+ * indent already required for list item children. Leading whitespace inside
86
+ * indented code blocks is trimmed up to the level of the opening fence and does
87
+ * not appear in the linted code. Further, lines can have less leading
88
+ * whitespace than the opening fence, so not all lines are guaranteed to have
89
+ * the same column offset as the opening fence.
90
+ *
91
+ * The source code of a non-configuration-comment line in the linted JS is a
92
+ * suffix of the corresponding line in the Markdown code block. There are no
93
+ * differences within the line, so the mapping need only provide the offset
94
+ * delta at the beginning of each line.
95
+ * @param {string} text The text of the file.
96
+ * @param {Code} node A Markdown code block AST node.
97
+ * @param {string[]} comments List of configuration comment strings that will be
98
+ * inserted at the beginning of the code block.
99
+ * @returns {RangeMap[]} A list of offset-based adjustments, where lookups are
100
+ * done based on the `js` key, which represents the range in the linted JS,
101
+ * and the `md` key is the offset delta that, when added to the JS range,
102
+ * returns the corresponding location in the original Markdown source.
103
+ */
104
+ function getBlockRangeMap(text, node, comments) {
105
+ const startOffset = getBeginningOfLineOffset(node);
106
+ const lines = text.slice(startOffset, node.position.end.offset).split("\n");
107
+ const baseIndent = getIndentText(text, node).length;
108
+ const commentLength = comments.reduce((len, comment) => len + comment.length + 1, 0);
109
+ const rangeMap = [{
110
+ indent: baseIndent,
111
+ js: 0,
112
+ md: 0
113
+ }];
114
+ let jsOffset = commentLength;
115
+ let mdOffset = startOffset + lines[0].length + 1;
116
+ for (let i = 0; i + 1 < lines.length; i++) {
117
+ const line = lines[i + 1];
118
+ const leadingWhitespaceLength = leadingWhitespaceRegex.exec(line)[0].length;
119
+ const trimLength = Math.min(baseIndent, leadingWhitespaceLength);
120
+ rangeMap.push({
121
+ indent: trimLength,
122
+ js: jsOffset,
123
+ md: mdOffset + trimLength - jsOffset
124
+ });
125
+ mdOffset += line.length + 1;
126
+ jsOffset += line.length - trimLength + 1;
127
+ }
128
+ return rangeMap;
129
+ }
130
+ const codeBlockFileNameRegex = /filename=(?<quote>["'])(?<filename>.*?)\1/u;
131
+ /**
132
+ * Parses the file name from a block meta, if available.
133
+ * @param {Block} block A code block.
134
+ * @returns {string | null | undefined} The filename, if parsed from block meta.
135
+ */
136
+ function fileNameFromMeta(block) {
137
+ return block.meta?.match(codeBlockFileNameRegex)?.groups.filename.replaceAll(/\s+/gu, "_");
138
+ }
139
+ const languageToFileExtension = {
140
+ javascript: "js",
141
+ ecmascript: "js",
142
+ typescript: "ts",
143
+ markdown: "md"
144
+ };
145
+ const defaultParser = createParser();
146
+ /**
147
+ * Extracts lintable code blocks from Markdown text.
148
+ * @param {string} sourceText The text of the file.
149
+ * @param {string} filename The filename of the file.
150
+ * @returns {Array<{ filename: string, text: string }>} Source code blocks to lint.
151
+ */
152
+ function preprocess(sourceText, filename) {
153
+ const text = sourceText.startsWith(BOM) ? sourceText.slice(1) : sourceText;
154
+ const ast = defaultParser.parse(text);
155
+ /** @type {Block[]} */
156
+ const blocks = [];
157
+ blocksCache.set(filename, blocks);
158
+ /**
159
+ * During the depth-first traversal, keep track of any sequences of HTML
160
+ * comment nodes containing `eslint-*` or `global` comments. If a code
161
+ * block immediately follows such a sequence, insert the comments at the
162
+ * top of the code block. Any non-ESLint comment or other node type breaks
163
+ * and empties the sequence.
164
+ * @type {string[]}
165
+ */
166
+ let htmlComments = [];
167
+ traverse(ast, {
168
+ "*"() {
169
+ htmlComments = [];
170
+ },
171
+ /**
172
+ * Visit a code node.
173
+ * @param {Code} node The visited node.
174
+ * @returns {void}
175
+ */
176
+ code(node) {
177
+ if (node.lang) {
178
+ /** @type {string[]} */
179
+ const comments = [];
180
+ for (const comment of htmlComments) {
181
+ if (comment.trim() === "eslint-skip") {
182
+ htmlComments = [];
183
+ return;
184
+ }
185
+ comments.push(`/*${comment}*/`);
186
+ }
187
+ htmlComments = [];
188
+ blocks.push({
189
+ ...node,
190
+ baseIndentText: getIndentText(text, node),
191
+ comments,
192
+ rangeMap: getBlockRangeMap(text, node, comments)
193
+ });
194
+ }
195
+ },
196
+ /**
197
+ * Visit an HTML node.
198
+ * @param {Html} node The visited node.
199
+ * @returns {void}
200
+ */
201
+ html(node) {
202
+ const comment = getComment(node.value);
203
+ if (comment) htmlComments.push(comment);
204
+ else htmlComments = [];
205
+ }
206
+ });
207
+ return blocks.map((block, index) => {
208
+ const [language] = block.lang.trim().split(" ");
209
+ const fileExtension = Object.hasOwn(languageToFileExtension, language) ? languageToFileExtension[language] : language;
210
+ return {
211
+ filename: fileNameFromMeta(block) ?? `${index}.${fileExtension}`,
212
+ text: [
213
+ ...block.comments,
214
+ block.value,
215
+ ""
216
+ ].join("\n")
217
+ };
218
+ });
219
+ }
220
+ /**
221
+ * Adjusts a fix in a code block.
222
+ * @param {Block} block A code block.
223
+ * @param {RuleTextEdit} fix A fix to adjust.
224
+ * @returns {RuleTextEdit} The fix with adjusted ranges.
225
+ */
226
+ function adjustFix(block, fix) {
227
+ return {
228
+ range: fix.range.map((range) => {
229
+ let i = 1;
230
+ while (i < block.rangeMap.length && block.rangeMap[i].js <= range) i++;
231
+ return range + block.rangeMap[i - 1].md;
232
+ }),
233
+ text: fix.text.replace(/\n/gu, `\n${block.baseIndentText}`)
234
+ };
235
+ }
236
+ /**
237
+ * Creates a map function that adjusts messages in a code block.
238
+ * @param {Block} block A code block.
239
+ * @returns {(message: LintMessage) => LintMessage | null} A function that adjusts messages in a code block.
240
+ */
241
+ function adjustBlock(block) {
242
+ const leadingCommentLines = block.comments.reduce((count, comment) => count + comment.split("\n").length, 0);
243
+ const blockStart = block.position.start.line;
244
+ /**
245
+ * Adjusts ESLint messages to point to the correct location in the Markdown.
246
+ * @param {LintMessage} message A message from ESLint.
247
+ * @returns {LintMessage} The same message, but adjusted to the correct location.
248
+ */
249
+ return function adjustMessage(message) {
250
+ if (!Number.isInteger(message.line)) return {
251
+ ...message,
252
+ line: blockStart,
253
+ column: block.position.start.column
254
+ };
255
+ const lineInCode = message.line - leadingCommentLines;
256
+ if (lineInCode < 1 || lineInCode >= block.rangeMap.length) return null;
257
+ /** @type {Pick<LintMessage, "line" | "column" | "endLine" | "suggestions">} */
258
+ const out = {
259
+ line: lineInCode + blockStart,
260
+ column: message.column + block.rangeMap[lineInCode].indent
261
+ };
262
+ if (Number.isInteger(message.endLine)) out.endLine = message.endLine - leadingCommentLines + blockStart;
263
+ if (Array.isArray(message.suggestions)) out.suggestions = message.suggestions.map((suggestion) => ({
264
+ ...suggestion,
265
+ fix: adjustFix(block, suggestion.fix)
266
+ }));
267
+ const adjustedFix = {};
268
+ if (message.fix) adjustedFix.fix = adjustFix(block, message.fix);
269
+ return {
270
+ ...message,
271
+ ...out,
272
+ ...adjustedFix
273
+ };
274
+ };
275
+ }
276
+ /**
277
+ * Excludes unsatisfiable rules from the list of messages.
278
+ * @param {LintMessage} message A message from the linter.
279
+ * @returns {boolean} True if the message should be included in output.
280
+ */
281
+ function excludeUnsatisfiableRules(message) {
282
+ return message && !UNSATISFIABLE_RULES.has(message.ruleId);
283
+ }
284
+ /**
285
+ * Transforms generated messages for output.
286
+ * @param {Array<LintMessage[]>} messages An array containing one array of messages
287
+ * for each code block returned from `preprocess`.
288
+ * @param {string} filename The filename of the file
289
+ * @returns {LintMessage[]} A flattened array of messages with mapped locations.
290
+ */
291
+ function postprocess(messages, filename) {
292
+ const blocks = blocksCache.get(filename);
293
+ blocksCache.delete(filename);
294
+ return messages.flatMap((group, i) => {
295
+ const adjust = adjustBlock(blocks[i]);
296
+ return group.map(adjust).filter(excludeUnsatisfiableRules);
297
+ });
298
+ }
299
+ const processor = {
300
+ meta: {
301
+ name: "@eslint/markdown/markdown",
302
+ version: "8.0.3"
303
+ },
304
+ preprocess,
305
+ postprocess,
306
+ supportsAutofix: SUPPORTS_AUTOFIX
307
+ };
308
+ // vendor/src/util.js
309
+ /**
310
+ * @fileoverview Utility Library
311
+ * @author Nicholas C. Zakas
312
+ */
313
+ /**
314
+ * Line ending pattern to match all line endings (CRLF, CR, LF). (CommonMark spec)
315
+ * @see https://spec.commonmark.org/0.31.2/#line-ending
316
+ */
317
+ const lineEndingPattern = /\r\n|[\r\n]/u;
318
+ /**
319
+ * CommonMark does not allow any white space between the brackets in a reference link.
320
+ * If that pattern is detected, then it's treated as text and not as a link. This pattern
321
+ * is used to detect that situation.
322
+ */
323
+ const illegalShorthandTailPattern = /\]\[\s+\]$/u;
324
+ /**
325
+ * Regular expression to match HTML comments, including multiline comments.
326
+ */
327
+ const htmlCommentPattern = /<!--[\s\S]*?-->/gu;
328
+ /**
329
+ * Checks if a frontmatter block contains a title matching the given pattern.
330
+ * @param {string} value The frontmatter content.
331
+ * @param {RegExp|null} pattern The pattern to match against.
332
+ * @returns {boolean} Whether a title was found.
333
+ */
334
+ function frontmatterHasTitle(value, pattern) {
335
+ if (!pattern) return false;
336
+ const lines = value.split(lineEndingPattern);
337
+ for (const line of lines) if (pattern.test(line)) return true;
338
+ return false;
339
+ }
340
+ /**
341
+ * Normalizes a Markdown reference identifier.
342
+ * @param {string} identifier The identifier to normalize.
343
+ * @returns {string} The normalized lowercase identifier.
344
+ */
345
+ function normalizeIdentifier$1(identifier) {
346
+ return normalizeIdentifier(identifier).toLowerCase();
347
+ }
348
+ /**
349
+ * Replaces all HTML comments with whitespace.
350
+ * This preserves offsets and locations of characters
351
+ * outside HTML comments by keeping line breaks and replacing
352
+ * other code units with a space character.
353
+ * @param {string} value The string to remove HTML comments from.
354
+ * @returns {string} The string with HTML comments removed.
355
+ */
356
+ function stripHtmlComments(value) {
357
+ return value.replace(htmlCommentPattern, (match) => match.replace(/[^\r\n]/g, " "));
358
+ }
359
+ // vendor/src/language/markdown-source-code.js
360
+ /**
361
+ * @fileoverview The MarkdownSourceCode class.
362
+ * @author Nicholas C. Zakas
363
+ */
364
+ /**
365
+ * @import { Position } from "unist";
366
+ * @import { Parent, Root, Node, Html } from "mdast";
367
+ * @import { TraversalStep, FileProblem, DirectiveType, RulesConfig } from "@eslint/core";
368
+ * @import { MarkdownLanguageOptions } from "../types.js";
369
+ */
370
+ const commentParser = new ConfigCommentParser();
371
+ const configCommentStart = /<!--\s*eslint(?:-enable|-disable(?:(?:-next)?-line)?)?(?:\s|-->)/u;
372
+ const htmlComment = /<!--(.*?)-->/gsu;
373
+ /**
374
+ * Represents an inline config comment in the source code.
375
+ */
376
+ var InlineConfigComment = class {
377
+ /**
378
+ * The comment text.
379
+ * @type {string}
380
+ */
381
+ value;
382
+ /**
383
+ * The position of the comment in the source code.
384
+ * @type {Position}
385
+ */
386
+ position;
387
+ /**
388
+ * Creates a new instance.
389
+ * @param {Object} options The options for the instance.
390
+ * @param {string} options.value The comment text.
391
+ * @param {Position} options.position The position of the comment in the source code.
392
+ */
393
+ constructor({ value, position }) {
394
+ this.value = value.trim();
395
+ this.position = position;
396
+ }
397
+ };
398
+ /**
399
+ * Extracts inline configuration comments from an HTML node.
400
+ * @param {Html} node The HTML node to extract comments from.
401
+ * @param {MarkdownSourceCode} sourceCode The Markdown source code object.
402
+ * @returns {Array<InlineConfigComment>} The inline configuration comments found in the node.
403
+ */
404
+ function extractInlineConfigCommentsFromHTML(node, sourceCode) {
405
+ if (!configCommentStart.test(node.value)) return [];
406
+ /** @type {Array<InlineConfigComment>} */
407
+ const comments = [];
408
+ /** @type {RegExpExecArray | null} */
409
+ let match;
410
+ while (match = htmlComment.exec(node.value)) if (configCommentStart.test(match[0])) {
411
+ const startOffset = match.index + node.position.start.offset;
412
+ const endOffset = startOffset + match[0].length;
413
+ comments.push(new InlineConfigComment({
414
+ value: match[1].trim(),
415
+ position: {
416
+ start: {
417
+ ...sourceCode.getLocFromIndex(startOffset),
418
+ offset: startOffset
419
+ },
420
+ end: {
421
+ ...sourceCode.getLocFromIndex(endOffset),
422
+ offset: endOffset
423
+ }
424
+ }
425
+ }));
426
+ }
427
+ return comments;
428
+ }
429
+ /**
430
+ * Markdown Source Code Object
431
+ * @extends {TextSourceCodeBase<{LangOptions: MarkdownLanguageOptions, RootNode: Root, SyntaxElementWithLoc: Node, ConfigNode: { value: string; position: Position }}>}
432
+ */
433
+ var MarkdownSourceCode = class extends TextSourceCodeBase {
434
+ /**
435
+ * Cached traversal steps.
436
+ * @type {Array<VisitNodeStep>|undefined}
437
+ */
438
+ #steps;
439
+ /**
440
+ * Cache of parent nodes.
441
+ * @type {WeakMap<Node, Parent|undefined>}
442
+ */
443
+ #parents = /* @__PURE__ */ new WeakMap();
444
+ /**
445
+ * Collection of HTML nodes. Used to find directive comments.
446
+ * @type {Array<Html>}
447
+ */
448
+ #htmlNodes = [];
449
+ /**
450
+ * Collection of inline configuration comments.
451
+ * @type {Array<InlineConfigComment>}
452
+ */
453
+ #inlineConfigComments;
454
+ /**
455
+ * The AST of the source code.
456
+ * @type {Root}
457
+ */
458
+ ast = void 0;
459
+ /**
460
+ * Creates a new instance.
461
+ * @param {Object} options The options for the instance.
462
+ * @param {string} options.text The source code text.
463
+ * @param {Root} options.ast The root AST node.
464
+ */
465
+ constructor({ text, ast }) {
466
+ super({
467
+ ast,
468
+ text,
469
+ lineEndingPattern
470
+ });
471
+ this.ast = ast;
472
+ this.traverse();
473
+ }
474
+ /**
475
+ * Returns the parent of the given node.
476
+ * @param {Node} node The node to get the parent of.
477
+ * @returns {Parent|undefined} The parent of the node.
478
+ */
479
+ getParent(node) {
480
+ return this.#parents.get(node);
481
+ }
482
+ /**
483
+ * Returns an array of all inline configuration nodes found in the
484
+ * source code.
485
+ * @returns {Array<InlineConfigComment>} An array of all inline configuration nodes.
486
+ */
487
+ getInlineConfigNodes() {
488
+ if (!this.#inlineConfigComments) this.#inlineConfigComments = this.#htmlNodes.flatMap((htmlNode) => extractInlineConfigCommentsFromHTML(htmlNode, this));
489
+ return this.#inlineConfigComments;
490
+ }
491
+ /**
492
+ * Returns an all directive nodes that enable or disable rules along with any problems
493
+ * encountered while parsing the directives.
494
+ * @returns {{problems:Array<FileProblem>,directives:Array<Directive>}} Information
495
+ * that ESLint needs to further process the directives.
496
+ */
497
+ getDisableDirectives() {
498
+ /** @type {Array<FileProblem>} */
499
+ const problems = [];
500
+ /** @type {Array<Directive>} */
501
+ const directives = [];
502
+ this.getInlineConfigNodes().forEach((comment) => {
503
+ const { label, value, justification: justificationPart } = commentParser.parseDirective(comment.value);
504
+ if (label === "eslint-disable-line" && comment.position.start.line !== comment.position.end.line) {
505
+ const message = `${label} comment should not span multiple lines.`;
506
+ problems.push({
507
+ ruleId: null,
508
+ message,
509
+ loc: comment.position
510
+ });
511
+ return;
512
+ }
513
+ switch (label) {
514
+ case "eslint-disable":
515
+ case "eslint-enable":
516
+ case "eslint-disable-next-line":
517
+ case "eslint-disable-line": {
518
+ const directiveType = label.slice(7);
519
+ directives.push(new Directive({
520
+ type: directiveType,
521
+ node: comment,
522
+ value,
523
+ justification: justificationPart
524
+ }));
525
+ }
526
+ }
527
+ });
528
+ return {
529
+ problems,
530
+ directives
531
+ };
532
+ }
533
+ /**
534
+ * Returns inline rule configurations along with any problems
535
+ * encountered while parsing the configurations.
536
+ * @returns {{problems:Array<FileProblem>,configs:Array<{config:{rules:RulesConfig},loc:Position}>}} Information
537
+ * that ESLint needs to further process the rule configurations.
538
+ */
539
+ applyInlineConfig() {
540
+ /** @type {Array<FileProblem>} */
541
+ const problems = [];
542
+ /** @type {Array<{config:{rules:RulesConfig},loc:Position}>} */
543
+ const configs = [];
544
+ this.getInlineConfigNodes().forEach((comment) => {
545
+ const { label, value } = commentParser.parseDirective(comment.value);
546
+ if (label === "eslint") {
547
+ const parseResult = commentParser.parseJSONLikeConfig(value);
548
+ if (parseResult.ok) configs.push({
549
+ config: { rules: parseResult.config },
550
+ loc: comment.position
551
+ });
552
+ else problems.push({
553
+ ruleId: null,
554
+ message:
555
+ /** @type {{ok: false, error: { message: string }}} */ parseResult.error.message,
556
+ loc: comment.position
557
+ });
558
+ }
559
+ });
560
+ return {
561
+ configs,
562
+ problems
563
+ };
564
+ }
565
+ /**
566
+ * Traverse the source code and return the steps that were taken.
567
+ * @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
568
+ */
569
+ traverse() {
570
+ if (this.#steps) return this.#steps.values();
571
+ /** @type {Array<VisitNodeStep>} */
572
+ const steps = this.#steps = [];
573
+ /**
574
+ * Recursively visits a node and its children.
575
+ * @param {Node} node The node to visit.
576
+ * @param {Parent} [parent] The parent of the node.
577
+ * @returns {void}
578
+ */
579
+ const visit = (node, parent) => {
580
+ this.#parents.set(node, parent);
581
+ steps.push(new VisitNodeStep({
582
+ target: node,
583
+ phase: 1,
584
+ args: [node, parent]
585
+ }));
586
+ if (node.type === "html") this.#htmlNodes.push(node);
587
+ if ("children" in node) {
588
+ const parentNode = node;
589
+ parentNode.children.forEach((child) => {
590
+ visit(child, parentNode);
591
+ });
592
+ }
593
+ steps.push(new VisitNodeStep({
594
+ target: node,
595
+ phase: 2,
596
+ args: [node, parent]
597
+ }));
598
+ };
599
+ visit(this.ast);
600
+ return steps.values();
601
+ }
602
+ };
603
+ // vendor/src/language/markdown-language.js
604
+ /**
605
+ * @fileoverview The MarkdownLanguage class.
606
+ * @author Nicholas C. Zakas
607
+ */
608
+ /**
609
+ * @import { Language, File, ParseResult, OkParseResult } from "@eslint/core";
610
+ * @import { Node, Root } from "mdast";
611
+ * @import { Parser } from "satorigear";
612
+ * @import { MarkdownLanguageOptions, MarkdownLanguageContext } from "../types.js";
613
+ * @typedef {"commonmark"|"gfm"} ParserMode
614
+ */
615
+ /**
616
+ * The cache of parsers.
617
+ * @type {Map<string, Parser>}
618
+ */
619
+ const parsers = /* @__PURE__ */ new Map();
620
+ /**
621
+ * Create parser options based on `mode` and `languageOptions`.
622
+ * @param {ParserMode} mode The markdown parser mode.
623
+ * @param {MarkdownLanguageOptions} languageOptions Language options.
624
+ * @returns {Parser} Satorigear parser.
625
+ */
626
+ function createParser$1(mode, languageOptions) {
627
+ const key = [
628
+ mode,
629
+ languageOptions.frontmatter,
630
+ languageOptions.math
631
+ ].join("+");
632
+ if (parsers.has(key)) return parsers.get(key);
633
+ const gfm = mode === "gfm";
634
+ const parser = createParser({ features: {
635
+ footnote: gfm,
636
+ frontmatter: languageOptions.frontmatter && { marker: languageOptions.frontmatter === "toml" ? "+" : "-" },
637
+ math: languageOptions.math,
638
+ strikethrough: gfm,
639
+ table: gfm,
640
+ taskList: gfm
641
+ } });
642
+ parsers.set(key, parser);
643
+ return parser;
644
+ }
645
+ /**
646
+ * Markdown Language Object
647
+ * @implements {Language<{ LangOptions: MarkdownLanguageOptions; Code: MarkdownSourceCode; RootNode: Root; Node: Node }>}
648
+ */
649
+ var MarkdownLanguage = class {
650
+ /**
651
+ * The type of file to read.
652
+ * @type {"text"}
653
+ */
654
+ fileType = "text";
655
+ /**
656
+ * The line number at which the parser starts counting.
657
+ * @type {0|1}
658
+ */
659
+ lineStart = 1;
660
+ /**
661
+ * The column number at which the parser starts counting.
662
+ * @type {0|1}
663
+ */
664
+ columnStart = 1;
665
+ /**
666
+ * The name of the key that holds the type of the node.
667
+ * @type {string}
668
+ */
669
+ nodeTypeKey = "type";
670
+ /**
671
+ * Default language options. User-defined options are merged with this object.
672
+ * @type {MarkdownLanguageOptions}
673
+ */
674
+ defaultLanguageOptions = {
675
+ frontmatter: false,
676
+ math: false
677
+ };
678
+ /**
679
+ * The Markdown parser mode.
680
+ * @type {ParserMode}
681
+ */
682
+ #mode = "commonmark";
683
+ /**
684
+ * Creates a new instance.
685
+ * @param {Object} options The options to use for this instance.
686
+ * @param {ParserMode} [options.mode] The Markdown parser mode to use.
687
+ */
688
+ constructor({ mode } = {}) {
689
+ if (mode) this.#mode = mode;
690
+ }
691
+ /**
692
+ * Validates the language options.
693
+ * @param {MarkdownLanguageOptions} languageOptions The language options to validate.
694
+ * @returns {void}
695
+ * @throws {Error} When the language options are invalid.
696
+ */
697
+ validateLanguageOptions(languageOptions) {
698
+ const frontmatterOption = languageOptions?.frontmatter;
699
+ if (frontmatterOption !== void 0 && !(/* @__PURE__ */ new Set([
700
+ false,
701
+ "yaml",
702
+ "toml",
703
+ "json"
704
+ ])).has(frontmatterOption)) throw new Error(`Invalid language option value \`${String(frontmatterOption)}\` for frontmatter. Expected one of \`false\`, \`"yaml"\`, \`"toml"\`, or \`"json"\`.`);
705
+ const mathOption = languageOptions?.math;
706
+ if (mathOption !== void 0 && typeof mathOption !== "boolean") throw new Error(`Invalid language option value \`${String(mathOption)}\` for math. Expected a boolean.`);
707
+ }
708
+ /**
709
+ * Parses the given file into an AST.
710
+ * @param {File} file The virtual file to parse.
711
+ * @param {MarkdownLanguageContext} context The options to use for parsing.
712
+ * @returns {ParseResult<Root>} The result of parsing.
713
+ */
714
+ parse(file, context) {
715
+ const text = file.body;
716
+ try {
717
+ return {
718
+ ok: true,
719
+ ast: createParser$1(this.#mode, context?.languageOptions).parse(text)
720
+ };
721
+ } catch (ex) {
722
+ return {
723
+ ok: false,
724
+ errors: [ex]
725
+ };
726
+ }
727
+ }
728
+ /**
729
+ * Creates a new `MarkdownSourceCode` object from the given information.
730
+ * @param {File} file The virtual file to create a `MarkdownSourceCode` object from.
731
+ * @param {OkParseResult<Root>} parseResult The result returned from `parse()`.
732
+ * @returns {MarkdownSourceCode} The new `MarkdownSourceCode` object.
733
+ */
734
+ createSourceCode(file, parseResult) {
735
+ return new MarkdownSourceCode({
736
+ text: file.body,
737
+ ast: parseResult.ast
738
+ });
739
+ }
740
+ };
741
+ // vendor/src/rules/fenced-code-language.js
742
+ /**
743
+ * @fileoverview Rule to enforce languages for fenced code.
744
+ * @author Nicholas C. Zakas
745
+ */
746
+ /**
747
+ * @import { MarkdownRuleDefinition } from "../types.js";
748
+ * @typedef {"missingLanguage" | "disallowedLanguage"} FencedCodeLanguageMessageIds
749
+ * @typedef {[{ required?: string[] }]} FencedCodeLanguageOptions
750
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: FencedCodeLanguageOptions, MessageIds: FencedCodeLanguageMessageIds }>} FencedCodeLanguageRuleDefinition
751
+ */
752
+ const fencedCodeCharacters = /* @__PURE__ */ new Set(["`", "~"]);
753
+ var fenced_code_language_default = {
754
+ meta: {
755
+ type: "problem",
756
+ languages: ["markdown/commonmark", "markdown/gfm"],
757
+ docs: {
758
+ recommended: true,
759
+ description: "Require languages for fenced code blocks",
760
+ dialects: ["CommonMark", "GFM"],
761
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/fenced-code-language.md"
762
+ },
763
+ messages: {
764
+ missingLanguage: "Missing code block language.",
765
+ disallowedLanguage: "Code block language \"{{lang}}\" is not allowed."
766
+ },
767
+ schema: [{
768
+ type: "object",
769
+ properties: { required: {
770
+ type: "array",
771
+ items: { type: "string" },
772
+ uniqueItems: true
773
+ } },
774
+ additionalProperties: false
775
+ }],
776
+ defaultOptions: [{ required: [] }]
777
+ },
778
+ create(context) {
779
+ const required = new Set(context.options[0].required);
780
+ const { sourceCode } = context;
781
+ return { code(node) {
782
+ if (!node.lang) {
783
+ if (!fencedCodeCharacters.has(sourceCode.text[node.position.start.offset])) return;
784
+ /** @type {number} */
785
+ let openingCodeFenceEndOffset;
786
+ for (openingCodeFenceEndOffset = node.position.start.offset; fencedCodeCharacters.has(sourceCode.text[openingCodeFenceEndOffset]); openingCodeFenceEndOffset++);
787
+ context.report({
788
+ loc: {
789
+ start: node.position.start,
790
+ end: {
791
+ line: node.position.start.line,
792
+ column: node.position.start.column + openingCodeFenceEndOffset - node.position.start.offset
793
+ }
794
+ },
795
+ messageId: "missingLanguage"
796
+ });
797
+ return;
798
+ }
799
+ if (required.size && !required.has(node.lang)) {
800
+ const langIndex = sourceCode.getText(node).indexOf(node.lang);
801
+ context.report({
802
+ loc: {
803
+ start: node.position.start,
804
+ end: {
805
+ line: node.position.start.line,
806
+ column: node.position.start.column + langIndex + node.lang.length
807
+ }
808
+ },
809
+ messageId: "disallowedLanguage",
810
+ data: { lang: node.lang }
811
+ });
812
+ }
813
+ } };
814
+ }
815
+ };
816
+ // vendor/src/rules/fenced-code-meta.js
817
+ /**
818
+ * @fileoverview Rule to require or disallow metadata for fenced code blocks.
819
+ * @author TKDev7
820
+ */
821
+ /**
822
+ * @import { MarkdownRuleDefinition } from "../types.js";
823
+ * @typedef {"missingMetadata" | "disallowedMetadata"} FencedCodeMetaMessageIds
824
+ * @typedef {["always" | "never"]} FencedCodeMetaOptions
825
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: FencedCodeMetaOptions, MessageIds: FencedCodeMetaMessageIds }>} FencedCodeMetaRuleDefinition
826
+ */
827
+ var fenced_code_meta_default = {
828
+ meta: {
829
+ type: "problem",
830
+ languages: ["markdown/commonmark", "markdown/gfm"],
831
+ docs: {
832
+ recommended: false,
833
+ description: "Require or disallow metadata for fenced code blocks",
834
+ dialects: ["CommonMark", "GFM"],
835
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/fenced-code-meta.md"
836
+ },
837
+ messages: {
838
+ missingMetadata: "Missing code block metadata.",
839
+ disallowedMetadata: "Code block metadata is not allowed."
840
+ },
841
+ schema: [{ enum: ["always", "never"] }],
842
+ defaultOptions: ["always"]
843
+ },
844
+ create(context) {
845
+ const [mode] = context.options;
846
+ const { sourceCode } = context;
847
+ return { code(node) {
848
+ const fenceLineText = sourceCode.lines[node.position.start.line - 1].slice(node.position.start.column - 1);
849
+ if (mode === "always") {
850
+ if (node.lang && !node.meta) {
851
+ const langIndex = fenceLineText.indexOf(node.lang);
852
+ context.report({
853
+ loc: {
854
+ start: node.position.start,
855
+ end: {
856
+ line: node.position.start.line,
857
+ column: node.position.start.column + langIndex + node.lang.length
858
+ }
859
+ },
860
+ messageId: "missingMetadata"
861
+ });
862
+ }
863
+ return;
864
+ }
865
+ if (node.meta) {
866
+ const metaIndex = fenceLineText.lastIndexOf(node.meta);
867
+ context.report({
868
+ loc: {
869
+ start: {
870
+ line: node.position.start.line,
871
+ column: node.position.start.column + metaIndex
872
+ },
873
+ end: {
874
+ line: node.position.start.line,
875
+ column: node.position.start.column + metaIndex + node.meta.trimEnd().length
876
+ }
877
+ },
878
+ messageId: "disallowedMetadata"
879
+ });
880
+ }
881
+ } };
882
+ }
883
+ };
884
+ // vendor/src/rules/heading-increment.js
885
+ /**
886
+ * @fileoverview Rule to enforce heading levels increment by one.
887
+ * @author Nicholas C. Zakas
888
+ */
889
+ /**
890
+ * @import { MarkdownRuleDefinition } from "../types.js";
891
+ * @typedef {"skippedHeading"} HeadingIncrementMessageIds
892
+ * @typedef {[{ frontmatterTitle?: string }]} HeadingIncrementOptions
893
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: HeadingIncrementOptions, MessageIds: HeadingIncrementMessageIds }>} HeadingIncrementRuleDefinition
894
+ */
895
+ var heading_increment_default = {
896
+ meta: {
897
+ type: "problem",
898
+ languages: ["markdown/commonmark", "markdown/gfm"],
899
+ docs: {
900
+ recommended: true,
901
+ description: "Enforce heading levels increment by one",
902
+ dialects: ["CommonMark", "GFM"],
903
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/heading-increment.md"
904
+ },
905
+ messages: { skippedHeading: "Heading level skipped from {{fromLevel}} to {{toLevel}}." },
906
+ schema: [{
907
+ type: "object",
908
+ properties: { frontmatterTitle: { type: "string" } },
909
+ additionalProperties: false
910
+ }],
911
+ defaultOptions: [{ frontmatterTitle: "^(?!\\s*['\"]title[:=]['\"])\\s*\\{?\\s*['\"]?title['\"]?\\s*[:=]" }]
912
+ },
913
+ create(context) {
914
+ const [{ frontmatterTitle }] = context.options;
915
+ const titlePattern = frontmatterTitle === "" ? null : new RegExp(frontmatterTitle, "iu");
916
+ let lastHeadingDepth = 0;
917
+ return {
918
+ yaml(node) {
919
+ if (frontmatterHasTitle(node.value, titlePattern)) lastHeadingDepth = 1;
920
+ },
921
+ toml(node) {
922
+ if (frontmatterHasTitle(node.value, titlePattern)) lastHeadingDepth = 1;
923
+ },
924
+ json(node) {
925
+ if (frontmatterHasTitle(node.value, titlePattern)) lastHeadingDepth = 1;
926
+ },
927
+ heading(node) {
928
+ if (lastHeadingDepth > 0 && node.depth > lastHeadingDepth + 1) context.report({
929
+ loc: node.position,
930
+ messageId: "skippedHeading",
931
+ data: {
932
+ fromLevel: lastHeadingDepth,
933
+ toLevel: node.depth
934
+ }
935
+ });
936
+ lastHeadingDepth = node.depth;
937
+ }
938
+ };
939
+ }
940
+ };
941
+ // vendor/src/rules/no-bare-urls.js
942
+ /**
943
+ * @fileoverview Rule to prevent bare URLs in Markdown.
944
+ * @author xbinaryx
945
+ */
946
+ /**
947
+ * @import { Link, Html } from "mdast";
948
+ * @import { MarkdownRuleDefinition } from "../types.js";
949
+ * @typedef {"bareUrl"} NoBareUrlsMessageIds
950
+ * @typedef {[]} NoBareUrlsOptions
951
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoBareUrlsOptions, MessageIds: NoBareUrlsMessageIds }>} NoBareUrlsRuleDefinition
952
+ */
953
+ const htmlTagNamePattern = /^<(?<tagName>[^!>][^/\s>]*)/u;
954
+ const linkTextSpecialCharacterPattern = /[[\]\\*_~`$&|]/gu;
955
+ const linkDestinationSpecialCharacterPattern = /[\\()&|]/gu;
956
+ /**
957
+ * Parses an HTML tag to extract its name and closing status
958
+ * @param {string} tagText The HTML tag text to parse
959
+ * @returns {{ name: string, isClosing: boolean } | null} Object containing tag name and closing status, or null if not a valid tag
960
+ */
961
+ function parseHtmlTag(tagText) {
962
+ const match = tagText.match(htmlTagNamePattern);
963
+ if (match) {
964
+ const tagName = match.groups.tagName.toLowerCase();
965
+ const isClosing = tagName.startsWith("/");
966
+ return {
967
+ name: isClosing ? tagName.slice(1) : tagName,
968
+ isClosing
969
+ };
970
+ }
971
+ return null;
972
+ }
973
+ var no_bare_urls_default = {
974
+ meta: {
975
+ type: "problem",
976
+ languages: ["markdown/gfm"],
977
+ docs: {
978
+ description: "Disallow bare URLs",
979
+ dialects: ["GFM"],
980
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-bare-urls.md"
981
+ },
982
+ fixable: "code",
983
+ messages: { bareUrl: "Unexpected bare URL. Use autolink (<URL>) or link ([text](URL)) instead." }
984
+ },
985
+ create(context) {
986
+ const { sourceCode } = context;
987
+ /**
988
+ * This array is used to store all `Link` nodes for the final report.
989
+ * @type {Array<Link>}
990
+ */
991
+ const linkNodes = [];
992
+ /**
993
+ * This array is used to store `Link` nodes that are estimated to be between opening and closing HTML tags.
994
+ * @type {Array<Link>}
995
+ */
996
+ const tempLinkNodes = [];
997
+ /** @type {string | null} */
998
+ let lastTagName = null;
999
+ /**
1000
+ * Resets `tempLinkNodes` and `lastTagName`
1001
+ * @returns {void}
1002
+ */
1003
+ function reset() {
1004
+ tempLinkNodes.length = 0;
1005
+ lastTagName = null;
1006
+ }
1007
+ return {
1008
+ ":matches(heading, paragraph, tableCell) html"(node) {
1009
+ const tagInfo = parseHtmlTag(node.value);
1010
+ if (!tagInfo) return;
1011
+ if (!tagInfo.isClosing && lastTagName === null) lastTagName = tagInfo.name;
1012
+ if (tagInfo.isClosing && lastTagName === tagInfo.name) reset();
1013
+ },
1014
+ ":matches(heading, paragraph, tableCell) link"(node) {
1015
+ if (lastTagName !== null) tempLinkNodes.push(node);
1016
+ else linkNodes.push(node);
1017
+ },
1018
+ "heading:exit"() {
1019
+ linkNodes.push(...tempLinkNodes);
1020
+ reset();
1021
+ },
1022
+ "paragraph:exit"() {
1023
+ linkNodes.push(...tempLinkNodes);
1024
+ reset();
1025
+ },
1026
+ "tableCell:exit"() {
1027
+ linkNodes.push(...tempLinkNodes);
1028
+ reset();
1029
+ },
1030
+ "root:exit"() {
1031
+ for (const linkNode of linkNodes) {
1032
+ const text = sourceCode.getText(linkNode);
1033
+ const { url } = linkNode;
1034
+ if (url === text || url === `http://${text}` || url === `mailto:${text}`) context.report({
1035
+ node: linkNode,
1036
+ messageId: "bareUrl",
1037
+ fix(fixer) {
1038
+ if (url !== `http://${text}`) return fixer.replaceText(linkNode, `<${text}>`);
1039
+ const escapedLinkText = text.replace(linkTextSpecialCharacterPattern, "\\$&");
1040
+ const escapedLinkDestination = url.replace(linkDestinationSpecialCharacterPattern, "\\$&");
1041
+ return fixer.replaceText(linkNode, `[${escapedLinkText}](${escapedLinkDestination})`);
1042
+ }
1043
+ });
1044
+ }
1045
+ }
1046
+ };
1047
+ }
1048
+ };
1049
+ // vendor/src/rules/no-duplicate-definitions.js
1050
+ /**
1051
+ * @fileoverview Rule to prevent duplicate definitions in Markdown.
1052
+ * @author 루밀LuMir(lumirlumir)
1053
+ */
1054
+ /**
1055
+ * @import { Definition, FootnoteDefinition } from "mdast";
1056
+ * @import { MarkdownRuleDefinition } from "../types.js";
1057
+ * @typedef {"duplicateDefinition" | "duplicateFootnoteDefinition"} NoDuplicateDefinitionsMessageIds
1058
+ * @typedef {[{ allowDefinitions?: string[], allowFootnoteDefinitions?: string[], checkFootnoteDefinitions?: boolean }]} NoDuplicateDefinitionsOptions
1059
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoDuplicateDefinitionsOptions, MessageIds: NoDuplicateDefinitionsMessageIds }>} NoDuplicateDefinitionsRuleDefinition
1060
+ */
1061
+ var no_duplicate_definitions_default = {
1062
+ meta: {
1063
+ type: "problem",
1064
+ languages: ["markdown/commonmark", "markdown/gfm"],
1065
+ docs: {
1066
+ recommended: true,
1067
+ description: "Disallow duplicate definitions",
1068
+ dialects: ["CommonMark", "GFM"],
1069
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-duplicate-definitions.md"
1070
+ },
1071
+ messages: {
1072
+ duplicateDefinition: "Unexpected duplicate definition `{{ identifier }}` (label: `{{ label }}`) found. First defined at line {{ firstLine }} (label: `{{ firstLabel }}`).",
1073
+ duplicateFootnoteDefinition: "Unexpected duplicate footnote definition `{{ identifier }}` (label: `{{ label }}`) found. First defined at line {{ firstLine }} (label: `{{ firstLabel }}`)."
1074
+ },
1075
+ schema: [{
1076
+ type: "object",
1077
+ properties: {
1078
+ allowDefinitions: {
1079
+ type: "array",
1080
+ items: { type: "string" },
1081
+ uniqueItems: true
1082
+ },
1083
+ allowFootnoteDefinitions: {
1084
+ type: "array",
1085
+ items: { type: "string" },
1086
+ uniqueItems: true
1087
+ },
1088
+ checkFootnoteDefinitions: { type: "boolean" }
1089
+ },
1090
+ additionalProperties: false
1091
+ }],
1092
+ defaultOptions: [{
1093
+ allowDefinitions: ["//"],
1094
+ allowFootnoteDefinitions: [],
1095
+ checkFootnoteDefinitions: true
1096
+ }]
1097
+ },
1098
+ create(context) {
1099
+ const allowDefinitions = new Set(context.options[0].allowDefinitions.map(normalizeIdentifier$1));
1100
+ const allowFootnoteDefinitions = new Set(context.options[0].allowFootnoteDefinitions.map(normalizeIdentifier$1));
1101
+ const [{ checkFootnoteDefinitions }] = context.options;
1102
+ /** @type {Map<string, Definition>} */
1103
+ const definitions = /* @__PURE__ */ new Map();
1104
+ /** @type {Map<string, FootnoteDefinition>} */
1105
+ const footnoteDefinitions = /* @__PURE__ */ new Map();
1106
+ return {
1107
+ definition(node) {
1108
+ if (allowDefinitions.has(node.identifier)) return;
1109
+ if (definitions.has(node.identifier)) {
1110
+ const firstDefinitionNode = definitions.get(node.identifier);
1111
+ context.report({
1112
+ node,
1113
+ messageId: "duplicateDefinition",
1114
+ data: {
1115
+ identifier: node.identifier,
1116
+ label: node.label.trim(),
1117
+ firstLine: firstDefinitionNode.position.start.line,
1118
+ firstLabel: firstDefinitionNode.label.trim()
1119
+ }
1120
+ });
1121
+ } else definitions.set(node.identifier, node);
1122
+ },
1123
+ footnoteDefinition(node) {
1124
+ if (!checkFootnoteDefinitions || allowFootnoteDefinitions.has(node.identifier)) return;
1125
+ if (footnoteDefinitions.has(node.identifier)) {
1126
+ const firstFootnoteDefinitionNode = footnoteDefinitions.get(node.identifier);
1127
+ context.report({
1128
+ node,
1129
+ messageId: "duplicateFootnoteDefinition",
1130
+ data: {
1131
+ identifier: node.identifier,
1132
+ label: node.label,
1133
+ firstLine: firstFootnoteDefinitionNode.position.start.line,
1134
+ firstLabel: firstFootnoteDefinitionNode.label
1135
+ }
1136
+ });
1137
+ } else footnoteDefinitions.set(node.identifier, node);
1138
+ }
1139
+ };
1140
+ }
1141
+ };
1142
+ // vendor/src/rules/no-duplicate-headings.js
1143
+ /**
1144
+ * @fileoverview Rule to prevent duplicate headings in Markdown.
1145
+ * @author Nicholas C. Zakas
1146
+ */
1147
+ /**
1148
+ * @import { MarkdownRuleDefinition } from "../types.js";
1149
+ * @typedef {"duplicateHeading"} NoDuplicateHeadingsMessageIds
1150
+ * @typedef {[{ checkSiblingsOnly?: boolean }]} NoDuplicateHeadingsOptions
1151
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoDuplicateHeadingsOptions, MessageIds: NoDuplicateHeadingsMessageIds }>} NoDuplicateHeadingsRuleDefinition
1152
+ */
1153
+ var no_duplicate_headings_default = {
1154
+ meta: {
1155
+ type: "problem",
1156
+ languages: ["markdown/commonmark", "markdown/gfm"],
1157
+ docs: {
1158
+ description: "Disallow duplicate headings in the same document",
1159
+ dialects: ["CommonMark", "GFM"],
1160
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-duplicate-headings.md"
1161
+ },
1162
+ messages: { duplicateHeading: "Duplicate heading \"{{text}}\" found." },
1163
+ schema: [{
1164
+ type: "object",
1165
+ properties: { checkSiblingsOnly: { type: "boolean" } },
1166
+ additionalProperties: false
1167
+ }],
1168
+ defaultOptions: [{ checkSiblingsOnly: false }]
1169
+ },
1170
+ create(context) {
1171
+ const [{ checkSiblingsOnly }] = context.options;
1172
+ /** @type {Map<number, Set<string>>} */
1173
+ const headingsByLevel = checkSiblingsOnly ? /* @__PURE__ */ new Map([
1174
+ [1, /* @__PURE__ */ new Set()],
1175
+ [2, /* @__PURE__ */ new Set()],
1176
+ [3, /* @__PURE__ */ new Set()],
1177
+ [4, /* @__PURE__ */ new Set()],
1178
+ [5, /* @__PURE__ */ new Set()],
1179
+ [6, /* @__PURE__ */ new Set()]
1180
+ ]) : /* @__PURE__ */ new Map([[1, /* @__PURE__ */ new Set()]]);
1181
+ let lastLevel = 1;
1182
+ let currentLevelHeadings = headingsByLevel.get(lastLevel);
1183
+ /** @type {string} */
1184
+ let headingChildrenSequence;
1185
+ /** @type {string} */
1186
+ let headingText;
1187
+ return {
1188
+ heading(node) {
1189
+ headingChildrenSequence = "";
1190
+ headingText = "";
1191
+ if (checkSiblingsOnly) {
1192
+ const currentLevel = node.depth;
1193
+ if (currentLevel < lastLevel) for (let level = lastLevel; level > currentLevel; level--) headingsByLevel.get(level).clear();
1194
+ lastLevel = currentLevel;
1195
+ currentLevelHeadings = headingsByLevel.get(currentLevel);
1196
+ }
1197
+ },
1198
+ "heading *"({ type, value }) {
1199
+ if (value) {
1200
+ headingChildrenSequence += `[${type},${value}]`;
1201
+ if (type !== "html") headingText += value;
1202
+ } else headingChildrenSequence += `[${type}]`;
1203
+ },
1204
+ "heading:exit"(node) {
1205
+ if (currentLevelHeadings.has(headingChildrenSequence)) context.report({
1206
+ loc: node.position,
1207
+ messageId: "duplicateHeading",
1208
+ data: { text: headingText }
1209
+ });
1210
+ else currentLevelHeadings.add(headingChildrenSequence);
1211
+ }
1212
+ };
1213
+ }
1214
+ };
1215
+ // vendor/src/rules/no-empty-definitions.js
1216
+ /**
1217
+ * @fileoverview Rule to prevent empty definitions in Markdown.
1218
+ * @author Pixel998
1219
+ */
1220
+ /**
1221
+ * @import { MarkdownRuleDefinition } from "../types.js";
1222
+ * @typedef {"emptyDefinition" | "emptyFootnoteDefinition"} NoEmptyDefinitionsMessageIds
1223
+ * @typedef {[{ allowDefinitions?: string[], allowFootnoteDefinitions?: string[], checkFootnoteDefinitions?: boolean }]} NoEmptyDefinitionsOptions
1224
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoEmptyDefinitionsOptions, MessageIds: NoEmptyDefinitionsMessageIds }>} NoEmptyDefinitionsRuleDefinition
1225
+ */
1226
+ /**
1227
+ * Checks if a string contains only HTML comments.
1228
+ * @param {string} value The input string to check.
1229
+ * @returns {boolean} True if the string contains only HTML comments, false otherwise.
1230
+ */
1231
+ function isOnlyComments(value) {
1232
+ return value.replace(htmlCommentPattern, "").trim().length === 0;
1233
+ }
1234
+ var no_empty_definitions_default = {
1235
+ meta: {
1236
+ type: "problem",
1237
+ languages: ["markdown/commonmark", "markdown/gfm"],
1238
+ docs: {
1239
+ recommended: true,
1240
+ description: "Disallow empty definitions",
1241
+ dialects: ["CommonMark", "GFM"],
1242
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-empty-definitions.md"
1243
+ },
1244
+ messages: {
1245
+ emptyDefinition: "Unexpected empty definition `{{ identifier }}` (label: `{{ label }}`) found.",
1246
+ emptyFootnoteDefinition: "Unexpected empty footnote definition `{{ identifier }}` (label: `{{ label }}`) found."
1247
+ },
1248
+ schema: [{
1249
+ type: "object",
1250
+ properties: {
1251
+ allowDefinitions: {
1252
+ type: "array",
1253
+ items: { type: "string" },
1254
+ uniqueItems: true
1255
+ },
1256
+ allowFootnoteDefinitions: {
1257
+ type: "array",
1258
+ items: { type: "string" },
1259
+ uniqueItems: true
1260
+ },
1261
+ checkFootnoteDefinitions: { type: "boolean" }
1262
+ },
1263
+ additionalProperties: false
1264
+ }],
1265
+ defaultOptions: [{
1266
+ allowDefinitions: ["//"],
1267
+ allowFootnoteDefinitions: [],
1268
+ checkFootnoteDefinitions: true
1269
+ }]
1270
+ },
1271
+ create(context) {
1272
+ const allowDefinitions = new Set(context.options[0].allowDefinitions.map(normalizeIdentifier$1));
1273
+ const allowFootnoteDefinitions = new Set(context.options[0].allowFootnoteDefinitions.map(normalizeIdentifier$1));
1274
+ const [{ checkFootnoteDefinitions }] = context.options;
1275
+ return {
1276
+ definition(node) {
1277
+ if ((!node.url || node.url === "#") && !allowDefinitions.has(node.identifier)) context.report({
1278
+ loc: node.position,
1279
+ messageId: "emptyDefinition",
1280
+ data: {
1281
+ identifier: node.identifier,
1282
+ label: node.label.trim()
1283
+ }
1284
+ });
1285
+ },
1286
+ footnoteDefinition(node) {
1287
+ if (checkFootnoteDefinitions && !allowFootnoteDefinitions.has(node.identifier) && (node.children.length === 0 || node.children.every((child) => child.type === "html" && isOnlyComments(child.value)))) context.report({
1288
+ loc: node.position,
1289
+ messageId: "emptyFootnoteDefinition",
1290
+ data: {
1291
+ identifier: node.identifier,
1292
+ label: node.label
1293
+ }
1294
+ });
1295
+ }
1296
+ };
1297
+ }
1298
+ };
1299
+ // vendor/src/rules/no-empty-images.js
1300
+ /**
1301
+ * @fileoverview Rule to prevent empty images in Markdown.
1302
+ * @author 루밀LuMir(lumirlumir)
1303
+ */
1304
+ /**
1305
+ * @import { MarkdownRuleDefinition } from "../types.js";
1306
+ * @typedef {"emptyImage"} NoEmptyImagesMessageIds
1307
+ * @typedef {[]} NoEmptyImagesOptions
1308
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoEmptyImagesOptions, MessageIds: NoEmptyImagesMessageIds }>} NoEmptyImagesRuleDefinition
1309
+ */
1310
+ var no_empty_images_default = {
1311
+ meta: {
1312
+ type: "problem",
1313
+ languages: ["markdown/commonmark", "markdown/gfm"],
1314
+ docs: {
1315
+ recommended: true,
1316
+ description: "Disallow empty images",
1317
+ dialects: ["CommonMark", "GFM"],
1318
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-empty-images.md"
1319
+ },
1320
+ messages: { emptyImage: "Unexpected empty image found." }
1321
+ },
1322
+ create(context) {
1323
+ return { image(node) {
1324
+ if (!node.url || node.url === "#") context.report({
1325
+ loc: node.position,
1326
+ messageId: "emptyImage"
1327
+ });
1328
+ } };
1329
+ }
1330
+ };
1331
+ // vendor/src/rules/no-empty-links.js
1332
+ /**
1333
+ * @fileoverview Rule to prevent empty links in Markdown.
1334
+ * @author Nicholas C. Zakas
1335
+ */
1336
+ /**
1337
+ * @import { MarkdownRuleDefinition } from "../types.js";
1338
+ * @typedef {"emptyLink"} NoEmptyLinksMessageIds
1339
+ * @typedef {[]} NoEmptyLinksOptions
1340
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoEmptyLinksOptions, MessageIds: NoEmptyLinksMessageIds }>} NoEmptyLinksRuleDefinition
1341
+ */
1342
+ var no_empty_links_default = {
1343
+ meta: {
1344
+ type: "problem",
1345
+ languages: ["markdown/commonmark", "markdown/gfm"],
1346
+ docs: {
1347
+ recommended: true,
1348
+ description: "Disallow empty links",
1349
+ dialects: ["CommonMark", "GFM"],
1350
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-empty-links.md"
1351
+ },
1352
+ messages: { emptyLink: "Unexpected empty link found." }
1353
+ },
1354
+ create(context) {
1355
+ return { link(node) {
1356
+ if (!node.url || node.url === "#") context.report({
1357
+ loc: node.position,
1358
+ messageId: "emptyLink"
1359
+ });
1360
+ } };
1361
+ }
1362
+ };
1363
+ // vendor/src/rules/no-html.js
1364
+ /**
1365
+ * @fileoverview Rule to disallow HTML inside of content.
1366
+ * @author Nicholas C. Zakas
1367
+ */
1368
+ /**
1369
+ * @import { MarkdownRuleDefinition } from "../types.js";
1370
+ * @typedef {"disallowedElement"} NoHtmlMessageIds
1371
+ * @typedef {[{ allowed?: string[], allowedIgnoreCase?: boolean }]} NoHtmlOptions
1372
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoHtmlOptions, MessageIds: NoHtmlMessageIds }>} NoHtmlRuleDefinition
1373
+ */
1374
+ const htmlTagPattern$1 = /<(?<tagName>[a-z0-9]+(?:-[a-z0-9]+)*)(?:\s(?:[^>"']|"[^"]*"|'[^']*')*)?\/?>/giu;
1375
+ var no_html_default = {
1376
+ meta: {
1377
+ type: "problem",
1378
+ languages: ["markdown/commonmark", "markdown/gfm"],
1379
+ docs: {
1380
+ description: "Disallow HTML tags",
1381
+ dialects: ["CommonMark", "GFM"],
1382
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-html.md"
1383
+ },
1384
+ messages: { disallowedElement: "HTML element \"{{name}}\" is not allowed." },
1385
+ schema: [{
1386
+ type: "object",
1387
+ properties: {
1388
+ allowed: {
1389
+ type: "array",
1390
+ items: { type: "string" },
1391
+ uniqueItems: true
1392
+ },
1393
+ allowedIgnoreCase: { type: "boolean" }
1394
+ },
1395
+ additionalProperties: false
1396
+ }],
1397
+ defaultOptions: [{
1398
+ allowed: [],
1399
+ allowedIgnoreCase: false
1400
+ }]
1401
+ },
1402
+ create(context) {
1403
+ const { sourceCode } = context;
1404
+ const [{ allowed, allowedIgnoreCase }] = context.options;
1405
+ /**
1406
+ * Normalize a tag name based on the `allowedIgnoreCase` option.
1407
+ * @param {string} tagName The tag name to normalize.
1408
+ * @returns {string} The normalized tag name.
1409
+ */
1410
+ function normalizeTagName(tagName) {
1411
+ return allowedIgnoreCase ? tagName.toLowerCase() : tagName;
1412
+ }
1413
+ const allowedElements = new Set(allowed.map(normalizeTagName));
1414
+ return { html(node) {
1415
+ const text = stripHtmlComments(sourceCode.getText(node));
1416
+ /** @type {RegExpExecArray | null} */
1417
+ let match;
1418
+ while ((match = htmlTagPattern$1.exec(text)) !== null) {
1419
+ const fullMatch = match[0];
1420
+ const { tagName } = match.groups;
1421
+ const firstNewlineIndex = fullMatch.search(lineEndingPattern);
1422
+ const startOffset = match.index + node.position.start.offset;
1423
+ const endOffset = startOffset + (firstNewlineIndex === -1 ? fullMatch.length : firstNewlineIndex);
1424
+ if (!allowedElements.has(normalizeTagName(tagName))) context.report({
1425
+ loc: {
1426
+ start: sourceCode.getLocFromIndex(startOffset),
1427
+ end: sourceCode.getLocFromIndex(endOffset)
1428
+ },
1429
+ messageId: "disallowedElement",
1430
+ data: { name: tagName }
1431
+ });
1432
+ }
1433
+ } };
1434
+ }
1435
+ };
1436
+ // vendor/src/rules/no-invalid-label-refs.js
1437
+ /**
1438
+ * @fileoverview Rule to prevent non-complaint link references.
1439
+ * @author Nicholas C. Zakas
1440
+ */
1441
+ /**
1442
+ * @import { Position } from "unist";
1443
+ * @import { Text } from "mdast";
1444
+ * @import { MarkdownRuleDefinition } from "../types.js";
1445
+ * @import { MarkdownSourceCode } from "../language/markdown-source-code.js";
1446
+ * @typedef {"invalidLabelRef"} NoInvalidLabelRefsMessageIds
1447
+ * @typedef {[]} NoInvalidLabelRefsOptions
1448
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoInvalidLabelRefsOptions, MessageIds: NoInvalidLabelRefsMessageIds }>} NoInvalidLabelRefsRuleDefinition
1449
+ */
1450
+ /** matches i.e., `[foo][bar]` */
1451
+ const labelPattern = /\]\[([^\]]+)\]/u;
1452
+ /**
1453
+ * Finds missing references in a node.
1454
+ * @param {Text} node The node to check.
1455
+ * @param {MarkdownSourceCode} sourceCode The Markdown source code object.
1456
+ * @returns {Array<{label:string,position:Position}>} The missing references.
1457
+ */
1458
+ function findInvalidLabelReferences(node, sourceCode) {
1459
+ const nodeText = sourceCode.getText(node);
1460
+ const docText = sourceCode.text;
1461
+ const invalid = [];
1462
+ let startIndex = 0;
1463
+ while (startIndex < nodeText.length) {
1464
+ const match = nodeText.slice(startIndex).match(labelPattern);
1465
+ if (!match) break;
1466
+ if (!illegalShorthandTailPattern.test(match[0])) {
1467
+ startIndex += match.index + match[0].length;
1468
+ continue;
1469
+ }
1470
+ const startOffset = startIndex + match.index + node.position.start.offset;
1471
+ const endOffset = startOffset + match[0].length;
1472
+ const lastOpenBracketIndex = docText.lastIndexOf("[", startOffset);
1473
+ if (lastOpenBracketIndex === -1) {
1474
+ startIndex += match.index + match[0].length;
1475
+ continue;
1476
+ }
1477
+ const label = docText.slice(lastOpenBracketIndex, endOffset).match(/!?\[([^\]]+)\]/u)[1];
1478
+ invalid.push({
1479
+ label: label.trim(),
1480
+ position: {
1481
+ start: sourceCode.getLocFromIndex(startOffset + 1),
1482
+ end: sourceCode.getLocFromIndex(endOffset)
1483
+ }
1484
+ });
1485
+ startIndex += match.index + match[0].length;
1486
+ }
1487
+ return invalid;
1488
+ }
1489
+ var no_invalid_label_refs_default = {
1490
+ meta: {
1491
+ type: "problem",
1492
+ languages: ["markdown/commonmark", "markdown/gfm"],
1493
+ docs: {
1494
+ recommended: true,
1495
+ description: "Disallow invalid label references",
1496
+ dialects: ["CommonMark", "GFM"],
1497
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-invalid-label-refs.md"
1498
+ },
1499
+ messages: { invalidLabelRef: "Label reference '{{label}}' is invalid due to white space between [ and ]." }
1500
+ },
1501
+ create(context) {
1502
+ const { sourceCode } = context;
1503
+ return { text(node) {
1504
+ const invalidReferences = findInvalidLabelReferences(node, sourceCode);
1505
+ for (const invalidReference of invalidReferences) context.report({
1506
+ loc: invalidReference.position,
1507
+ messageId: "invalidLabelRef",
1508
+ data: { label: invalidReference.label }
1509
+ });
1510
+ } };
1511
+ }
1512
+ };
1513
+ // vendor/src/rules/no-missing-atx-heading-space.js
1514
+ /**
1515
+ * @fileoverview Rule to ensure there is a space after hash on ATX style headings in Markdown.
1516
+ * @author Sweta Tanwar (@SwetaTanwar)
1517
+ */
1518
+ /**
1519
+ * @import { MarkdownRuleDefinition } from "../types.js";
1520
+ * @typedef {"missingSpace"} NoMissingAtxHeadingSpaceMessageIds
1521
+ * @typedef {[{ checkClosedHeadings?: boolean }]} NoMissingAtxHeadingSpaceOptions
1522
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoMissingAtxHeadingSpaceOptions, MessageIds: NoMissingAtxHeadingSpaceMessageIds }>} NoMissingAtxHeadingSpaceRuleDefinition
1523
+ */
1524
+ const leadingAtxHeadingHashPattern = /(?:^|(?<=[\r\n]))(?<hashes>#{1,6})(?:[^# \t]|$)/gu;
1525
+ const trailingAtxHeadingHashPattern = /(?<![ \t])(?<spaces>[ \t]*)(?<=(?<!\\)(?:\\{2})*)(?<hashes>#+)[ \t]*$/u;
1526
+ var no_missing_atx_heading_space_default = {
1527
+ meta: {
1528
+ type: "problem",
1529
+ languages: ["markdown/commonmark", "markdown/gfm"],
1530
+ docs: {
1531
+ recommended: true,
1532
+ description: "Disallow headings without a space after the hash characters",
1533
+ dialects: ["CommonMark", "GFM"],
1534
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-missing-atx-heading-space.md"
1535
+ },
1536
+ fixable: "whitespace",
1537
+ messages: { missingSpace: "Missing space {{position}} hash(es) on ATX style heading." },
1538
+ schema: [{
1539
+ type: "object",
1540
+ properties: { checkClosedHeadings: { type: "boolean" } },
1541
+ additionalProperties: false
1542
+ }],
1543
+ defaultOptions: [{ checkClosedHeadings: false }]
1544
+ },
1545
+ create(context) {
1546
+ const { sourceCode } = context;
1547
+ const [{ checkClosedHeadings }] = context.options;
1548
+ return {
1549
+ heading(node) {
1550
+ if (!checkClosedHeadings) return;
1551
+ const text = sourceCode.getText(node);
1552
+ const match = trailingAtxHeadingHashPattern.exec(text);
1553
+ if (match === null || match.index === 0) return;
1554
+ const { spaces, hashes } = match.groups;
1555
+ if (spaces.length > 0) return;
1556
+ const startOffset = node.position.start.offset + match.index;
1557
+ const endOffset = startOffset + hashes.length;
1558
+ context.report({
1559
+ loc: {
1560
+ start: sourceCode.getLocFromIndex(startOffset - 1),
1561
+ end: sourceCode.getLocFromIndex(endOffset)
1562
+ },
1563
+ messageId: "missingSpace",
1564
+ data: { position: "before" },
1565
+ fix(fixer) {
1566
+ return fixer.insertTextBeforeRange([startOffset, startOffset + 1], " ");
1567
+ }
1568
+ });
1569
+ },
1570
+ paragraph(node) {
1571
+ const text = sourceCode.getText(node);
1572
+ /** @type {RegExpExecArray | null} */
1573
+ let match;
1574
+ while ((match = leadingAtxHeadingHashPattern.exec(text)) !== null) {
1575
+ const { hashes } = match.groups;
1576
+ const startOffset = node.position.start.offset + match.index;
1577
+ const endOffset = startOffset + hashes.length;
1578
+ context.report({
1579
+ loc: {
1580
+ start: sourceCode.getLocFromIndex(startOffset),
1581
+ end: sourceCode.getLocFromIndex(endOffset + 1)
1582
+ },
1583
+ messageId: "missingSpace",
1584
+ data: { position: "after" },
1585
+ fix(fixer) {
1586
+ return fixer.insertTextAfterRange([endOffset - 1, endOffset], " ");
1587
+ }
1588
+ });
1589
+ }
1590
+ }
1591
+ };
1592
+ }
1593
+ };
1594
+ // vendor/src/rules/no-missing-label-refs.js
1595
+ /**
1596
+ * @fileoverview Rule to prevent missing label references in Markdown.
1597
+ * @author Nicholas C. Zakas
1598
+ */
1599
+ /**
1600
+ * @import { Position } from "unist";
1601
+ * @import { Text } from "mdast";
1602
+ * @import { MarkdownRuleDefinition } from "../types.js";
1603
+ * @import { MarkdownSourceCode } from "../language/markdown-source-code.js";
1604
+ * @typedef {"notFound"} NoMissingLabelRefsMessageIds
1605
+ * @typedef {[{ allowLabels?: string[] }]} NoMissingLabelRefsOptions
1606
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoMissingLabelRefsOptions, MessageIds: NoMissingLabelRefsMessageIds }>} NoMissingLabelRefsRuleDefinition
1607
+ */
1608
+ /**
1609
+ * Finds missing references in a node.
1610
+ * @param {Text} node The node to check.
1611
+ * @param {MarkdownSourceCode} sourceCode The Markdown source code object.
1612
+ * @returns {Array<{label:string,position:Position}>} The missing references.
1613
+ */
1614
+ function findMissingReferences(node, sourceCode) {
1615
+ /** @type {Array<{label:string,position:Position}>} */
1616
+ const missing = [];
1617
+ const nodeText = sourceCode.getText(node);
1618
+ /**
1619
+ * Matches substrings like `"[foo]"`, `"[]"`, `"[foo][bar]"`, `"[foo][]"`, `"[][bar]"`, or `"[][]"`.
1620
+ * `left` is the content between the first brackets. It can be empty.
1621
+ * `right` is the content between the second brackets. It can be empty, and it can be undefined.
1622
+ */
1623
+ const labelPattern = /(?<=(?<!\\)(?:\\{2})*)\[(?<left>(?:\\.|[^[\]\\])*)\](?:\[(?<right>(?:\\.|[^\]\\])*)\])?/dgu;
1624
+ /** @type {RegExpExecArray | null} */
1625
+ let match;
1626
+ while (match = labelPattern.exec(nodeText)) {
1627
+ if (illegalShorthandTailPattern.test(match[0])) continue;
1628
+ const { left, right } = match.groups;
1629
+ if (!left && !right) continue;
1630
+ let label, labelIndices;
1631
+ if (right) {
1632
+ label = right;
1633
+ labelIndices = match.indices.groups.right;
1634
+ } else {
1635
+ label = left;
1636
+ labelIndices = match.indices.groups.left;
1637
+ }
1638
+ const startOffset = labelIndices[0] + node.position.start.offset;
1639
+ const endOffset = labelIndices[1] + node.position.start.offset;
1640
+ missing.push({
1641
+ label: label.trim(),
1642
+ position: {
1643
+ start: sourceCode.getLocFromIndex(startOffset),
1644
+ end: sourceCode.getLocFromIndex(endOffset)
1645
+ }
1646
+ });
1647
+ }
1648
+ return missing;
1649
+ }
1650
+ var no_missing_label_refs_default = {
1651
+ meta: {
1652
+ type: "problem",
1653
+ languages: ["markdown/commonmark", "markdown/gfm"],
1654
+ docs: {
1655
+ recommended: true,
1656
+ description: "Disallow missing label references",
1657
+ dialects: ["CommonMark", "GFM"],
1658
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-missing-label-refs.md"
1659
+ },
1660
+ schema: [{
1661
+ type: "object",
1662
+ properties: { allowLabels: {
1663
+ type: "array",
1664
+ items: { type: "string" },
1665
+ uniqueItems: true
1666
+ } },
1667
+ additionalProperties: false
1668
+ }],
1669
+ defaultOptions: [{ allowLabels: [] }],
1670
+ messages: { notFound: "Label reference '{{label}}' not found." }
1671
+ },
1672
+ create(context) {
1673
+ const { sourceCode } = context;
1674
+ const allowLabels = new Set(context.options[0].allowLabels);
1675
+ /** @type {Array<{label:string,position:Position}>} */
1676
+ let allMissingReferences = [];
1677
+ return {
1678
+ "root:exit"() {
1679
+ for (const missingReference of allMissingReferences) context.report({
1680
+ loc: missingReference.position,
1681
+ messageId: "notFound",
1682
+ data: { label: missingReference.label }
1683
+ });
1684
+ },
1685
+ text(node) {
1686
+ const missingReferences = findMissingReferences(node, sourceCode);
1687
+ for (const missingReference of missingReferences) if (!allowLabels.has(missingReference.label)) allMissingReferences.push(missingReference);
1688
+ },
1689
+ definition(node) {
1690
+ allMissingReferences = allMissingReferences.filter((missingReference) => missingReference.label !== node.identifier);
1691
+ }
1692
+ };
1693
+ }
1694
+ };
1695
+ // vendor/src/rules/no-missing-link-fragments.js
1696
+ /**
1697
+ * @fileoverview Rule to ensure link fragments (URLs that start with #) reference valid headings
1698
+ * @author Sweta Tanwar (@SwetaTanwar)
1699
+ */
1700
+ /**
1701
+ * @import { Definition, Link } from "mdast";
1702
+ * @import { MarkdownRuleDefinition } from "../types.js";
1703
+ * @typedef {"invalidFragment"} NoMissingLinkFragmentsMessageIds
1704
+ * @typedef {[{ ignoreCase?: boolean; allowPattern?: string }]} NoMissingLinkFragmentsOptions
1705
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoMissingLinkFragmentsOptions, MessageIds: NoMissingLinkFragmentsMessageIds }>} NoMissingLinkFragmentsRuleDefinition
1706
+ */
1707
+ const githubLineReferencePattern = /^L\d+(?:C\d+)?(?:-L\d+(?:C\d+)?)?$/u;
1708
+ const customHeadingIdPattern = /\{#(?<id>[^}\s]+)\}\s*$/u;
1709
+ const htmlHeadingPattern = /<h(?<depth>[1-6])[^>]*>(?<children>[\s\S]*?)<\/h\k<depth>\s*>/giu;
1710
+ const htmlIdNamePattern = /(?<!<)<[^>]+\s(?:id|name)\s*=\s*["']?(?<id>[^"'\s>]+)["']?/giu;
1711
+ const htmlTagPattern = /<\/?[a-z0-9]+(?:-[a-z0-9]+)*(?:\s(?:[^>"']|"[^"]*"|'[^']*')*)?(?:\/\s*)?>/giu;
1712
+ var no_missing_link_fragments_default = {
1713
+ meta: {
1714
+ type: "problem",
1715
+ languages: ["markdown/commonmark", "markdown/gfm"],
1716
+ docs: {
1717
+ recommended: true,
1718
+ description: "Disallow link fragments that do not reference valid headings",
1719
+ dialects: ["CommonMark", "GFM"],
1720
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-missing-link-fragments.md"
1721
+ },
1722
+ schema: [{
1723
+ type: "object",
1724
+ properties: {
1725
+ ignoreCase: { type: "boolean" },
1726
+ allowPattern: { type: "string" }
1727
+ },
1728
+ additionalProperties: false
1729
+ }],
1730
+ messages: { invalidFragment: "Link fragment '#{{fragment}}' does not reference a heading or anchor in this document." },
1731
+ defaultOptions: [{
1732
+ ignoreCase: true,
1733
+ allowPattern: ""
1734
+ }]
1735
+ },
1736
+ create(context) {
1737
+ const [{ allowPattern, ignoreCase }] = context.options;
1738
+ const allowPatternOrNull = allowPattern ? new RegExp(allowPattern, "u") : null;
1739
+ const fragmentIds = /* @__PURE__ */ new Set(["top"]);
1740
+ const slugger = new GithubSlugger();
1741
+ /** @type {Array<Definition | Link>} */
1742
+ const relevantNodes = [];
1743
+ /** @type {string} */
1744
+ let headingText;
1745
+ return {
1746
+ heading() {
1747
+ headingText = "";
1748
+ },
1749
+ "heading *:not(html)"({ value }) {
1750
+ headingText += value ?? "";
1751
+ },
1752
+ "heading:exit"() {
1753
+ const customIdMatch = headingText.match(customHeadingIdPattern);
1754
+ const id = customIdMatch ? customIdMatch.groups.id : headingText;
1755
+ fragmentIds.add(slugger.slug(id));
1756
+ },
1757
+ html(node) {
1758
+ const htmlTextWithoutComments = stripHtmlComments(node.value);
1759
+ for (const match of htmlTextWithoutComments.matchAll(htmlIdNamePattern)) {
1760
+ const { id } = match.groups;
1761
+ fragmentIds.add(slugger.slug(id));
1762
+ }
1763
+ for (const match of htmlTextWithoutComments.matchAll(htmlHeadingPattern)) {
1764
+ const { children } = match.groups;
1765
+ const id = children.replace(htmlTagPattern, "");
1766
+ fragmentIds.add(slugger.slug(id));
1767
+ }
1768
+ },
1769
+ "definition, link"(node) {
1770
+ const { url } = node;
1771
+ if (url === "" || url === "#" || !url.startsWith("#")) return;
1772
+ relevantNodes.push(node);
1773
+ },
1774
+ "root:exit"() {
1775
+ for (const node of relevantNodes) {
1776
+ const fragment = node.url.slice(1);
1777
+ let decodedFragment = fragment;
1778
+ try {
1779
+ decodedFragment = decodeURIComponent(fragment);
1780
+ } catch {}
1781
+ if (allowPatternOrNull?.test(decodedFragment) || githubLineReferencePattern.test(decodedFragment)) continue;
1782
+ const normalizedFragment = ignoreCase ? decodedFragment.toLowerCase() : decodedFragment;
1783
+ if (!fragmentIds.has(normalizedFragment)) context.report({
1784
+ loc: node.position,
1785
+ messageId: "invalidFragment",
1786
+ data: { fragment }
1787
+ });
1788
+ }
1789
+ }
1790
+ };
1791
+ }
1792
+ };
1793
+ // vendor/src/rules/no-multiple-h1.js
1794
+ /**
1795
+ * @fileoverview Rule to enforce at most one H1 heading in Markdown.
1796
+ * @author Pixel998
1797
+ */
1798
+ /**
1799
+ * @import { Yaml } from "mdast";
1800
+ * @import { MarkdownRuleDefinition, Toml, Json } from "../types.js";
1801
+ * @typedef {"multipleH1"} NoMultipleH1MessageIds
1802
+ * @typedef {[{ frontmatterTitle?: string }]} NoMultipleH1Options
1803
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoMultipleH1Options, MessageIds: NoMultipleH1MessageIds }>} NoMultipleH1RuleDefinition
1804
+ */
1805
+ const h1TagPattern = /<h1[^>]*>[\s\S]*?<\/h1\s*>/giu;
1806
+ var no_multiple_h1_default = {
1807
+ meta: {
1808
+ type: "problem",
1809
+ languages: ["markdown/commonmark", "markdown/gfm"],
1810
+ docs: {
1811
+ recommended: true,
1812
+ description: "Disallow multiple H1 headings in the same document",
1813
+ dialects: ["CommonMark", "GFM"],
1814
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-multiple-h1.md"
1815
+ },
1816
+ messages: { multipleH1: "Unexpected additional H1 heading found." },
1817
+ schema: [{
1818
+ type: "object",
1819
+ properties: { frontmatterTitle: { type: "string" } },
1820
+ additionalProperties: false
1821
+ }],
1822
+ defaultOptions: [{ frontmatterTitle: "^(?!\\s*['\"]title[:=]['\"])\\s*\\{?\\s*['\"]?title['\"]?\\s*[:=]" }]
1823
+ },
1824
+ create(context) {
1825
+ const { sourceCode } = context;
1826
+ const [{ frontmatterTitle }] = context.options;
1827
+ const titlePattern = frontmatterTitle === "" ? null : new RegExp(frontmatterTitle, "iu");
1828
+ let h1Count = 0;
1829
+ return {
1830
+ "yaml, toml, json"(node) {
1831
+ if (frontmatterHasTitle(node.value, titlePattern)) h1Count++;
1832
+ },
1833
+ html(node) {
1834
+ const text = stripHtmlComments(node.value);
1835
+ /** @type {RegExpExecArray | null} */
1836
+ let match;
1837
+ while ((match = h1TagPattern.exec(text)) !== null) {
1838
+ h1Count++;
1839
+ if (h1Count > 1) {
1840
+ const startOffset = match.index + node.position.start.offset;
1841
+ const endOffset = startOffset + match[0].length;
1842
+ context.report({
1843
+ loc: {
1844
+ start: sourceCode.getLocFromIndex(startOffset),
1845
+ end: sourceCode.getLocFromIndex(endOffset)
1846
+ },
1847
+ messageId: "multipleH1"
1848
+ });
1849
+ }
1850
+ }
1851
+ },
1852
+ heading(node) {
1853
+ if (node.depth === 1) {
1854
+ h1Count++;
1855
+ if (h1Count > 1) context.report({
1856
+ loc: node.position,
1857
+ messageId: "multipleH1"
1858
+ });
1859
+ }
1860
+ }
1861
+ };
1862
+ }
1863
+ };
1864
+ // vendor/src/rules/no-reference-like-urls.js
1865
+ /**
1866
+ * @fileoverview Rule to enforce reference-style links when URL matches a defined identifier.
1867
+ * @author TKDev7
1868
+ */
1869
+ /**
1870
+ * @import { Image, Link } from "mdast";
1871
+ * @import { MarkdownRuleDefinition } from "../types.js";
1872
+ * @typedef {"referenceLikeUrl"} NoReferenceLikeUrlsMessageIds
1873
+ * @typedef {[]} NoReferenceLikeUrlsOptions
1874
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoReferenceLikeUrlsOptions, MessageIds: NoReferenceLikeUrlsMessageIds }>} NoReferenceLikeUrlsRuleDefinition
1875
+ */
1876
+ /**
1877
+ * Pattern to match both inline links: `[text](url)` and images: `![alt](url)`, with optional title.
1878
+ * The nested group is `\((?:\\[\s\S]|[^()\\])*\)`, not `\([\s\S]*\)`, so that it cannot overlap the
1879
+ * other alternatives. Overlapping alternatives cause catastrophic backtracking.
1880
+ */
1881
+ const linkOrImagePattern = /^!?\[(?<label>(?:\\.|[^()\\]|\((?:\\[\s\S]|[^()\\])*\))*?)\]\((?<destination>[ \t]*\r?\n?(?<![ \t])[ \t]*(?:<[^>]*>|[^ \t()]+))(?:[ \t]*\r?\n?(?<![ \t])[ \t]*(?:"[^"]*"|'[^']*'|\([^)]*\)))?[ \t]*\r?\n?(?<![ \t])[ \t]*\)$/u;
1882
+ var no_reference_like_urls_default = {
1883
+ meta: {
1884
+ type: "problem",
1885
+ languages: ["markdown/commonmark", "markdown/gfm"],
1886
+ docs: {
1887
+ recommended: true,
1888
+ description: "Disallow URLs that match defined reference identifiers",
1889
+ dialects: ["CommonMark", "GFM"],
1890
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-reference-like-urls.md"
1891
+ },
1892
+ fixable: "code",
1893
+ messages: { referenceLikeUrl: "Unexpected resource {{type}} ('{{prefix}}[text](url)') with URL that matches a definition identifier. Use '[text][id]' syntax instead." }
1894
+ },
1895
+ create(context) {
1896
+ const { sourceCode } = context;
1897
+ /** @type {Set<string>} */
1898
+ const definitionIdentifiers = /* @__PURE__ */ new Set();
1899
+ /** @type {Array<Image | Link>} */
1900
+ const relevantNodes = [];
1901
+ return {
1902
+ definition(node) {
1903
+ definitionIdentifiers.add(node.identifier);
1904
+ },
1905
+ "image, link"(node) {
1906
+ relevantNodes.push(node);
1907
+ },
1908
+ "root:exit"() {
1909
+ for (const node of relevantNodes) {
1910
+ const text = sourceCode.getText(node);
1911
+ const match = linkOrImagePattern.exec(text);
1912
+ if (match !== null) {
1913
+ const { label, destination } = match.groups;
1914
+ const { type, title } = node;
1915
+ const prefix = type === "image" ? "!" : "";
1916
+ const url = normalizeIdentifier$1(destination);
1917
+ if (definitionIdentifiers.has(url)) context.report({
1918
+ loc: node.position,
1919
+ messageId: "referenceLikeUrl",
1920
+ data: {
1921
+ type,
1922
+ prefix
1923
+ },
1924
+ fix(fixer) {
1925
+ if (title) return null;
1926
+ return fixer.replaceText(node, `${prefix}[${label}][${destination}]`);
1927
+ }
1928
+ });
1929
+ }
1930
+ }
1931
+ }
1932
+ };
1933
+ }
1934
+ };
1935
+ // vendor/src/rules/no-reversed-media-syntax.js
1936
+ /**
1937
+ * @fileoverview Rule to prevent reversed link and image syntax in Markdown.
1938
+ * @author xbinaryx
1939
+ */
1940
+ /**
1941
+ * @import { Heading, Paragraph, TableCell, FootnoteReference, Html, Image, ImageReference, InlineCode, LinkReference } from "mdast";
1942
+ * @import { InlineMath } from "satorigear";
1943
+ * @import { MarkdownRuleDefinition } from "../types.js";
1944
+ * @typedef {"reversedSyntax"} NoReversedMediaSyntaxMessageIds
1945
+ * @typedef {[]} NoReversedMediaSyntaxOptions
1946
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoReversedMediaSyntaxOptions, MessageIds: NoReversedMediaSyntaxMessageIds }>} NoReversedMediaSyntaxRuleDefinition
1947
+ */
1948
+ /**
1949
+ * Matches reversed link/image syntax like `(text)[url]`, ignoring escaped characters like `\(text\)[url]`.
1950
+ * The nested group is `\((?:\\[\s\S]|[^()\\])*\)`, not `\([\s\S]*\)`, so that it cannot overlap the
1951
+ * other alternatives. Overlapping alternatives cause catastrophic backtracking.
1952
+ */
1953
+ const reversedPattern = /(?<=(?<!\\)(?:\\{2})*)\((?<label>(?:\\.|[^()\\]|\((?:\\[\s\S]|[^()\\])*\))*)\)\[(?<url>(?:\\.|[^\]\\\r\n])*)\](?!\()/gu;
1954
+ var no_reversed_media_syntax_default = {
1955
+ meta: {
1956
+ type: "problem",
1957
+ languages: ["markdown/commonmark", "markdown/gfm"],
1958
+ docs: {
1959
+ recommended: true,
1960
+ description: "Disallow reversed link and image syntax",
1961
+ dialects: ["CommonMark", "GFM"],
1962
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-reversed-media-syntax.md"
1963
+ },
1964
+ fixable: "code",
1965
+ messages: { reversedSyntax: "Unexpected reversed syntax found. Use [label](URL) syntax instead." }
1966
+ },
1967
+ create(context) {
1968
+ const { sourceCode } = context;
1969
+ /** @type {string[]} */
1970
+ let buffer;
1971
+ /** @type {number} */
1972
+ let nodeStartOffset;
1973
+ return {
1974
+ "heading, paragraph, tableCell"(node) {
1975
+ buffer = sourceCode.getText(node).split("");
1976
+ nodeStartOffset = node.position.start.offset;
1977
+ },
1978
+ ":matches(heading, paragraph, tableCell) :matches(footnoteReference, html, image, imageReference, inlineCode, linkReference, inlineMath)"(node) {
1979
+ const [startOffset, endOffset] = sourceCode.getRange(node);
1980
+ for (let i = startOffset; i < endOffset; i++) buffer[i - nodeStartOffset] = " ";
1981
+ },
1982
+ ":matches(heading, paragraph, tableCell):exit"() {
1983
+ const maskedText = buffer.join("");
1984
+ /** @type {RegExpExecArray | null} */
1985
+ let match;
1986
+ while ((match = reversedPattern.exec(maskedText)) !== null) {
1987
+ const { label, url } = match.groups;
1988
+ const startOffset = match.index + nodeStartOffset;
1989
+ const endOffset = startOffset + match[0].length;
1990
+ const labelStartOffset = startOffset + 1;
1991
+ const labelEndOffset = labelStartOffset + label.length;
1992
+ const urlStartOffset = labelEndOffset + 2;
1993
+ const urlEndOffset = urlStartOffset + url.length;
1994
+ context.report({
1995
+ loc: {
1996
+ start: sourceCode.getLocFromIndex(startOffset),
1997
+ end: sourceCode.getLocFromIndex(endOffset)
1998
+ },
1999
+ messageId: "reversedSyntax",
2000
+ fix(fixer) {
2001
+ return fixer.replaceTextRange([startOffset, endOffset], `[${sourceCode.text.slice(labelStartOffset, labelEndOffset)}](${sourceCode.text.slice(urlStartOffset, urlEndOffset)})`);
2002
+ }
2003
+ });
2004
+ }
2005
+ }
2006
+ };
2007
+ }
2008
+ };
2009
+ // vendor/src/rules/no-space-in-emphasis.js
2010
+ /**
2011
+ * @fileoverview Rule to prevent spaces around emphasis markers in Markdown.
2012
+ * @author Pixel998
2013
+ */
2014
+ /**
2015
+ * @import { SourceRange } from "@eslint/core";
2016
+ * @import { Heading, Paragraph, TableCell, Text } from "mdast";
2017
+ * @import { MarkdownRuleDefinition } from "../types.js";
2018
+ * @typedef {"spaceInEmphasis"} NoSpaceInEmphasisMessageIds
2019
+ * @typedef {[{ checkStrikethrough?: boolean }]} NoSpaceInEmphasisOptions
2020
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoSpaceInEmphasisOptions, MessageIds: NoSpaceInEmphasisMessageIds }>} NoSpaceInEmphasisRuleDefinition
2021
+ */
2022
+ const whitespacePattern = /[ \t]/u;
2023
+ /**
2024
+ * Creates a marker pattern based on whether strikethrough should be included.
2025
+ * @param {boolean} checkStrikethrough Whether to include strikethrough markers.
2026
+ * @returns {RegExp} The marker pattern.
2027
+ */
2028
+ function createMarkerPattern(checkStrikethrough) {
2029
+ return checkStrikethrough ? /(?<=(?<!\\)(?:\\{2})*)(?:\*\*\*|\*\*|\*|___|__|_|~~|~)/gu : /(?<=(?<!\\)(?:\\{2})*)(?:\*\*\*|\*\*|\*|___|__|_)/gu;
2030
+ }
2031
+ var no_space_in_emphasis_default = {
2032
+ meta: {
2033
+ type: "problem",
2034
+ languages: ["markdown/commonmark", "markdown/gfm"],
2035
+ docs: {
2036
+ recommended: true,
2037
+ description: "Disallow spaces around emphasis markers",
2038
+ dialects: ["CommonMark", "GFM"],
2039
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-space-in-emphasis.md"
2040
+ },
2041
+ fixable: "whitespace",
2042
+ messages: { spaceInEmphasis: "Unexpected space around emphasis marker." },
2043
+ schema: [{
2044
+ type: "object",
2045
+ properties: { checkStrikethrough: { type: "boolean" } },
2046
+ additionalProperties: false
2047
+ }],
2048
+ defaultOptions: [{ checkStrikethrough: false }]
2049
+ },
2050
+ create(context) {
2051
+ const { sourceCode } = context;
2052
+ const [{ checkStrikethrough }] = context.options;
2053
+ const markerPattern = createMarkerPattern(checkStrikethrough);
2054
+ /** @type {string[]} */
2055
+ let buffer;
2056
+ /**
2057
+ * Reports a surrounding-space violation if present.
2058
+ * @param {number} checkIndex Character index to test for whitespace.
2059
+ * @param {number} highlightStartIndex Start index for highlighting.
2060
+ * @param {number} highlightEndIndex End index for highlighting.
2061
+ * @returns {void}
2062
+ */
2063
+ function reportWhitespace(checkIndex, highlightStartIndex, highlightEndIndex) {
2064
+ if (whitespacePattern.test(sourceCode.text[checkIndex])) context.report({
2065
+ loc: {
2066
+ start: sourceCode.getLocFromIndex(highlightStartIndex),
2067
+ end: sourceCode.getLocFromIndex(highlightEndIndex)
2068
+ },
2069
+ messageId: "spaceInEmphasis",
2070
+ fix(fixer) {
2071
+ return fixer.removeRange([checkIndex, checkIndex + 1]);
2072
+ }
2073
+ });
2074
+ }
2075
+ return {
2076
+ "heading, paragraph, tableCell"(node) {
2077
+ const [startOffset, endOffset] = sourceCode.getRange(node);
2078
+ buffer = new Array(endOffset - startOffset).fill(" ");
2079
+ },
2080
+ ":matches(heading, paragraph, tableCell) > text"(node) {
2081
+ const [startOffset, endOffset] = sourceCode.getRange(node);
2082
+ const parentNodeStartOffset = sourceCode.getParent(node).position.start.offset;
2083
+ for (let i = startOffset; i < endOffset; i++) buffer[i - parentNodeStartOffset] = sourceCode.text[i];
2084
+ },
2085
+ ":matches(heading, paragraph, tableCell):exit"(node) {
2086
+ const maskedText = buffer.join("");
2087
+ /** @type {Map<string, SourceRange[]>} */
2088
+ const markerGroups = /* @__PURE__ */ new Map();
2089
+ /** @type {RegExpExecArray | null} */
2090
+ let match;
2091
+ while ((match = markerPattern.exec(maskedText)) !== null) {
2092
+ const marker = match[0];
2093
+ const startOffset = match.index + node.position.start.offset;
2094
+ const endOffset = startOffset + marker.length;
2095
+ if (!markerGroups.has(marker)) markerGroups.set(marker, []);
2096
+ markerGroups.get(marker).push([startOffset, endOffset]);
2097
+ }
2098
+ for (const group of markerGroups.values()) for (let i = 0; i < group.length - 1; i += 2) {
2099
+ const startMarker = group[i];
2100
+ reportWhitespace(startMarker[1], startMarker[0], startMarker[1] + 2);
2101
+ const endMarker = group[i + 1];
2102
+ reportWhitespace(endMarker[0] - 1, endMarker[0] - 2, endMarker[1]);
2103
+ }
2104
+ }
2105
+ };
2106
+ }
2107
+ };
2108
+ // vendor/src/rules/no-unused-definitions.js
2109
+ /**
2110
+ * @fileoverview Rule to prevent unused definitions in Markdown.
2111
+ * @author 루밀LuMir(lumirlumir)
2112
+ */
2113
+ /**
2114
+ * @import { Definition, FootnoteDefinition } from "mdast";
2115
+ * @import { MarkdownRuleDefinition } from "../types.js";
2116
+ * @typedef {"unusedDefinition" | "unusedFootnoteDefinition"} NoUnusedDefinitionsMessageIds
2117
+ * @typedef {[{ allowDefinitions?: string[], allowFootnoteDefinitions?: string[], checkFootnoteDefinitions?: boolean }]} NoUnusedDefinitionsOptions
2118
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: NoUnusedDefinitionsOptions, MessageIds: NoUnusedDefinitionsMessageIds }>} NoUnusedDefinitionsRuleDefinition
2119
+ */
2120
+ var no_unused_definitions_default = {
2121
+ meta: {
2122
+ type: "problem",
2123
+ languages: ["markdown/commonmark", "markdown/gfm"],
2124
+ docs: {
2125
+ recommended: true,
2126
+ description: "Disallow unused definitions",
2127
+ dialects: ["CommonMark", "GFM"],
2128
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-unused-definitions.md"
2129
+ },
2130
+ messages: {
2131
+ unusedDefinition: "Unexpected unused definition `{{ identifier }}` (label: `{{ label }}`) found.",
2132
+ unusedFootnoteDefinition: "Unexpected unused footnote definition `{{ identifier }}` (label: `{{ label }}`) found."
2133
+ },
2134
+ schema: [{
2135
+ type: "object",
2136
+ properties: {
2137
+ allowDefinitions: {
2138
+ type: "array",
2139
+ items: { type: "string" },
2140
+ uniqueItems: true
2141
+ },
2142
+ allowFootnoteDefinitions: {
2143
+ type: "array",
2144
+ items: { type: "string" },
2145
+ uniqueItems: true
2146
+ },
2147
+ checkFootnoteDefinitions: { type: "boolean" }
2148
+ },
2149
+ additionalProperties: false
2150
+ }],
2151
+ defaultOptions: [{
2152
+ allowDefinitions: ["//"],
2153
+ allowFootnoteDefinitions: [],
2154
+ checkFootnoteDefinitions: true
2155
+ }]
2156
+ },
2157
+ create(context) {
2158
+ const allowDefinitions = new Set(context.options[0].allowDefinitions.map(normalizeIdentifier$1));
2159
+ const allowFootnoteDefinitions = new Set(context.options[0].allowFootnoteDefinitions.map(normalizeIdentifier$1));
2160
+ const [{ checkFootnoteDefinitions }] = context.options;
2161
+ /** @type {Set<string>} Set to track used identifiers */
2162
+ const usedIdentifiers = /* @__PURE__ */ new Set();
2163
+ /** @type {Set<string>} Set to track used footnote identifiers */
2164
+ const usedFootnoteIdentifiers = /* @__PURE__ */ new Set();
2165
+ /** @type {Set<Definition>} */
2166
+ const definitions = /* @__PURE__ */ new Set();
2167
+ /** @type {Set<FootnoteDefinition>} */
2168
+ const footnoteDefinitions = /* @__PURE__ */ new Set();
2169
+ return {
2170
+ imageReference(node) {
2171
+ usedIdentifiers.add(node.identifier);
2172
+ },
2173
+ linkReference(node) {
2174
+ usedIdentifiers.add(node.identifier);
2175
+ },
2176
+ footnoteReference(node) {
2177
+ usedFootnoteIdentifiers.add(node.identifier);
2178
+ },
2179
+ definition(node) {
2180
+ if (allowDefinitions.has(node.identifier)) return;
2181
+ definitions.add(node);
2182
+ },
2183
+ footnoteDefinition(node) {
2184
+ if (!checkFootnoteDefinitions || allowFootnoteDefinitions.has(node.identifier)) return;
2185
+ footnoteDefinitions.add(node);
2186
+ },
2187
+ "root:exit"() {
2188
+ for (const definition of definitions) if (!usedIdentifiers.has(definition.identifier)) context.report({
2189
+ node: definition,
2190
+ messageId: "unusedDefinition",
2191
+ data: {
2192
+ identifier: definition.identifier,
2193
+ label: definition.label.trim()
2194
+ }
2195
+ });
2196
+ for (const footnoteDefinition of footnoteDefinitions) if (!usedFootnoteIdentifiers.has(footnoteDefinition.identifier)) context.report({
2197
+ node: footnoteDefinition,
2198
+ messageId: "unusedFootnoteDefinition",
2199
+ data: {
2200
+ identifier: footnoteDefinition.identifier,
2201
+ label: footnoteDefinition.label
2202
+ }
2203
+ });
2204
+ }
2205
+ };
2206
+ }
2207
+ };
2208
+ // vendor/src/rules/require-alt-text.js
2209
+ /**
2210
+ * @fileoverview Rule to require alternative text for images in Markdown.
2211
+ * @author Pixel998
2212
+ */
2213
+ /**
2214
+ * @import { Image, ImageReference } from "mdast";
2215
+ * @import { MarkdownRuleDefinition } from "../types.js";
2216
+ * @typedef {"altTextRequired"} RequireAltTextMessageIds
2217
+ * @typedef {[]} RequireAltTextOptions
2218
+ * @typedef {MarkdownRuleDefinition<{ RuleOptions: RequireAltTextOptions, MessageIds: RequireAltTextMessageIds }>} RequireAltTextRuleDefinition
2219
+ */
2220
+ const imgTagPattern = /<img(?:\s(?:[^>"']|"[^"]*"|'[^']*')*)?\/?>/giu;
2221
+ const ariaHiddenTruePattern = /[\s"']aria-hidden\s*=\s*(?:"true"|'true'|true(?=\s|\/?>))/iu;
2222
+ /**
2223
+ * Creates a regex to match HTML attributes
2224
+ * @param {string} name The attribute name to match
2225
+ * @returns {RegExp} Regular expression for matching the attribute
2226
+ */
2227
+ function getHtmlAttributeRe(name) {
2228
+ return new RegExp(`\\s${name}(?:\\s*=\\s*['"]([^'"]*)['"])?`, "iu");
2229
+ }
2230
+ // vendor/src/build/rules.js
2231
+ var rules_default = {
2232
+ "fenced-code-language": fenced_code_language_default,
2233
+ "fenced-code-meta": fenced_code_meta_default,
2234
+ "heading-increment": heading_increment_default,
2235
+ "no-bare-urls": no_bare_urls_default,
2236
+ "no-duplicate-definitions": no_duplicate_definitions_default,
2237
+ "no-duplicate-headings": no_duplicate_headings_default,
2238
+ "no-empty-definitions": no_empty_definitions_default,
2239
+ "no-empty-images": no_empty_images_default,
2240
+ "no-empty-links": no_empty_links_default,
2241
+ "no-html": no_html_default,
2242
+ "no-invalid-label-refs": no_invalid_label_refs_default,
2243
+ "no-missing-atx-heading-space": no_missing_atx_heading_space_default,
2244
+ "no-missing-label-refs": no_missing_label_refs_default,
2245
+ "no-missing-link-fragments": no_missing_link_fragments_default,
2246
+ "no-multiple-h1": no_multiple_h1_default,
2247
+ "no-reference-like-urls": no_reference_like_urls_default,
2248
+ "no-reversed-media-syntax": no_reversed_media_syntax_default,
2249
+ "no-space-in-emphasis": no_space_in_emphasis_default,
2250
+ "no-unused-definitions": no_unused_definitions_default,
2251
+ "require-alt-text": {
2252
+ meta: {
2253
+ type: "problem",
2254
+ languages: ["markdown/commonmark", "markdown/gfm"],
2255
+ docs: {
2256
+ recommended: true,
2257
+ description: "Require alternative text for images",
2258
+ dialects: ["CommonMark", "GFM"],
2259
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/require-alt-text.md"
2260
+ },
2261
+ messages: { altTextRequired: "Alternative text for image is required." }
2262
+ },
2263
+ create(context) {
2264
+ const { sourceCode } = context;
2265
+ return {
2266
+ "image, imageReference"(node) {
2267
+ if (node.alt.trim().length === 0) context.report({
2268
+ loc: node.position,
2269
+ messageId: "altTextRequired"
2270
+ });
2271
+ },
2272
+ html(node) {
2273
+ const text = stripHtmlComments(sourceCode.getText(node));
2274
+ /** @type {RegExpExecArray | null} */
2275
+ let match;
2276
+ while ((match = imgTagPattern.exec(text)) !== null) {
2277
+ const imgTag = match[0];
2278
+ if (ariaHiddenTruePattern.test(imgTag)) continue;
2279
+ const altMatch = imgTag.match(getHtmlAttributeRe("alt"));
2280
+ if (!altMatch || altMatch[1] && altMatch[1].trim().length === 0 && altMatch[1].length > 0) {
2281
+ const startOffset = match.index + node.position.start.offset;
2282
+ const endOffset = startOffset + imgTag.length;
2283
+ context.report({
2284
+ loc: {
2285
+ start: sourceCode.getLocFromIndex(startOffset),
2286
+ end: sourceCode.getLocFromIndex(endOffset)
2287
+ },
2288
+ messageId: "altTextRequired"
2289
+ });
2290
+ }
2291
+ }
2292
+ }
2293
+ };
2294
+ }
2295
+ },
2296
+ "table-column-count": {
2297
+ meta: {
2298
+ type: "problem",
2299
+ languages: ["markdown/gfm"],
2300
+ docs: {
2301
+ description: "Disallow data rows in a GitHub Flavored Markdown table from having more cells than the header row",
2302
+ dialects: ["GFM"],
2303
+ url: "https://github.com/eslint/markdown/blob/main/docs/rules/table-column-count.md"
2304
+ },
2305
+ messages: {
2306
+ extraCells: "Table column count mismatch (Expected: {{expectedCells}}, Actual: {{actualCells}}), extra data starting here will be ignored.",
2307
+ missingCells: "Table column count mismatch (Expected: {{expectedCells}}, Actual: {{actualCells}}), row might be missing data."
2308
+ },
2309
+ schema: [{
2310
+ type: "object",
2311
+ properties: { checkMissingCells: { type: "boolean" } },
2312
+ additionalProperties: false
2313
+ }],
2314
+ defaultOptions: [{ checkMissingCells: false }]
2315
+ },
2316
+ create(context) {
2317
+ const [{ checkMissingCells }] = context.options;
2318
+ return { table(node) {
2319
+ if (node.children.length < 1) return;
2320
+ const expectedCellsLength = node.children[0].children.length;
2321
+ for (let i = 1; i < node.children.length; i++) {
2322
+ const currentRow = node.children[i];
2323
+ const actualCellsLength = currentRow.children.length;
2324
+ const lastActualCellNode = currentRow.children[actualCellsLength - 1];
2325
+ if (actualCellsLength > expectedCellsLength) {
2326
+ const firstExtraCellNode = currentRow.children[expectedCellsLength];
2327
+ context.report({
2328
+ loc: {
2329
+ start: firstExtraCellNode.position.start,
2330
+ end: lastActualCellNode.position.end
2331
+ },
2332
+ messageId: "extraCells",
2333
+ data: {
2334
+ actualCells: actualCellsLength,
2335
+ expectedCells: expectedCellsLength
2336
+ }
2337
+ });
2338
+ } else if (checkMissingCells && actualCellsLength < expectedCellsLength) context.report({
2339
+ loc: {
2340
+ start: {
2341
+ column: lastActualCellNode.position.end.column - 1,
2342
+ line: lastActualCellNode.position.end.line
2343
+ },
2344
+ end: currentRow.position.end
2345
+ },
2346
+ messageId: "missingCells",
2347
+ data: {
2348
+ actualCells: actualCellsLength,
2349
+ expectedCells: expectedCellsLength
2350
+ }
2351
+ });
2352
+ }
2353
+ } };
2354
+ }
2355
+ }
2356
+ };
2357
+ export { processor as a, MarkdownSourceCode as i, MarkdownLanguage as n, InlineConfigComment as r, rules_default as t };