markdownlint 0.23.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.
package/lib/md011.js CHANGED
@@ -2,40 +2,44 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, forEachInlineChild, unescapeMarkdown } =
6
- require("../helpers");
5
+ const { addError, forEachLine, overlapsAnyRange } = require("../helpers");
6
+ const { inlineCodeSpanRanges, lineMetadata } = require("./cache");
7
7
 
8
- const reversedLinkRe = /\(([^)]+)\)\[([^\]^][^\]]*)]/g;
8
+ const reversedLinkRe =
9
+ /(^|[^\\])\(([^)]+)\)\[([^\]^][^\]]*)](?!\()/g;
9
10
 
10
11
  module.exports = {
11
12
  "names": [ "MD011", "no-reversed-links" ],
12
13
  "description": "Reversed link syntax",
13
14
  "tags": [ "links" ],
14
15
  "function": function MD011(params, onError) {
15
- forEachInlineChild(params, "text", (token) => {
16
- const { lineNumber, content } = token;
17
- let match = null;
18
- while ((match = reversedLinkRe.exec(content)) !== null) {
19
- const [ reversedLink, linkText, linkDestination ] = match;
20
- const line = params.lines[lineNumber - 1];
21
- const column = unescapeMarkdown(line).indexOf(reversedLink) + 1;
22
- const length = reversedLink.length;
23
- const range = column ? [ column, length ] : null;
24
- const fixInfo = column ?
25
- {
26
- "editColumn": column,
27
- "deleteCount": length,
28
- "insertText": `[${linkText}](${linkDestination})`
29
- } :
30
- null;
31
- addError(
32
- onError,
33
- lineNumber,
34
- reversedLink,
35
- null,
36
- range,
37
- fixInfo
38
- );
16
+ const exclusions = inlineCodeSpanRanges();
17
+ forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
18
+ if (!inCode && !onFence) {
19
+ let match = null;
20
+ while ((match = reversedLinkRe.exec(line)) !== null) {
21
+ const [ reversedLink, preChar, linkText, linkDestination ] = match;
22
+ const index = match.index + preChar.length;
23
+ const length = match[0].length - preChar.length;
24
+ if (
25
+ !linkText.endsWith("\\") &&
26
+ !linkDestination.endsWith("\\") &&
27
+ !overlapsAnyRange(exclusions, lineIndex, index, length)
28
+ ) {
29
+ addError(
30
+ onError,
31
+ lineIndex + 1,
32
+ reversedLink.slice(preChar.length),
33
+ null,
34
+ [ index + 1, length ],
35
+ {
36
+ "editColumn": index + 1,
37
+ "deleteCount": length,
38
+ "insertText": `[${linkText}](${linkDestination})`
39
+ }
40
+ );
41
+ }
42
+ }
39
43
  }
40
44
  });
41
45
  }
package/lib/md037.js CHANGED
@@ -78,11 +78,23 @@ module.exports = {
78
78
  lineMetadata(),
79
79
  (line, lineIndex, inCode, onFence, inTable, inItem, onBreak, inMath) => {
80
80
  const onItemStart = (inItem === 1);
81
- if (inCode || inTable || onBreak || onItemStart || isBlankLine(line)) {
81
+ if (
82
+ inCode ||
83
+ onFence ||
84
+ inTable ||
85
+ onBreak ||
86
+ onItemStart ||
87
+ isBlankLine(line)
88
+ ) {
82
89
  // Emphasis resets when leaving a block
83
90
  resetRunTracking();
84
91
  }
85
- if (inCode || onBreak || inMath) {
92
+ if (
93
+ inCode ||
94
+ onFence ||
95
+ onBreak ||
96
+ inMath
97
+ ) {
86
98
  // Emphasis has no meaning here
87
99
  return;
88
100
  }
package/lib/md043.js CHANGED
@@ -28,8 +28,11 @@ module.exports = {
28
28
  const actual = levels[heading.tag] + " " + content;
29
29
  const expected = getExpected();
30
30
  if (expected === "*") {
31
- matchAny = true;
32
- getExpected();
31
+ const nextExpected = getExpected();
32
+ if (nextExpected.toLowerCase() !== actual.toLowerCase()) {
33
+ matchAny = true;
34
+ i--;
35
+ }
33
36
  } else if (expected === "+") {
34
37
  matchAny = true;
35
38
  } else if (expected.toLowerCase() === actual.toLowerCase()) {
@@ -43,9 +46,11 @@ module.exports = {
43
46
  }
44
47
  }
45
48
  });
49
+ const extraHeadings = requiredHeadings.length - i;
46
50
  if (
47
51
  !hasError &&
48
- (i < requiredHeadings.length) &&
52
+ ((extraHeadings > 1) ||
53
+ ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
49
54
  (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
50
55
  ) {
51
56
  addErrorContext(onError, params.lines.length,
package/lib/md044.js CHANGED
@@ -2,11 +2,9 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, bareUrlRe, escapeForRegExp, filterTokens,
6
- forEachInlineChild, newLineRe } = require("../helpers");
7
-
8
- const startNonWordRe = /^\W/;
9
- const endNonWordRe = /\W$/;
5
+ const { addErrorDetailIf, bareUrlRe, escapeForRegExp, forEachLine,
6
+ overlapsAnyRange, linkRe, linkReferenceRe } = require("../helpers");
7
+ const { inlineCodeSpanRanges, lineMetadata } = require("./cache");
10
8
 
11
9
  module.exports = {
12
10
  "names": [ "MD044", "proper-names" ],
@@ -15,80 +13,65 @@ module.exports = {
15
13
  "function": function MD044(params, onError) {
16
14
  let names = params.config.names;
17
15
  names = Array.isArray(names) ? names : [];
16
+ names.sort((a, b) => (b.length - a.length) || a.localeCompare(b));
18
17
  const codeBlocks = params.config.code_blocks;
19
18
  const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
20
- // Text of automatic hyperlinks is implicitly a URL
21
- const autolinkText = new Set();
22
- filterTokens(params, "inline", (token) => {
23
- let inAutoLink = false;
24
- token.children.forEach((child) => {
25
- const { info, type } = child;
26
- if ((type === "link_open") && (info === "auto")) {
27
- inAutoLink = true;
28
- } else if (type === "link_close") {
29
- inAutoLink = false;
30
- } else if ((type === "text") && inAutoLink) {
31
- autolinkText.add(child);
19
+ const exclusions = [];
20
+ forEachLine(lineMetadata(), (line, lineIndex) => {
21
+ if (linkReferenceRe.test(line)) {
22
+ exclusions.push([ lineIndex, 0, line.length ]);
23
+ } else {
24
+ let match = null;
25
+ while ((match = bareUrlRe.exec(line)) !== null) {
26
+ exclusions.push([ lineIndex, match.index, match[0].length ]);
32
27
  }
33
- });
28
+ while ((match = linkRe.exec(line)) !== null) {
29
+ const [ , text, destination ] = match;
30
+ if (destination) {
31
+ exclusions.push(
32
+ [ lineIndex, match.index + text.length, destination.length ]
33
+ );
34
+ }
35
+ }
36
+ }
34
37
  });
35
- // For each proper name...
36
- names.forEach((name) => {
38
+ if (!includeCodeBlocks) {
39
+ exclusions.push(...inlineCodeSpanRanges());
40
+ }
41
+ for (const name of names) {
37
42
  const escapedName = escapeForRegExp(name);
38
- const startNamePattern = startNonWordRe.test(name) ? "" : "\\S*\\b";
39
- const endNamePattern = endNonWordRe.test(name) ? "" : "\\b\\S*";
43
+ const startNamePattern = /^\W/.test(name) ? "" : "\\b_*";
44
+ const endNamePattern = /\W$/.test(name) ? "" : "_*\\b";
40
45
  const namePattern =
41
- `(${startNamePattern})(${escapedName})(${endNamePattern})`;
42
- const anyNameRe = new RegExp(namePattern, "gi");
43
- // eslint-disable-next-line jsdoc/require-jsdoc
44
- function forToken(token) {
45
- if (!autolinkText.has(token)) {
46
- const fenceOffset = (token.type === "fence") ? 1 : 0;
47
- token.content.split(newLineRe).forEach((line, index) => {
48
- let match = null;
49
- while ((match = anyNameRe.exec(line)) !== null) {
50
- const [ fullMatch, leftMatch, nameMatch, rightMatch ] = match;
51
- if (fullMatch.search(bareUrlRe) === -1) {
52
- const wordMatch = fullMatch
53
- .replace(new RegExp(`^\\W{0,${leftMatch.length}}`), "")
54
- .replace(new RegExp(`\\W{0,${rightMatch.length}}$`), "");
55
- if (!names.includes(wordMatch)) {
56
- const lineNumber = token.lineNumber + index + fenceOffset;
57
- const fullLine = params.lines[lineNumber - 1];
58
- const matchLength = wordMatch.length;
59
- const matchIndex = fullLine.indexOf(wordMatch);
60
- const range = (matchIndex === -1) ?
61
- null :
62
- [ matchIndex + 1, matchLength ];
63
- const fixInfo = (matchIndex === -1) ?
64
- null :
65
- {
66
- "editColumn": matchIndex + 1,
67
- "deleteCount": matchLength,
68
- "insertText": name
69
- };
70
- addErrorDetailIf(
71
- onError,
72
- lineNumber,
73
- name,
74
- nameMatch,
75
- null,
76
- null,
77
- range,
78
- fixInfo
79
- );
46
+ `(${startNamePattern})(${escapedName})${endNamePattern}`;
47
+ const nameRe = new RegExp(namePattern, "gi");
48
+ forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
49
+ if (includeCodeBlocks || (!inCode && !onFence)) {
50
+ let match = null;
51
+ while ((match = nameRe.exec(line)) !== null) {
52
+ const [ , leftMatch, nameMatch ] = match;
53
+ const index = match.index + leftMatch.length;
54
+ const length = nameMatch.length;
55
+ if (!overlapsAnyRange(exclusions, lineIndex, index, length)) {
56
+ addErrorDetailIf(
57
+ onError,
58
+ lineIndex + 1,
59
+ name,
60
+ nameMatch,
61
+ null,
62
+ null,
63
+ [ index + 1, length ],
64
+ {
65
+ "editColumn": index + 1,
66
+ "deleteCount": length,
67
+ "insertText": name
80
68
  }
81
- }
69
+ );
82
70
  }
83
- });
71
+ exclusions.push([ lineIndex, index, length ]);
72
+ }
84
73
  }
85
- }
86
- forEachInlineChild(params, "text", forToken);
87
- if (includeCodeBlocks) {
88
- forEachInlineChild(params, "code_inline", forToken);
89
- filterTokens(params, "code_block", forToken);
90
- filterTokens(params, "fence", forToken);
91
- }
92
- });
74
+ });
75
+ }
93
76
  }
94
77
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint",
3
- "version": "0.23.1",
3
+ "version": "0.24.0",
4
4
  "description": "A Node.js style checker and lint tool for Markdown/CommonMark files.",
5
5
  "main": "lib/markdownlint.js",
6
6
  "types": "lib/markdownlint.d.ts",
@@ -21,8 +21,16 @@
21
21
  "build-example": "npm install --no-save --ignore-scripts grunt grunt-cli gulp through2",
22
22
  "ci": "npm-run-all --continue-on-error --parallel test-cover lint declaration build-config build-demo && git diff --exit-code",
23
23
  "clean-test-repos": "rimraf test-repos",
24
- "clone-test-repos": "mkdir test-repos && cd test-repos && git clone https://github.com/eslint/eslint eslint-eslint --depth 1 --no-tags --quiet && git clone https://github.com/mkdocs/mkdocs mkdocs-mkdocs --depth 1 --no-tags --quiet && git clone https://github.com/pi-hole/docs pi-hole-docs --depth 1 --no-tags --quiet",
25
- "clone-test-repos-large": "npm run clone-test-repos && cd test-repos && git clone https://github.com/dotnet/docs dotnet-docs --depth 1 --no-tags --quiet",
24
+ "clone-test-repos-dotnet-docs": "cd test-repos && git clone https://github.com/dotnet/docs dotnet-docs --depth 1 --no-tags --quiet",
25
+ "clone-test-repos-eslint-eslint": "cd test-repos && git clone https://github.com/eslint/eslint eslint-eslint --depth 1 --no-tags --quiet",
26
+ "clone-test-repos-mkdocs-mkdocs": "cd test-repos && git clone https://github.com/mkdocs/mkdocs mkdocs-mkdocs --depth 1 --no-tags --quiet",
27
+ "clone-test-repos-mochajs-mocha": "cd test-repos && git clone https://github.com/mochajs/mocha mochajs-mocha --depth 1 --no-tags --quiet",
28
+ "clone-test-repos-pi-hole-docs": "cd test-repos && git clone https://github.com/pi-hole/docs pi-hole-docs --depth 1 --no-tags --quiet",
29
+ "clone-test-repos-v8-v8-dev": "cd test-repos && git clone https://github.com/v8/v8.dev v8-v8-dev --depth 1 --no-tags --quiet",
30
+ "clone-test-repos-webhintio-hint": "cd test-repos && git clone https://github.com/webhintio/hint webhintio-hint --depth 1 --no-tags --quiet",
31
+ "clone-test-repos-webpack-webpack-js-org": "cd test-repos && git clone https://github.com/webpack/webpack.js.org webpack-webpack-js-org --depth 1 --no-tags --quiet",
32
+ "clone-test-repos": "mkdir test-repos && cd test-repos && npm run clone-test-repos-eslint-eslint && npm run clone-test-repos-mkdocs-mkdocs && npm run clone-test-repos-mochajs-mocha && npm run clone-test-repos-pi-hole-docs && npm run clone-test-repos-webhintio-hint && npm run clone-test-repos-webpack-webpack-js-org",
33
+ "clone-test-repos-large": "npm run clone-test-repos && cd test-repos && npm run clone-test-repos-dotnet-docs && npm run clone-test-repos-v8-v8-dev",
26
34
  "declaration": "npm run build-declaration && npm run test-declaration",
27
35
  "example": "cd example && node standalone.js && grunt markdownlint --force && gulp markdownlint",
28
36
  "lint": "eslint --max-warnings 0 .",
@@ -36,32 +44,32 @@
36
44
  "node": ">=10"
37
45
  },
38
46
  "dependencies": {
39
- "markdown-it": "12.0.4"
47
+ "markdown-it": "12.2.0"
40
48
  },
41
49
  "devDependencies": {
42
50
  "ava": "~3.15.0",
43
- "c8": "~7.5.0",
51
+ "c8": "~7.8.0",
44
52
  "cpy-cli": "~3.1.1",
45
- "eslint": "~7.19.0",
46
- "eslint-plugin-jsdoc": "~31.6.0",
53
+ "eslint": "~7.32.0",
54
+ "eslint-plugin-jsdoc": "~36.0.7",
47
55
  "eslint-plugin-node": "~11.1.0",
48
- "eslint-plugin-unicorn": "~27.0.0",
49
- "globby": "~11.0.2",
50
- "js-yaml": "~4.0.0",
56
+ "eslint-plugin-unicorn": "~35.0.0",
57
+ "globby": "~11.0.4",
58
+ "js-yaml": "~4.1.0",
51
59
  "markdown-it-for-inline": "~0.1.1",
52
60
  "markdown-it-sub": "~1.0.0",
53
61
  "markdown-it-sup": "~1.0.0",
54
- "markdown-it-texmath": "~0.8.0",
55
- "markdownlint-rule-helpers": "~0.13.0",
62
+ "markdown-it-texmath": "~0.9.1",
63
+ "markdownlint-rule-helpers": "~0.14.0",
56
64
  "npm-run-all": "~4.1.5",
57
65
  "rimraf": "~3.0.2",
58
66
  "strip-json-comments": "~3.1.1",
59
67
  "toml": "~3.0.0",
60
- "ts-loader": "~8.0.15",
68
+ "ts-loader": "~9.2.5",
61
69
  "tv4": "~1.3.0",
62
- "typescript": "~4.1.3",
63
- "webpack": "~5.21.1",
64
- "webpack-cli": "~4.5.0"
70
+ "typescript": "~4.3.5",
71
+ "webpack": "~5.51.1",
72
+ "webpack-cli": "~4.8.0"
65
73
  },
66
74
  "keywords": [
67
75
  "markdown",
@@ -55,7 +55,9 @@
55
55
  // MD010/no-hard-tabs - Hard tabs
56
56
  "MD010": {
57
57
  // Include code blocks
58
- "code_blocks": true
58
+ "code_blocks": true,
59
+ // Number of spaces for each hard tab
60
+ "spaces_per_tab": 1
59
61
  },
60
62
 
61
63
  // MD011/no-reversed-links - Reversed link syntax
@@ -244,7 +246,7 @@
244
246
 
245
247
  // MD048/code-fence-style - Code fence style
246
248
  "MD048": {
247
- // Code fence syle
249
+ // Code fence style
248
250
  "style": "consistent"
249
251
  }
250
252
  }
@@ -50,6 +50,8 @@ MD009:
50
50
  MD010:
51
51
  # Include code blocks
52
52
  code_blocks: true
53
+ # Number of spaces for each hard tab
54
+ spaces_per_tab: 1
53
55
 
54
56
  # MD011/no-reversed-links - Reversed link syntax
55
57
  MD011: true
@@ -221,5 +223,5 @@ MD047: true
221
223
 
222
224
  # MD048/code-fence-style - Code fence style
223
225
  MD048:
224
- # Code fence syle
226
+ # Code fence style
225
227
  style: "consistent"
@@ -327,6 +327,11 @@
327
327
  "description": "Include code blocks",
328
328
  "type": "boolean",
329
329
  "default": true
330
+ },
331
+ "spaces_per_tab": {
332
+ "description": "Number of spaces for each hard tab",
333
+ "type": "number",
334
+ "default": 1
330
335
  }
331
336
  },
332
337
  "additionalProperties": false
@@ -343,6 +348,11 @@
343
348
  "description": "Include code blocks",
344
349
  "type": "boolean",
345
350
  "default": true
351
+ },
352
+ "spaces_per_tab": {
353
+ "description": "Number of spaces for each hard tab",
354
+ "type": "number",
355
+ "default": 1
346
356
  }
347
357
  },
348
358
  "additionalProperties": false
@@ -1396,7 +1406,7 @@
1396
1406
  "default": true,
1397
1407
  "properties": {
1398
1408
  "style": {
1399
- "description": "Code fence syle",
1409
+ "description": "Code fence style",
1400
1410
  "type": "string",
1401
1411
  "enum": [
1402
1412
  "consistent",
@@ -1417,7 +1427,7 @@
1417
1427
  "default": true,
1418
1428
  "properties": {
1419
1429
  "style": {
1420
- "description": "Code fence syle",
1430
+ "description": "Code fence style",
1421
1431
  "type": "string",
1422
1432
  "enum": [
1423
1433
  "consistent",
@@ -1551,7 +1561,7 @@
1551
1561
  }
1552
1562
  },
1553
1563
  "additionalProperties": {
1554
- "type:": [
1564
+ "type": [
1555
1565
  "boolean",
1556
1566
  "object"
1557
1567
  ]