markdownlint 0.35.0 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +72 -35
  3. package/demo/markdownlint-browser.js +1259 -1215
  4. package/doc/CustomRules.md +55 -249
  5. package/doc/Rules.md +36 -29
  6. package/doc/md003.md +2 -2
  7. package/doc/md004.md +4 -3
  8. package/doc/md035.md +3 -8
  9. package/doc/md046.md +2 -2
  10. package/doc/md048.md +3 -2
  11. package/doc/md049.md +4 -3
  12. package/doc/md050.md +4 -4
  13. package/doc/md051.md +11 -3
  14. package/doc/md055.md +3 -2
  15. package/helpers/README.md +2 -19
  16. package/helpers/helpers.js +87 -347
  17. package/helpers/micromark-helpers.cjs +296 -0
  18. package/helpers/micromark-parse.cjs +313 -0
  19. package/helpers/package.json +5 -2
  20. package/helpers/shared.js +6 -0
  21. package/helpers/test.cjs +28 -0
  22. package/lib/cache.js +3 -3
  23. package/lib/configuration-strict.d.ts +120 -106
  24. package/lib/constants.js +1 -1
  25. package/lib/markdownit.cjs +172 -0
  26. package/lib/markdownlint.d.ts +41 -3
  27. package/lib/markdownlint.js +153 -115
  28. package/lib/md001.js +1 -1
  29. package/lib/md003.js +1 -1
  30. package/lib/md004.js +3 -3
  31. package/lib/md007.js +3 -3
  32. package/lib/md009.js +1 -1
  33. package/lib/md010.js +18 -14
  34. package/lib/md011.js +24 -23
  35. package/lib/md012.js +1 -1
  36. package/lib/md013.js +1 -1
  37. package/lib/md014.js +5 -9
  38. package/lib/md018.js +1 -1
  39. package/lib/md019-md021.js +1 -1
  40. package/lib/md020.js +2 -2
  41. package/lib/md022.js +9 -15
  42. package/lib/md024.js +1 -1
  43. package/lib/md025.js +1 -1
  44. package/lib/md027.js +2 -2
  45. package/lib/md029.js +3 -3
  46. package/lib/md031.js +8 -6
  47. package/lib/md032.js +27 -15
  48. package/lib/md033.js +1 -1
  49. package/lib/md034.js +21 -29
  50. package/lib/md036.js +18 -24
  51. package/lib/md037.js +1 -1
  52. package/lib/md038.js +9 -14
  53. package/lib/md039.js +13 -15
  54. package/lib/md040.js +11 -13
  55. package/lib/md041.js +4 -4
  56. package/lib/md042.js +2 -6
  57. package/lib/md043.js +1 -1
  58. package/lib/md044.js +31 -28
  59. package/lib/md045.js +2 -2
  60. package/lib/md048.js +28 -17
  61. package/lib/md049-md050.js +20 -5
  62. package/lib/md051.js +16 -18
  63. package/lib/md053.js +5 -4
  64. package/lib/md054.js +11 -13
  65. package/lib/md055.js +39 -46
  66. package/lib/md056.js +31 -36
  67. package/lib/md058.js +29 -14
  68. package/package.json +14 -13
  69. package/schema/.markdownlint.jsonc +55 -52
  70. package/schema/.markdownlint.yaml +54 -52
  71. package/schema/markdownlint-config-schema-strict.json +132 -110
  72. package/schema/markdownlint-config-schema.json +132 -110
  73. package/helpers/micromark.cjs +0 -544
@@ -0,0 +1,296 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ const { flatTokensSymbol, htmlFlowSymbol } = require("./shared.js");
6
+
7
+ /** @typedef {import("markdownlint-micromark").TokenType} TokenType */
8
+ /** @typedef {import("../lib/markdownlint.js").MicromarkToken} Token */
9
+
10
+ /**
11
+ * Determines if a Micromark token is within an htmlFlow type.
12
+ *
13
+ * @param {Token} token Micromark token.
14
+ * @returns {boolean} True iff the token is within an htmlFlow type.
15
+ */
16
+ function inHtmlFlow(token) {
17
+ return Boolean(token[htmlFlowSymbol]);
18
+ }
19
+
20
+ /**
21
+ * Returns whether a token is an htmlFlow type containing an HTML comment.
22
+ *
23
+ * @param {Token} token Micromark token.
24
+ * @returns {boolean} True iff token is htmlFlow containing a comment.
25
+ */
26
+ function isHtmlFlowComment(token) {
27
+ const { text, type } = token;
28
+ if (
29
+ (type === "htmlFlow") &&
30
+ text.startsWith("<!--") &&
31
+ text.endsWith("-->")
32
+ ) {
33
+ const comment = text.slice(4, -3);
34
+ return (
35
+ !comment.startsWith(">") &&
36
+ !comment.startsWith("->") &&
37
+ !comment.endsWith("-")
38
+ // The following condition from the CommonMark specification is commented
39
+ // to avoid parsing HTML comments that include "--" because that is NOT a
40
+ // condition of the HTML specification.
41
+ // https://spec.commonmark.org/0.30/#raw-html
42
+ // https://html.spec.whatwg.org/multipage/syntax.html#comments
43
+ // && !comment.includes("--")
44
+ );
45
+ }
46
+ return false;
47
+ }
48
+
49
+ /**
50
+ * Adds a range of numbers to a set.
51
+ *
52
+ * @param {Set<number>} set Set of numbers.
53
+ * @param {number} start Starting number.
54
+ * @param {number} end Ending number.
55
+ * @returns {void}
56
+ */
57
+ function addRangeToSet(set, start, end) {
58
+ for (let i = start; i <= end; i++) {
59
+ set.add(i);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @callback AllowedPredicate
65
+ * @param {Token} token Micromark token.
66
+ * @returns {boolean} True iff allowed.
67
+ */
68
+
69
+ /**
70
+ * @callback TransformPredicate
71
+ * @param {Token} token Micromark token.
72
+ * @returns {Token[]} Child tokens.
73
+ */
74
+
75
+ /**
76
+ * Filter a list of Micromark tokens by predicate.
77
+ *
78
+ * @param {Token[]} tokens Micromark tokens.
79
+ * @param {AllowedPredicate} [allowed] Allowed token predicate.
80
+ * @param {TransformPredicate} [transformChildren] Transform predicate.
81
+ * @returns {Token[]} Filtered tokens.
82
+ */
83
+ function filterByPredicate(tokens, allowed, transformChildren) {
84
+ allowed = allowed || (() => true);
85
+ const result = [];
86
+ const queue = [
87
+ {
88
+ "array": tokens,
89
+ "index": 0
90
+ }
91
+ ];
92
+ while (queue.length > 0) {
93
+ const current = queue[queue.length - 1];
94
+ const { array, index } = current;
95
+ if (index < array.length) {
96
+ const token = array[current.index++];
97
+ if (allowed(token)) {
98
+ result.push(token);
99
+ }
100
+ const { children } = token;
101
+ if (children.length > 0) {
102
+ const transformed =
103
+ transformChildren ? transformChildren(token) : children;
104
+ queue.push(
105
+ {
106
+ "array": transformed,
107
+ "index": 0
108
+ }
109
+ );
110
+ }
111
+ } else {
112
+ queue.pop();
113
+ }
114
+ }
115
+ return result;
116
+ }
117
+
118
+ /**
119
+ * Filter a list of Micromark tokens by type.
120
+ *
121
+ * @param {Token[]} tokens Micromark tokens.
122
+ * @param {TokenType[]} types Types to allow.
123
+ * @param {boolean} [htmlFlow] Whether to include htmlFlow content.
124
+ * @returns {Token[]} Filtered tokens.
125
+ */
126
+ function filterByTypes(tokens, types, htmlFlow) {
127
+ const predicate = (token) => types.includes(token.type) && (htmlFlow || !inHtmlFlow(token));
128
+ const flatTokens = tokens[flatTokensSymbol];
129
+ if (flatTokens) {
130
+ return flatTokens.filter(predicate);
131
+ }
132
+ return filterByPredicate(tokens, predicate);
133
+ }
134
+
135
+ /**
136
+ * Gets the blockquote prefix text (if any) for the specified line number.
137
+ *
138
+ * @param {Token[]} tokens Micromark tokens.
139
+ * @param {number} lineNumber Line number to examine.
140
+ * @param {number} [count] Number of times to repeat.
141
+ * @returns {string} Blockquote prefix text.
142
+ */
143
+ function getBlockQuotePrefixText(tokens, lineNumber, count = 1) {
144
+ return filterByTypes(tokens, [ "blockQuotePrefix", "linePrefix" ])
145
+ .filter((prefix) => prefix.startLine === lineNumber)
146
+ .map((prefix) => prefix.text)
147
+ .join("")
148
+ .trimEnd()
149
+ // eslint-disable-next-line unicorn/prefer-spread
150
+ .concat("\n")
151
+ .repeat(count);
152
+ };
153
+
154
+ /**
155
+ * Gets a list of nested Micromark token descendants by type path.
156
+ *
157
+ * @param {Token|Token[]} parent Micromark token parent or parents.
158
+ * @param {(TokenType|TokenType[])[]} typePath Micromark token type path.
159
+ * @returns {Token[]} Micromark token descendants.
160
+ */
161
+ function getDescendantsByType(parent, typePath) {
162
+ let tokens = Array.isArray(parent) ? parent : [ parent ];
163
+ for (const type of typePath) {
164
+ const predicate = (token) => Array.isArray(type) ? type.includes(token.type) : (type === token.type);
165
+ tokens = tokens.flatMap((t) => t.children.filter(predicate));
166
+ }
167
+ return tokens;
168
+ }
169
+
170
+ /**
171
+ * Gets the heading level of a Micromark heading tokan.
172
+ *
173
+ * @param {Token} heading Micromark heading token.
174
+ * @returns {number} Heading level.
175
+ */
176
+ function getHeadingLevel(heading) {
177
+ let level = 1;
178
+ const headingSequence = heading.children.find(
179
+ (child) => [ "atxHeadingSequence", "setextHeadingLine" ].includes(child.type)
180
+ );
181
+ // @ts-ignore
182
+ const { text } = headingSequence;
183
+ if (text[0] === "#") {
184
+ level = Math.min(text.length, 6);
185
+ } else if (text[0] === "-") {
186
+ level = 2;
187
+ }
188
+ return level;
189
+ }
190
+
191
+ /**
192
+ * Gets the heading style of a Micromark heading tokan.
193
+ *
194
+ * @param {Token} heading Micromark heading token.
195
+ * @returns {"atx" | "atx_closed" | "setext"} Heading style.
196
+ */
197
+ function getHeadingStyle(heading) {
198
+ if (heading.type === "setextHeading") {
199
+ return "setext";
200
+ }
201
+ const atxHeadingSequenceLength = heading.children.filter(
202
+ (child) => child.type === "atxHeadingSequence"
203
+ ).length;
204
+ if (atxHeadingSequenceLength === 1) {
205
+ return "atx";
206
+ }
207
+ return "atx_closed";
208
+ }
209
+
210
+ /**
211
+ * Gets the heading text of a Micromark heading token.
212
+ *
213
+ * @param {Token} heading Micromark heading token.
214
+ * @returns {string} Heading text.
215
+ */
216
+ function getHeadingText(heading) {
217
+ const headingTexts = getDescendantsByType(heading, [ [ "atxHeadingText", "setextHeadingText" ] ]);
218
+ return headingTexts[0]?.text.replace(/[\r\n]+/g, " ") || "";
219
+ }
220
+
221
+ /**
222
+ * HTML tag information.
223
+ *
224
+ * @typedef {Object} HtmlTagInfo
225
+ * @property {boolean} close True iff close tag.
226
+ * @property {string} name Tag name.
227
+ */
228
+
229
+ /**
230
+ * Gets information about the tag in an HTML token.
231
+ *
232
+ * @param {Token} token Micromark token.
233
+ * @returns {HtmlTagInfo | null} HTML tag information.
234
+ */
235
+ function getHtmlTagInfo(token) {
236
+ const htmlTagNameRe = /^<([^!>][^/\s>]*)/;
237
+ if (token.type === "htmlText") {
238
+ const match = htmlTagNameRe.exec(token.text);
239
+ if (match) {
240
+ const name = match[1];
241
+ const close = name.startsWith("/");
242
+ return {
243
+ close,
244
+ "name": close ? name.slice(1) : name
245
+ };
246
+ }
247
+ }
248
+ return null;
249
+ }
250
+
251
+ /**
252
+ * Gets the nearest parent of the specified type for a Micromark token.
253
+ *
254
+ * @param {Token} token Micromark token.
255
+ * @param {TokenType[]} types Types to allow.
256
+ * @returns {Token | null} Parent token.
257
+ */
258
+ function getParentOfType(token, types) {
259
+ /** @type {Token | null} */
260
+ let current = token;
261
+ while ((current = current.parent) && !types.includes(current.type)) {
262
+ // Empty
263
+ }
264
+ return current;
265
+ }
266
+
267
+ /**
268
+ * Set containing token types that do not contain content.
269
+ *
270
+ * @type {Set<TokenType>}
271
+ */
272
+ const nonContentTokens = new Set([
273
+ "blockQuoteMarker",
274
+ "blockQuotePrefix",
275
+ "blockQuotePrefixWhitespace",
276
+ "lineEnding",
277
+ "lineEndingBlank",
278
+ "linePrefix",
279
+ "listItemIndent"
280
+ ]);
281
+
282
+ module.exports = {
283
+ addRangeToSet,
284
+ filterByPredicate,
285
+ filterByTypes,
286
+ getBlockQuotePrefixText,
287
+ getDescendantsByType,
288
+ getHeadingLevel,
289
+ getHeadingStyle,
290
+ getHeadingText,
291
+ getHtmlTagInfo,
292
+ getParentOfType,
293
+ inHtmlFlow,
294
+ isHtmlFlowComment,
295
+ nonContentTokens
296
+ };
@@ -0,0 +1,313 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ const micromark = require("markdownlint-micromark");
6
+ const { isHtmlFlowComment } = require("./micromark-helpers.cjs");
7
+ const { flatTokensSymbol, htmlFlowSymbol, newLineRe } = require("./shared.js");
8
+
9
+ /** @typedef {import("markdownlint-micromark").Construct} Construct */
10
+ /** @typedef {import("markdownlint-micromark").Event} Event */
11
+ /** @typedef {import("markdownlint-micromark").ParseOptions} MicromarkParseOptions */
12
+ /** @typedef {import("markdownlint-micromark").State} State */
13
+ /** @typedef {import("markdownlint-micromark").Token} Token */
14
+ /** @typedef {import("markdownlint-micromark").Tokenizer} Tokenizer */
15
+ /** @typedef {import("../lib/markdownlint.js").MicromarkToken} MicromarkToken */
16
+
17
+ /**
18
+ * Parse options.
19
+ *
20
+ * @typedef {Object} ParseOptions
21
+ * @property {boolean} [freezeTokens] Whether to freeze output Tokens.
22
+ */
23
+
24
+ /**
25
+ * Parses a Markdown document and returns Micromark events.
26
+ *
27
+ * @param {string} markdown Markdown document.
28
+ * @param {MicromarkParseOptions} [micromarkParseOptions] Options for micromark.
29
+ * @returns {Event[]} Micromark events.
30
+ */
31
+ function getEvents(
32
+ markdown,
33
+ micromarkParseOptions = {}
34
+ ) {
35
+ // Customize extensions list to add useful extensions
36
+ const extensions = [
37
+ micromark.directive(),
38
+ micromark.gfmAutolinkLiteral(),
39
+ micromark.gfmFootnote(),
40
+ micromark.gfmTable(),
41
+ micromark.math(),
42
+ ...(micromarkParseOptions.extensions || [])
43
+ ];
44
+
45
+ // // Shim labelEnd to identify undefined link labels
46
+ /** @type {Event[][]} */
47
+ const artificialEventLists = [];
48
+ /** @type {Construct} */
49
+ const labelEnd =
50
+ // @ts-ignore
51
+ micromark.labelEnd;
52
+ const tokenizeOriginal = labelEnd.tokenize;
53
+
54
+ /** @type {Tokenizer} */
55
+ function tokenizeShim(effects, okOriginal, nokOriginal) {
56
+ // eslint-disable-next-line consistent-this, unicorn/no-this-assignment, no-invalid-this
57
+ const tokenizeContext = this;
58
+ const events = tokenizeContext.events;
59
+
60
+ /** @type {State} */
61
+ const nokShim = (code) => {
62
+ // Find start of label (image or link)
63
+ let indexStart = events.length;
64
+ while (--indexStart >= 0) {
65
+ const event = events[indexStart];
66
+ const [ kind, token ] = event;
67
+ if (kind === "enter") {
68
+ const { type } = token;
69
+ if ((type === "labelImage") || (type === "labelLink")) {
70
+ // Found it
71
+ break;
72
+ }
73
+ }
74
+ }
75
+
76
+ // If found...
77
+ if (indexStart >= 0) {
78
+ // Create artificial enter/exit events and replicate all data/lineEnding events within
79
+ const eventStart = events[indexStart];
80
+ const [ , eventStartToken ] = eventStart;
81
+ const eventEnd = events[events.length - 1];
82
+ const [ , eventEndToken ] = eventEnd;
83
+ /** @type {Token} */
84
+ const undefinedReferenceType = {
85
+ "type": "undefinedReferenceShortcut",
86
+ "start": eventStartToken.start,
87
+ "end": eventEndToken.end
88
+ };
89
+ /** @type {Token} */
90
+ const undefinedReference = {
91
+ "type": "undefinedReference",
92
+ "start": eventStartToken.start,
93
+ "end": eventEndToken.end
94
+ };
95
+ const eventsToReplicate = events
96
+ .slice(indexStart)
97
+ .filter((event) => {
98
+ const [ , eventToken ] = event;
99
+ const { type } = eventToken;
100
+ return (type === "data") || (type === "lineEnding");
101
+ });
102
+
103
+ // Determine the type of the undefined reference
104
+ const previousUndefinedEvent = (artificialEventLists.length > 0) && artificialEventLists[artificialEventLists.length - 1][0];
105
+ const previousUndefinedToken = previousUndefinedEvent && previousUndefinedEvent[1];
106
+ if (
107
+ previousUndefinedToken &&
108
+ (previousUndefinedToken.end.line === undefinedReferenceType.start.line) &&
109
+ (previousUndefinedToken.end.column === undefinedReferenceType.start.column)
110
+ ) {
111
+ // Previous undefined reference event is immediately before this one
112
+ if (eventsToReplicate.length === 0) {
113
+ // The pair represent a collapsed reference (ex: [...][])
114
+ previousUndefinedToken.type = "undefinedReferenceCollapsed";
115
+ previousUndefinedToken.end = eventEndToken.end;
116
+ } else {
117
+ // The pair represent a full reference (ex: [...][...])
118
+ undefinedReferenceType.type = "undefinedReferenceFull";
119
+ undefinedReferenceType.start = previousUndefinedToken.start;
120
+ artificialEventLists.pop();
121
+ }
122
+ }
123
+
124
+ // Create artificial event list and replicate content
125
+ const text = eventsToReplicate
126
+ .filter((event) => event[0] === "enter")
127
+ .map((event) => tokenizeContext.sliceSerialize(event[1]))
128
+ .join("")
129
+ .trim();
130
+ if ((text.length > 0) && !text.includes("]")) {
131
+ /** @type {Event[]} */
132
+ const artificialEvents = [];
133
+ artificialEvents.push(
134
+ [ "enter", undefinedReferenceType, tokenizeContext ],
135
+ [ "enter", undefinedReference, tokenizeContext ]
136
+ );
137
+ for (const event of eventsToReplicate) {
138
+ const [ kind, token ] = event;
139
+ // Copy token because the current object will get modified by the parser
140
+ artificialEvents.push([ kind, { ...token }, tokenizeContext ]);
141
+ }
142
+ artificialEvents.push(
143
+ [ "exit", undefinedReference, tokenizeContext ],
144
+ [ "exit", undefinedReferenceType, tokenizeContext ]
145
+ );
146
+ artificialEventLists.push(artificialEvents);
147
+ }
148
+ }
149
+
150
+ // Continue with original behavior
151
+ return nokOriginal(code);
152
+ };
153
+
154
+ // Shim nok handler of labelEnd's tokenize
155
+ return tokenizeOriginal.call(tokenizeContext, effects, okOriginal, nokShim);
156
+ }
157
+
158
+ try {
159
+ // Shim labelEnd behavior to detect undefined references
160
+ labelEnd.tokenize = tokenizeShim;
161
+
162
+ // Use micromark to parse document into Events
163
+ const encoding = undefined;
164
+ const eol = true;
165
+ const parseContext = micromark.parse({ ...micromarkParseOptions, extensions });
166
+ const chunks = micromark.preprocess()(markdown, encoding, eol);
167
+ const events = micromark.postprocess(parseContext.document().write(chunks));
168
+
169
+ // Append artificial events and return all events
170
+ // eslint-disable-next-line unicorn/prefer-spread
171
+ return events.concat(...artificialEventLists);
172
+ } finally {
173
+ // Restore shimmed labelEnd behavior
174
+ labelEnd.tokenize = tokenizeOriginal;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Parses a Markdown document and returns micromark tokens (internal).
180
+ *
181
+ * @param {string} markdown Markdown document.
182
+ * @param {ParseOptions} [parseOptions] Options.
183
+ * @param {MicromarkParseOptions} [micromarkParseOptions] Options for micromark.
184
+ * @param {number} [lineDelta] Offset for start/end line.
185
+ * @param {MicromarkToken} [ancestor] Parent of top-most tokens.
186
+ * @returns {MicromarkToken[]} Micromark tokens.
187
+ */
188
+ function parseInternal(
189
+ markdown,
190
+ parseOptions = {},
191
+ micromarkParseOptions = {},
192
+ lineDelta = 0,
193
+ ancestor = undefined
194
+ ) {
195
+ // Get options
196
+ const freezeTokens = Boolean(parseOptions.freezeTokens);
197
+
198
+ // Use micromark to parse document into Events
199
+ const events = getEvents(markdown, micromarkParseOptions);
200
+
201
+ // Create Token objects
202
+ const document = [];
203
+ let flatTokens = [];
204
+ /** @type {MicromarkToken} */
205
+ const root = {
206
+ "type": "data",
207
+ "startLine": -1,
208
+ "startColumn": -1,
209
+ "endLine": -1,
210
+ "endColumn": -1,
211
+ "text": "ROOT",
212
+ "children": document,
213
+ "parent": null
214
+ };
215
+ const history = [ root ];
216
+ let current = root;
217
+ // eslint-disable-next-line jsdoc/valid-types
218
+ /** @type MicromarkParseOptions | null */
219
+ let reparseOptions = null;
220
+ let lines = null;
221
+ let skipHtmlFlowChildren = false;
222
+ for (const event of events) {
223
+ const [ kind, token, context ] = event;
224
+ const { type, start, end } = token;
225
+ const { "column": startColumn, "line": startLine } = start;
226
+ const { "column": endColumn, "line": endLine } = end;
227
+ const text = context.sliceSerialize(token);
228
+ if ((kind === "enter") && !skipHtmlFlowChildren) {
229
+ const previous = current;
230
+ history.push(previous);
231
+ current = {
232
+ type,
233
+ "startLine": startLine + lineDelta,
234
+ startColumn,
235
+ "endLine": endLine + lineDelta,
236
+ endColumn,
237
+ text,
238
+ "children": [],
239
+ "parent": ((previous === root) ? (ancestor || null) : previous)
240
+ };
241
+ if (ancestor) {
242
+ Object.defineProperty(current, htmlFlowSymbol, { "value": true });
243
+ }
244
+ previous.children.push(current);
245
+ flatTokens.push(current);
246
+ if ((current.type === "htmlFlow") && !isHtmlFlowComment(current)) {
247
+ skipHtmlFlowChildren = true;
248
+ if (!reparseOptions || !lines) {
249
+ reparseOptions = {
250
+ ...micromarkParseOptions,
251
+ "extensions": [
252
+ {
253
+ "disable": {
254
+ "null": [ "codeIndented", "htmlFlow" ]
255
+ }
256
+ }
257
+ ]
258
+ };
259
+ lines = markdown.split(newLineRe);
260
+ }
261
+ const reparseMarkdown = lines
262
+ .slice(current.startLine - 1, current.endLine)
263
+ .join("\n");
264
+ const tokens = parseInternal(
265
+ reparseMarkdown,
266
+ parseOptions,
267
+ reparseOptions,
268
+ current.startLine - 1,
269
+ current
270
+ );
271
+ current.children = tokens;
272
+ // Avoid stack overflow of Array.push(...spread)
273
+ // eslint-disable-next-line unicorn/prefer-spread
274
+ flatTokens = flatTokens.concat(tokens[flatTokensSymbol]);
275
+ }
276
+ } else if (kind === "exit") {
277
+ if (type === "htmlFlow") {
278
+ skipHtmlFlowChildren = false;
279
+ }
280
+ if (!skipHtmlFlowChildren) {
281
+ if (freezeTokens) {
282
+ Object.freeze(current.children);
283
+ Object.freeze(current);
284
+ }
285
+ // @ts-ignore
286
+ current = history.pop();
287
+ }
288
+ }
289
+ }
290
+
291
+ // Return document
292
+ Object.defineProperty(document, flatTokensSymbol, { "value": flatTokens });
293
+ if (freezeTokens) {
294
+ Object.freeze(document);
295
+ }
296
+ return document;
297
+ }
298
+
299
+ /**
300
+ * Parses a Markdown document and returns micromark tokens.
301
+ *
302
+ * @param {string} markdown Markdown document.
303
+ * @param {ParseOptions} [parseOptions] Options.
304
+ * @returns {MicromarkToken[]} Micromark tokens.
305
+ */
306
+ function parse(markdown, parseOptions) {
307
+ return parseInternal(markdown, parseOptions);
308
+ }
309
+
310
+ module.exports = {
311
+ getEvents,
312
+ parse
313
+ };
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "markdownlint-rule-helpers",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "A collection of markdownlint helper functions for custom rules",
5
5
  "main": "./helpers.js",
6
- "exports": "./helpers.js",
6
+ "exports": {
7
+ ".": "./helpers.js",
8
+ "./micromark": "./micromark-helpers.cjs"
9
+ },
7
10
  "author": "David Anson (https://dlaa.me/)",
8
11
  "license": "MIT",
9
12
  "homepage": "https://github.com/DavidAnson/markdownlint",
package/helpers/shared.js CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  "use strict";
4
4
 
5
+ // Symbol for identifing the flat tokens array from micromark parse
6
+ module.exports.flatTokensSymbol = Symbol("flat-tokens");
7
+
8
+ // Symbol for identifying the htmlFlow token from micromark parse
9
+ module.exports.htmlFlowSymbol = Symbol("html-flow");
10
+
5
11
  // Regular expression for matching common newline characters
6
12
  // See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
7
13
  module.exports.newLineRe = /\r\n?|\n/g;
@@ -0,0 +1,28 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ // eslint-disable-next-line n/no-extraneous-require
6
+ const test = require("ava").default;
7
+ const { "exports": packageExports, name } = require("../helpers/package.json");
8
+
9
+ const exportMappings = new Map([
10
+ [ ".", "../helpers/helpers.js" ],
11
+ [ "./micromark", "../helpers/micromark-helpers.cjs" ]
12
+ ]);
13
+
14
+ test("exportMappings", (t) => {
15
+ t.deepEqual(
16
+ Object.keys(packageExports),
17
+ [ ...exportMappings.keys() ]
18
+ );
19
+ });
20
+
21
+ for (const [ exportName, exportPath ] of exportMappings) {
22
+ test(exportName, (t) => {
23
+ t.is(
24
+ require(exportName.replace(/^\./u, name)),
25
+ require(exportPath)
26
+ );
27
+ });
28
+ }
package/lib/cache.js CHANGED
@@ -3,11 +3,10 @@
3
3
  "use strict";
4
4
 
5
5
  const helpers = require("../helpers");
6
- const { filterByTypes } = require("../helpers/micromark.cjs");
6
+ const { filterByTypes } = require("../helpers/micromark-helpers.cjs");
7
7
 
8
8
  /** @type {Map<string, object>} */
9
9
  const map = new Map();
10
- // eslint-disable-next-line no-undef-init
11
10
  let params = undefined;
12
11
 
13
12
  /**
@@ -46,7 +45,8 @@ function getCached(name, getValue) {
46
45
  */
47
46
  function filterByTypesCached(types, htmlFlow) {
48
47
  return getCached(
49
- types.join("|"),
48
+ // eslint-disable-next-line prefer-rest-params
49
+ JSON.stringify(arguments),
50
50
  () => filterByTypes(params.parsers.micromark.tokens, types, htmlFlow)
51
51
  );
52
52
  }