markdownlint 0.21.1 → 0.24.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.
@@ -12,12 +12,12 @@ module.exports.newLineRe = newLineRe;
12
12
  // Regular expression for matching common front matter (YAML and TOML)
13
13
  module.exports.frontMatterRe =
14
14
  // eslint-disable-next-line max-len
15
- /((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$))(\r\n|\r|\n|$)/m;
15
+ /((^---\s*$[^]*?^---\s*$)|(^\+\+\+\s*$[^]*?^(\+\+\+|\.\.\.)\s*$)|(^\{\s*$[^]*?^\}\s*$))(\r\n|\r|\n|$)/m;
16
16
 
17
17
  // Regular expression for matching inline disable/enable comments
18
18
  const inlineCommentRe =
19
19
  // eslint-disable-next-line max-len
20
- /<!--\s*markdownlint-(?:(?:(disable|enable|capture|restore|disable-file|enable-file)((?:\s+[a-z0-9_-]+)*))|(?:(configure-file)\s+([\s\S]*?)))\s*-->/ig;
20
+ /<!--\s*markdownlint-(?:(?:(disable|enable|capture|restore|disable-file|enable-file|disable-next-line)((?:\s+[a-z0-9_-]+)*))|(?:(configure-file)\s+([\s\S]*?)))\s*-->/ig;
21
21
  module.exports.inlineCommentRe = inlineCommentRe;
22
22
 
23
23
  // Regular expressions for range matching
@@ -29,13 +29,18 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
29
29
  const emphasisMarkersRe = /[_*]/g;
30
30
 
31
31
  // Regular expression for inline links and shortcut reference links
32
- const linkRe = /\[(?:[^[\]]|\[[^\]]*\])*\](?:\(\S*\))?/g;
32
+ const linkRe = /(\[(?:[^[\]]|\[[^\]]*\])*\])(\(\S*\)|\[\S*\])?/g;
33
+ module.exports.linkRe = linkRe;
33
34
 
34
- // readFile options for reading with the UTF-8 encoding
35
- module.exports.utf8Encoding = "utf8";
35
+ // Regular expression for link reference definition lines
36
+ module.exports.linkReferenceRe = /^ {0,3}\[[^\]]+]:\s.*$/;
36
37
 
37
38
  // All punctuation characters (normal and full-width)
38
- module.exports.allPunctuation = ".,;:!?。,;:!?";
39
+ const allPunctuation = ".,;:!?。,;:!?";
40
+ module.exports.allPunctuation = allPunctuation;
41
+
42
+ // All punctuation characters without question mark (normal and full-width)
43
+ module.exports.allPunctuationNoQuestion = allPunctuation.replace(/[??]/gu, "");
39
44
 
40
45
  // Returns true iff the input is a number
41
46
  module.exports.isNumber = function isNumber(obj) {
@@ -95,32 +100,47 @@ module.exports.includesSorted = function includesSorted(array, element) {
95
100
  return false;
96
101
  };
97
102
 
98
- // Replaces the text of all properly-formatted HTML comments with whitespace
103
+ // Replaces the content of properly-formatted CommonMark comments with "."
99
104
  // This preserves the line/column information for the rest of the document
100
- // Trailing whitespace is avoided with a '\' character in the last column
101
- // See https://www.w3.org/TR/html5/syntax.html#comments for details
105
+ // https://spec.commonmark.org/0.29/#html-blocks
106
+ // https://spec.commonmark.org/0.29/#html-comment
102
107
  const htmlCommentBegin = "<!--";
103
108
  const htmlCommentEnd = "-->";
104
109
  module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
105
110
  let i = 0;
106
111
  while ((i = text.indexOf(htmlCommentBegin, i)) !== -1) {
107
- const j = text.indexOf(htmlCommentEnd, i);
112
+ const j = text.indexOf(htmlCommentEnd, i + 2);
108
113
  if (j === -1) {
109
114
  // Un-terminated comments are treated as text
110
115
  break;
111
116
  }
112
- const comment = text.slice(i + htmlCommentBegin.length, j);
113
- if ((comment.length > 0) &&
114
- (comment[0] !== ">") &&
115
- (comment[comment.length - 1] !== "-") &&
116
- !comment.includes("--") &&
117
- (text.slice(i, j + htmlCommentEnd.length)
118
- .search(inlineCommentRe) === -1)) {
119
- const blanks = comment
120
- .replace(/[^\r\n]/g, " ")
121
- .replace(/ ([\r\n])/g, "\\$1");
122
- text = text.slice(0, i + htmlCommentBegin.length) +
123
- blanks + text.slice(j);
117
+ // If the comment has content...
118
+ if (j > i + htmlCommentBegin.length) {
119
+ let k = i - 1;
120
+ while (text[k] === " ") {
121
+ k--;
122
+ }
123
+ // If comment is not within an indented code block...
124
+ if (k >= i - 4) {
125
+ const content = text.slice(i + htmlCommentBegin.length, j);
126
+ const isBlock = (k < 0) || (text[k] === "\n");
127
+ const isValid = isBlock ||
128
+ (!content.startsWith(">") && !content.startsWith("->") &&
129
+ !content.endsWith("-") && !content.includes("--"));
130
+ // If a valid block/inline comment...
131
+ if (isValid) {
132
+ const inlineCommentIndex = text
133
+ .slice(i, j + htmlCommentEnd.length)
134
+ .search(inlineCommentRe);
135
+ // If not a markdownlint inline directive...
136
+ if (inlineCommentIndex === -1) {
137
+ text =
138
+ text.slice(0, i + htmlCommentBegin.length) +
139
+ content.replace(/[^\r\n]/g, ".") +
140
+ text.slice(j);
141
+ }
142
+ }
143
+ }
124
144
  }
125
145
  i = j + htmlCommentEnd.length;
126
146
  }
@@ -169,7 +189,7 @@ module.exports.fencedCodeBlockStyleFor =
169
189
  */
170
190
  function indentFor(token) {
171
191
  const line = token.line.replace(/^[\s>]*(> |>)/, "");
172
- return line.length - line.trimLeft().length;
192
+ return line.length - line.trimStart().length;
173
193
  }
174
194
  module.exports.indentFor = indentFor;
175
195
 
@@ -219,58 +239,82 @@ function filterTokens(params, type, handler) {
219
239
  }
220
240
  module.exports.filterTokens = filterTokens;
221
241
 
242
+ /**
243
+ * Returns whether a token is a math block (created by markdown-it-texmath).
244
+ *
245
+ * @param {Object} token MarkdownItToken instance.
246
+ * @returns {boolean} True iff token is a math block.
247
+ */
248
+ function isMathBlock(token) {
249
+ return (
250
+ (token.tag === "math") &&
251
+ token.type.startsWith("math_block") &&
252
+ !token.type.endsWith("_end")
253
+ );
254
+ }
255
+
222
256
  // Get line metadata array
223
257
  module.exports.getLineMetadata = function getLineMetadata(params) {
224
- const lineMetadata = params.lines.map(function mapLine(line, index) {
225
- return [ line, index, false, 0, false, false ];
226
- });
227
- filterTokens(params, "fence", function forToken(token) {
258
+ const lineMetadata = params.lines.map(
259
+ (line, index) => [ line, index, false, 0, false, false, false, false ]
260
+ );
261
+ filterTokens(params, "fence", (token) => {
228
262
  lineMetadata[token.map[0]][3] = 1;
229
263
  lineMetadata[token.map[1] - 1][3] = -1;
230
264
  for (let i = token.map[0] + 1; i < token.map[1] - 1; i++) {
231
265
  lineMetadata[i][2] = true;
232
266
  }
233
267
  });
234
- filterTokens(params, "code_block", function forToken(token) {
268
+ filterTokens(params, "code_block", (token) => {
235
269
  for (let i = token.map[0]; i < token.map[1]; i++) {
236
270
  lineMetadata[i][2] = true;
237
271
  }
238
272
  });
239
- filterTokens(params, "table_open", function forToken(token) {
273
+ filterTokens(params, "table_open", (token) => {
240
274
  for (let i = token.map[0]; i < token.map[1]; i++) {
241
275
  lineMetadata[i][4] = true;
242
276
  }
243
277
  });
244
- filterTokens(params, "list_item_open", function forToken(token) {
278
+ filterTokens(params, "list_item_open", (token) => {
245
279
  let count = 1;
246
280
  for (let i = token.map[0]; i < token.map[1]; i++) {
247
281
  lineMetadata[i][5] = count;
248
282
  count++;
249
283
  }
250
284
  });
251
- filterTokens(params, "hr", function forToken(token) {
285
+ filterTokens(params, "hr", (token) => {
252
286
  lineMetadata[token.map[0]][6] = true;
253
287
  });
288
+ params.tokens.filter(isMathBlock).forEach((token) => {
289
+ for (let i = token.map[0]; i < token.map[1]; i++) {
290
+ lineMetadata[i][7] = true;
291
+ }
292
+ });
254
293
  return lineMetadata;
255
294
  };
256
295
 
257
296
  // Calls the provided function for each line (with context)
258
297
  module.exports.forEachLine = function forEachLine(lineMetadata, handler) {
259
298
  lineMetadata.forEach(function forMetadata(metadata) {
260
- // Parameters: line, lineIndex, inCode, onFence, inTable, inItem, inBreak
299
+ // Parameters:
300
+ // line, lineIndex, inCode, onFence, inTable, inItem, inBreak, inMath
261
301
  handler(...metadata);
262
302
  });
263
303
  };
264
304
 
265
305
  // Returns (nested) lists as a flat array (in order)
266
- module.exports.flattenLists = function flattenLists(params) {
306
+ module.exports.flattenLists = function flattenLists(tokens) {
267
307
  const flattenedLists = [];
268
308
  const stack = [];
269
309
  let current = null;
270
310
  let nesting = 0;
271
311
  const nestingStack = [];
272
312
  let lastWithMap = { "map": [ 0, 1 ] };
273
- params.tokens.forEach(function forToken(token) {
313
+ tokens.forEach((token) => {
314
+ if (isMathBlock(token) && token.map[1]) {
315
+ // markdown-it-texmath plugin does not account for math_block_end
316
+ token.map[1]++;
317
+ }
274
318
  if ((token.type === "bullet_list_open") ||
275
319
  (token.type === "ordered_list_open")) {
276
320
  // Save current context and start a new one
@@ -476,6 +520,47 @@ module.exports.addErrorContext = function addErrorContext(
476
520
  addError(onError, lineNumber, null, context, range, fixInfo);
477
521
  };
478
522
 
523
+ /**
524
+ * Returns an array of code span ranges.
525
+ *
526
+ * @param {string[]} lines Lines to scan for code span ranges.
527
+ * @returns {number[][]} Array of ranges (line, index, length).
528
+ */
529
+ module.exports.inlineCodeSpanRanges = (lines) => {
530
+ const exclusions = [];
531
+ forEachInlineCodeSpan(
532
+ lines.join("\n"),
533
+ (code, lineIndex, columnIndex) => {
534
+ const codeLines = code.split(newLineRe);
535
+ // eslint-disable-next-line unicorn/no-for-loop
536
+ for (let i = 0; i < codeLines.length; i++) {
537
+ exclusions.push(
538
+ [ lineIndex + i, columnIndex, codeLines[i].length ]
539
+ );
540
+ columnIndex = 0;
541
+ }
542
+ }
543
+ );
544
+ return exclusions;
545
+ };
546
+
547
+ /**
548
+ * Determines whether the specified range overlaps another range.
549
+ *
550
+ * @param {number[][]} ranges Array of ranges (line, index, length).
551
+ * @param {number} lineIndex Line index to check.
552
+ * @param {number} index Index to check.
553
+ * @param {number} length Length to check.
554
+ * @returns {boolean} True iff the specified range overlaps.
555
+ */
556
+ module.exports.overlapsAnyRange = (ranges, lineIndex, index, length) => (
557
+ !ranges.every((span) => (
558
+ (lineIndex !== span[0]) ||
559
+ (index + length < span[1]) ||
560
+ (index > span[1] + span[2])
561
+ ))
562
+ );
563
+
479
564
  // Returns a range object for a line by applying a RegExp
480
565
  module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
481
566
  let range = null;
@@ -494,7 +579,10 @@ module.exports.frontMatterHasTitle =
494
579
  const ignoreFrontMatter =
495
580
  (frontMatterTitlePattern !== undefined) && !frontMatterTitlePattern;
496
581
  const frontMatterTitleRe =
497
- new RegExp(String(frontMatterTitlePattern || "^\\s*title\\s*[:=]"), "i");
582
+ new RegExp(
583
+ String(frontMatterTitlePattern || "^\\s*\"?title\"?\\s*[:=]"),
584
+ "i"
585
+ );
498
586
  return !ignoreFrontMatter &&
499
587
  frontMatterLines.some((line) => frontMatterTitleRe.test(line));
500
588
  };
@@ -677,8 +765,9 @@ module.exports.applyFixes = function applyFixes(input, errors) {
677
765
  const editIndex = editColumn - 1;
678
766
  if (
679
767
  (lineIndex !== lastLineIndex) ||
680
- ((editIndex + deleteCount) < lastEditIndex) ||
681
- (deleteCount === -1)
768
+ (deleteCount === -1) ||
769
+ ((editIndex + deleteCount) <=
770
+ (lastEditIndex - ((deleteCount > 0) ? 0 : 1)))
682
771
  ) {
683
772
  lines[lineIndex] = applyFix(lines[lineIndex], fixInfo, lineEnding);
684
773
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint-rule-helpers",
3
- "version": "0.12.0",
3
+ "version": "0.15.0",
4
4
  "description": "A collection of markdownlint helper functions for custom rules",
5
5
  "main": "helpers.js",
6
6
  "author": "David Anson (https://dlaa.me/)",
package/lib/cache.js CHANGED
@@ -2,14 +2,6 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- let lineMetadata = null;
6
- module.exports.lineMetadata = (value) => {
7
- if (value) {
8
- lineMetadata = value;
9
- }
10
- return lineMetadata;
11
- };
12
-
13
5
  let flattenedLists = null;
14
6
  module.exports.flattenedLists = (value) => {
15
7
  if (value) {
@@ -18,7 +10,24 @@ module.exports.flattenedLists = (value) => {
18
10
  return flattenedLists;
19
11
  };
20
12
 
13
+ let inlineCodeSpanRanges = null;
14
+ module.exports.inlineCodeSpanRanges = (value) => {
15
+ if (value) {
16
+ inlineCodeSpanRanges = value;
17
+ }
18
+ return inlineCodeSpanRanges;
19
+ };
20
+
21
+ let lineMetadata = null;
22
+ module.exports.lineMetadata = (value) => {
23
+ if (value) {
24
+ lineMetadata = value;
25
+ }
26
+ return lineMetadata;
27
+ };
28
+
21
29
  module.exports.clear = () => {
22
- lineMetadata = null;
23
30
  flattenedLists = null;
31
+ inlineCodeSpanRanges = null;
32
+ lineMetadata = null;
24
33
  };
@@ -8,7 +8,7 @@ export = markdownlint;
8
8
  */
9
9
  declare function markdownlint(options: Options, callback: LintCallback): void;
10
10
  declare namespace markdownlint {
11
- export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback };
11
+ export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
12
12
  }
13
13
  /**
14
14
  * Configuration options.
@@ -51,10 +51,14 @@ type Options = {
51
51
  /**
52
52
  * Additional plugins.
53
53
  */
54
- markdownItPlugins?: any[][];
54
+ markdownItPlugins?: Plugin[];
55
+ /**
56
+ * File system implementation.
57
+ */
58
+ fs?: any;
55
59
  };
56
60
  /**
57
- * Called with the result of the lint operation.
61
+ * Called with the result of the lint function.
58
62
  */
59
63
  type LintCallback = (err: Error | null, results?: LintResults) => void;
60
64
  /**
@@ -70,18 +74,21 @@ declare function markdownlintSync(options: Options): LintResults;
70
74
  * @param {string} file Configuration file name.
71
75
  * @param {ConfigurationParser[] | ReadConfigCallback} parsers Parsing
72
76
  * function(s).
77
+ * @param {Object} [fs] File system implementation.
73
78
  * @param {ReadConfigCallback} [callback] Callback (err, result) function.
74
79
  * @returns {void}
75
80
  */
76
- declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, callback?: ReadConfigCallback): void;
81
+ declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, fs?: any, callback?: ReadConfigCallback): void;
77
82
  /**
78
83
  * Read specified configuration file synchronously.
79
84
  *
80
85
  * @param {string} file Configuration file name.
81
86
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
87
+ * @param {Object} [fs] File system implementation.
82
88
  * @returns {Configuration} Configuration object.
89
+ * @throws An Error if processing fails.
83
90
  */
84
- declare function readConfigSync(file: string, parsers?: ConfigurationParser[]): Configuration;
91
+ declare function readConfigSync(file: string, parsers?: ConfigurationParser[], fs?: any): Configuration;
85
92
  /**
86
93
  * Gets the (semantic) version of the library.
87
94
  *
@@ -105,7 +112,7 @@ type RuleParams = {
105
112
  */
106
113
  name: string;
107
114
  /**
108
- * markdown-it token objects.
115
+ * Token objects from markdown-it.
109
116
  */
110
117
  tokens: MarkdownItToken[];
111
118
  /**
@@ -262,7 +269,7 @@ type Rule = {
262
269
  function: RuleFunction;
263
270
  };
264
271
  /**
265
- * markdown-it plugin.
272
+ * A markdown-it plugin.
266
273
  */
267
274
  type Plugin = any[];
268
275
  /**
@@ -310,12 +317,16 @@ type LintError = {
310
317
  /**
311
318
  * Fix information.
312
319
  */
313
- fixInfo: FixInfo;
320
+ fixInfo?: FixInfo;
314
321
  };
315
322
  /**
316
323
  * Fix information.
317
324
  */
318
325
  type FixInfo = {
326
+ /**
327
+ * Line number (1-based).
328
+ */
329
+ lineNumber?: number;
319
330
  /**
320
331
  * Column of the fix (1-based).
321
332
  */
@@ -334,20 +345,24 @@ type FixInfo = {
334
345
  * {@link ../schema/markdownlint-config-schema.json}.
335
346
  */
336
347
  type Configuration = {
337
- [x: string]: any;
348
+ [x: string]: RuleConfiguration;
338
349
  };
339
350
  /**
340
351
  * Rule configuration.
341
352
  */
342
- type RuleConfiguration = any;
353
+ type RuleConfiguration = boolean | any;
343
354
  /**
344
355
  * Parses a configuration string and returns a configuration object.
345
356
  */
346
357
  type ConfigurationParser = (text: string) => Configuration;
347
358
  /**
348
- * Called with the result of the readConfig operation.
359
+ * Called with the result of the readConfig function.
349
360
  */
350
361
  type ReadConfigCallback = (err: Error | null, config?: Configuration) => void;
362
+ /**
363
+ * Called with the result of the resolveConfigExtends function.
364
+ */
365
+ type ResolveConfigExtendsCallback = (err: Error | null, path?: string) => void;
351
366
  /**
352
367
  * Lint specified Markdown files.
353
368
  *
@@ -360,6 +375,7 @@ declare function markdownlintPromise(options: Options): Promise<LintResults>;
360
375
  *
361
376
  * @param {string} file Configuration file name.
362
377
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
378
+ * @param {Object} [fs] File system implementation.
363
379
  * @returns {Promise<Configuration>} Configuration object.
364
380
  */
365
- declare function readConfigPromise(file: string, parsers?: ConfigurationParser[]): Promise<Configuration>;
381
+ declare function readConfigPromise(file: string, parsers?: ConfigurationParser[], fs?: any): Promise<Configuration>;