markdownlint 0.31.1 → 0.32.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 (72) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/CONTRIBUTING.md +2 -2
  3. package/README.md +19 -28
  4. package/demo/markdownlint-browser.js +521 -352
  5. package/doc/Rules.md +146 -127
  6. package/doc/md001.md +2 -2
  7. package/doc/md003.md +2 -2
  8. package/doc/md007.md +3 -4
  9. package/doc/md013.md +0 -3
  10. package/doc/md018.md +1 -1
  11. package/doc/md019.md +1 -1
  12. package/doc/md020.md +1 -1
  13. package/doc/md021.md +1 -1
  14. package/doc/md022.md +2 -2
  15. package/doc/md023.md +9 -2
  16. package/doc/md024.md +2 -2
  17. package/doc/md025.md +1 -1
  18. package/doc/md026.md +1 -1
  19. package/doc/md036.md +2 -2
  20. package/doc/md041.md +1 -1
  21. package/doc/md043.md +2 -5
  22. package/doc/md045.md +6 -0
  23. package/doc/md049.md +1 -1
  24. package/doc/md050.md +1 -1
  25. package/doc/md053.md +3 -3
  26. package/doc/md054.md +100 -0
  27. package/helpers/helpers.js +27 -7
  28. package/helpers/micromark.cjs +62 -15
  29. package/helpers/package.json +2 -1
  30. package/helpers/shared.js +3 -0
  31. package/lib/configuration.d.ts +1152 -0
  32. package/lib/constants.js +7 -7
  33. package/lib/markdownlint.d.ts +2 -4
  34. package/lib/markdownlint.js +9 -8
  35. package/lib/md001.js +2 -2
  36. package/lib/md003.js +2 -2
  37. package/lib/md005.js +21 -17
  38. package/lib/md007.js +62 -25
  39. package/lib/md013.js +1 -4
  40. package/lib/md018.js +1 -1
  41. package/lib/md019.js +1 -1
  42. package/lib/md020.js +1 -1
  43. package/lib/md021.js +1 -1
  44. package/lib/md022.js +4 -4
  45. package/lib/md023.js +2 -2
  46. package/lib/md024.js +2 -2
  47. package/lib/md025.js +1 -1
  48. package/lib/md026.js +1 -1
  49. package/lib/md030.js +33 -22
  50. package/lib/md032.js +1 -4
  51. package/lib/md033.js +1 -3
  52. package/lib/md034.js +31 -25
  53. package/lib/md036.js +2 -2
  54. package/lib/md037.js +2 -2
  55. package/lib/md038.js +7 -5
  56. package/lib/md041.js +1 -1
  57. package/lib/md043.js +3 -3
  58. package/lib/md045.js +33 -3
  59. package/lib/md049-md050.js +2 -2
  60. package/lib/md051.js +8 -6
  61. package/lib/md053.js +1 -1
  62. package/lib/md054.js +125 -0
  63. package/lib/rules.js +5 -3
  64. package/package.json +18 -16
  65. package/schema/.markdownlint.jsonc +63 -60
  66. package/schema/.markdownlint.yaml +62 -59
  67. package/schema/markdownlint-config-schema.json +840 -186
  68. package/style/relaxed.json +0 -1
  69. package/doc/md002.md +0 -37
  70. package/doc/md006.md +0 -46
  71. package/lib/md002.js +0 -22
  72. package/lib/md006.js +0 -34
package/lib/md038.js CHANGED
@@ -3,7 +3,8 @@
3
3
  "use strict";
4
4
 
5
5
  const { addErrorContext } = require("../helpers");
6
- const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
6
+ const { filterByTypes, inHtmlFlow, tokenIfType } =
7
+ require("../helpers/micromark.cjs");
7
8
 
8
9
  const leftSpaceRe = /^\s(?:[^`]|$)/;
9
10
  const rightSpaceRe = /[^`]\s$/;
@@ -23,10 +24,11 @@ module.exports = {
23
24
  "description": "Spaces inside code span elements",
24
25
  "tags": [ "whitespace", "code" ],
25
26
  "function": function MD038(params, onError) {
26
- const codeTextTokens =
27
- filterByTypes(params.parsers.micromark.tokens, [ "codeText" ]);
28
- for (const token of codeTextTokens) {
29
- const { children } = token;
27
+ const codeTexts =
28
+ filterByTypes(params.parsers.micromark.tokens, [ "codeText" ])
29
+ .filter((codeText) => !inHtmlFlow(codeText));
30
+ for (const codeText of codeTexts) {
31
+ const { children } = codeText;
30
32
  const first = 0;
31
33
  const last = children.length - 1;
32
34
  const startSequence = tokenIfType(children[first], "codeTextSequence");
package/lib/md041.js CHANGED
@@ -7,7 +7,7 @@ const { addErrorContext, frontMatterHasTitle } = require("../helpers");
7
7
  module.exports = {
8
8
  "names": [ "MD041", "first-line-heading", "first-line-h1" ],
9
9
  "description": "First line in a file should be a top-level heading",
10
- "tags": [ "headings", "headers" ],
10
+ "tags": [ "headings" ],
11
11
  "function": function MD041(params, onError) {
12
12
  const level = Number(params.config.level || 1);
13
13
  const tag = "h" + level;
package/lib/md043.js CHANGED
@@ -6,11 +6,11 @@ const { addErrorContext, addErrorDetailIf, forEachHeading } =
6
6
  require("../helpers");
7
7
 
8
8
  module.exports = {
9
- "names": [ "MD043", "required-headings", "required-headers" ],
9
+ "names": [ "MD043", "required-headings" ],
10
10
  "description": "Required heading structure",
11
- "tags": [ "headings", "headers" ],
11
+ "tags": [ "headings" ],
12
12
  "function": function MD043(params, onError) {
13
- const requiredHeadings = params.config.headings || params.config.headers;
13
+ const requiredHeadings = params.config.headings;
14
14
  if (!Array.isArray(requiredHeadings)) {
15
15
  // Nothing to check; avoid doing any work
16
16
  return;
package/lib/md045.js CHANGED
@@ -2,15 +2,20 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError } = require("../helpers");
6
- const { filterByTypes } = require("../helpers/micromark.cjs");
5
+ const { addError, getHtmlAttributeRe, nextLinesRe } = require("../helpers");
6
+ const { filterByTypes, getHtmlTagInfo } = require("../helpers/micromark.cjs");
7
+
8
+ const altRe = getHtmlAttributeRe("alt");
7
9
 
8
10
  module.exports = {
9
11
  "names": [ "MD045", "no-alt-text" ],
10
12
  "description": "Images should have alternate text (alt text)",
11
13
  "tags": [ "accessibility", "images" ],
12
14
  "function": function MD045(params, onError) {
13
- const images = filterByTypes(params.parsers.micromark.tokens, [ "image" ]);
15
+ const { tokens } = params.parsers.micromark;
16
+
17
+ // Process Markdown images
18
+ const images = filterByTypes(tokens, [ "image" ]);
14
19
  for (const image of images) {
15
20
  const labelTexts = filterByTypes(image.children, [ "labelText" ]);
16
21
  if (labelTexts.some((labelText) => labelText.text.length === 0)) {
@@ -26,5 +31,30 @@ module.exports = {
26
31
  );
27
32
  }
28
33
  }
34
+
35
+ // Process HTML images
36
+ const htmlTexts = filterByTypes(tokens, [ "htmlText" ]);
37
+ for (const htmlText of htmlTexts) {
38
+ const { startColumn, startLine, text } = htmlText;
39
+ const htmlTagInfo = getHtmlTagInfo(htmlText);
40
+ if (
41
+ htmlTagInfo &&
42
+ !htmlTagInfo.close &&
43
+ (htmlTagInfo.name.toLowerCase() === "img") &&
44
+ !altRe.test(text)
45
+ ) {
46
+ const range = [
47
+ startColumn,
48
+ text.replace(nextLinesRe, "").length
49
+ ];
50
+ addError(
51
+ onError,
52
+ startLine,
53
+ undefined,
54
+ undefined,
55
+ range
56
+ );
57
+ }
58
+ }
29
59
  }
30
60
  };
@@ -58,7 +58,7 @@ const impl =
58
58
  module.exports = [
59
59
  {
60
60
  "names": [ "MD049", "emphasis-style" ],
61
- "description": "Emphasis style should be consistent",
61
+ "description": "Emphasis style",
62
62
  "tags": [ "emphasis" ],
63
63
  "function": function MD049(params, onError) {
64
64
  return impl(
@@ -73,7 +73,7 @@ module.exports = [
73
73
  },
74
74
  {
75
75
  "names": [ "MD050", "strong-style" ],
76
- "description": "Strong style should be consistent",
76
+ "description": "Strong style",
77
77
  "tags": [ "emphasis" ],
78
78
  "function": function MD050(params, onError) {
79
79
  return impl(
package/lib/md051.js CHANGED
@@ -2,18 +2,21 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addError, addErrorDetailIf } = require("../helpers");
5
+ const { addError, addErrorDetailIf, getHtmlAttributeRe } =
6
+ require("../helpers");
6
7
  const { filterByPredicate, filterByTypes, getHtmlTagInfo } =
7
8
  require("../helpers/micromark.cjs");
8
9
 
9
10
  // Regular expression for identifying HTML anchor names
10
- const idRe = /\sid\s*=\s*['"]?([^'"\s>]+)/iu;
11
- const nameRe = /\sname\s*=\s*['"]?([^'"\s>]+)/iu;
11
+ const idRe = getHtmlAttributeRe("id");
12
+ const nameRe = getHtmlAttributeRe("name");
12
13
  const anchorRe = /\{(#[a-z\d]+(?:[-_][a-z\d]+)*)\}/gu;
13
14
 
14
15
  // Sets for filtering heading tokens during conversion
15
16
  const childrenExclude = new Set([ "image", "reference", "resource" ]);
16
- const tokensInclude = new Set([ "codeTextData", "data" ]);
17
+ const tokensInclude = new Set(
18
+ [ "characterEscapeValue", "codeTextData", "data" ]
19
+ );
17
20
 
18
21
  /**
19
22
  * @typedef {import("../helpers/micromark.cjs").Token} Token
@@ -39,7 +42,6 @@ function convertHeadingToHTMLFragment(headingText) {
39
42
  inlineText
40
43
  .toLowerCase()
41
44
  // RegExp source with Ruby's \p{Word} expanded into its General Categories
42
- // eslint-disable-next-line max-len
43
45
  // https://github.com/gjtorikian/html-pipeline/blob/main/lib/html/pipeline/toc_filter.rb
44
46
  // https://ruby-doc.org/core-3.0.2/Regexp.html
45
47
  .replace(
@@ -99,7 +101,7 @@ module.exports = {
99
101
  if (htmlTagInfo && !htmlTagInfo.close) {
100
102
  const anchorMatch = idRe.exec(token.text) ||
101
103
  (htmlTagInfo.name.toLowerCase() === "a" && nameRe.exec(token.text));
102
- if (anchorMatch) {
104
+ if (anchorMatch && anchorMatch.length > 0) {
103
105
  fragments.set(`#${anchorMatch[1]}`, 0);
104
106
  }
105
107
  }
package/lib/md053.js CHANGED
@@ -23,7 +23,7 @@ module.exports = {
23
23
  };
24
24
  // Look for unused link references (unreferenced by any link/image)
25
25
  for (const definition of definitions.entries()) {
26
- const [ label, lineIndex ] = definition;
26
+ const [ label, [ lineIndex ] ] = definition;
27
27
  if (
28
28
  !ignored.has(label) &&
29
29
  !references.has(label) &&
package/lib/md054.js ADDED
@@ -0,0 +1,125 @@
1
+ // @ts-check
2
+
3
+ "use strict";
4
+
5
+ const { addErrorContext, nextLinesRe } = require("../helpers");
6
+ const { filterByTypes, filterByPredicate, getTokenTextByType } =
7
+ require("../helpers/micromark.cjs");
8
+ const { referenceLinkImageData } = require("./cache");
9
+
10
+ const backslashEscapeRe = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g;
11
+ const removeBackslashEscapes = (text) => text.replace(backslashEscapeRe, "$1");
12
+ const autolinkDisallowedRe = /[ <>]/;
13
+ const autolinkAble = (destination) => {
14
+ try {
15
+ // eslint-disable-next-line no-new
16
+ new URL(destination);
17
+ } catch {
18
+ // Not an absolute URL
19
+ return false;
20
+ }
21
+ return !autolinkDisallowedRe.test(destination);
22
+ };
23
+
24
+ module.exports = {
25
+ "names": [ "MD054", "link-image-style" ],
26
+ "description": "Link and image style",
27
+ "tags": [ "images", "links" ],
28
+ "function": (params, onError) => {
29
+ const { parsers, config } = params;
30
+ const autolink = (config.autolink === undefined) || !!config.autolink;
31
+ const inline = (config.inline === undefined) || !!config.inline;
32
+ const full = (config.full === undefined) || !!config.full;
33
+ const collapsed = (config.collapsed === undefined) || !!config.collapsed;
34
+ const shortcut = (config.shortcut === undefined) || !!config.shortcut;
35
+ const urlInline = (config.url_inline === undefined) || !!config.url_inline;
36
+ if (autolink && inline && full && collapsed && shortcut && urlInline) {
37
+ // Everything allowed, nothing to check
38
+ return;
39
+ }
40
+ const { definitions } = referenceLinkImageData();
41
+ const links = filterByTypes(
42
+ parsers.micromark.tokens,
43
+ [ "autolink", "image", "link" ]
44
+ );
45
+ for (const link of links) {
46
+ let label = null;
47
+ let destination = null;
48
+ const {
49
+ children, endColumn, endLine, startColumn, startLine, text, type
50
+ } = link;
51
+ const image = (type === "image");
52
+ let isError = false;
53
+ if (type === "autolink") {
54
+ // link kind is an autolink
55
+ destination = getTokenTextByType(children, "autolinkProtocol");
56
+ label = destination;
57
+ isError = !autolink;
58
+ } else {
59
+ // link type is "image" or "link"
60
+ const descendents = filterByPredicate(children);
61
+ label = getTokenTextByType(descendents, "labelText");
62
+ destination =
63
+ getTokenTextByType(descendents, "resourceDestinationString");
64
+ if (destination) {
65
+ // link kind is an inline link
66
+ const title = getTokenTextByType(descendents, "resourceTitleString");
67
+ isError = !inline || (
68
+ !urlInline &&
69
+ autolink &&
70
+ !image &&
71
+ !title &&
72
+ (label === destination) &&
73
+ autolinkAble(destination)
74
+ );
75
+ } else {
76
+ // link kind is a full/collapsed/shortcut reference link
77
+ const isShortcut = !children.some((t) => t.type === "reference");
78
+ const referenceString = getTokenTextByType(descendents, "referenceString");
79
+ const isCollapsed = (referenceString === null);
80
+ const definition = definitions.get(referenceString || label);
81
+ destination = definition && definition[1];
82
+ isError = destination &&
83
+ (isShortcut ? !shortcut : (isCollapsed ? !collapsed : !full));
84
+ }
85
+ }
86
+ if (isError) {
87
+ let range = null;
88
+ let fixInfo = null;
89
+ if (startLine === endLine) {
90
+ range = [ startColumn, endColumn - startColumn ];
91
+ let insertText = null;
92
+ const canInline = (inline && label);
93
+ const canAutolink = (autolink && !image && autolinkAble(destination));
94
+ if (canInline && (urlInline || !canAutolink)) {
95
+ // Most useful form
96
+ const prefix = (image ? "!" : "");
97
+ // @ts-ignore
98
+ const escapedLabel = label.replace(/[[\]]/g, "\\$&");
99
+ const escapedDestination = destination.replace(/[()]/g, "\\$&");
100
+ insertText = `${prefix}[${escapedLabel}](${escapedDestination})`;
101
+ } else if (canAutolink) {
102
+ // Simplest form
103
+ insertText = `<${removeBackslashEscapes(destination)}>`;
104
+ }
105
+ if (insertText) {
106
+ fixInfo = {
107
+ "editColumn": range[0],
108
+ insertText,
109
+ "deleteCount": range[1]
110
+ };
111
+ }
112
+ }
113
+ addErrorContext(
114
+ onError,
115
+ startLine,
116
+ text.replace(nextLinesRe, ""),
117
+ null,
118
+ null,
119
+ range,
120
+ fixInfo
121
+ );
122
+ }
123
+ }
124
+ }
125
+ };
package/lib/rules.js CHANGED
@@ -6,11 +6,9 @@ const { homepage, version } = require("./constants");
6
6
 
7
7
  const rules = [
8
8
  require("./md001"),
9
- require("./md002"),
10
9
  require("./md003"),
11
10
  require("./md004"),
12
11
  require("./md005"),
13
- require("./md006"),
14
12
  require("./md007"),
15
13
  require("./md009"),
16
14
  require("./md010"),
@@ -52,7 +50,11 @@ const rules = [
52
50
  ...require("./md049-md050"),
53
51
  require("./md051"),
54
52
  require("./md052"),
55
- require("./md053")
53
+ require("./md053"),
54
+ require("./md054")
55
+ // md055: See https://github.com/markdownlint/markdownlint
56
+ // md056: See https://github.com/markdownlint/markdownlint
57
+ // md057: See https://github.com/markdownlint/markdownlint
56
58
  ];
57
59
  for (const rule of rules) {
58
60
  const name = rule.names[0].toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint",
3
- "version": "0.31.1",
3
+ "version": "0.32.1",
4
4
  "description": "A Node.js style checker and lint tool for Markdown/CommonMark files.",
5
5
  "type": "commonjs",
6
6
  "main": "./lib/markdownlint.js",
@@ -21,11 +21,12 @@
21
21
  "url": "git+https://github.com/DavidAnson/markdownlint.git"
22
22
  },
23
23
  "bugs": "https://github.com/DavidAnson/markdownlint/issues",
24
+ "funding": "https://github.com/sponsors/DavidAnson",
24
25
  "scripts": {
25
26
  "build-config": "npm run build-config-schema && npm run build-config-example",
26
27
  "build-config-example": "node schema/build-config-example.js",
27
28
  "build-config-schema": "node schema/build-config-schema.js",
28
- "build-declaration": "tsc --allowJs --declaration --emitDeclarationOnly --module commonjs --resolveJsonModule --target es2015 lib/markdownlint.js && node scripts delete 'lib/{c,md,r}*.d.ts' 'micromark/*.d.cts' 'helpers/*.d.{cts,ts}'",
29
+ "build-declaration": "tsc --allowJs --declaration --emitDeclarationOnly --module commonjs --resolveJsonModule --target es2015 lib/markdownlint.js && node scripts delete 'lib/{cache,constants,md,r}*.d.ts' 'micromark/*.d.cts' 'helpers/*.d.{cts,ts}'",
29
30
  "build-demo": "node scripts copy node_modules/markdown-it/dist/markdown-it.min.js demo/markdown-it.min.js && node scripts copy node_modules/markdownlint-micromark/micromark-browser.js demo/micromark-browser.js && node scripts copy node_modules/markdownlint-micromark/micromark-html-browser.js demo/micromark-html-browser.js && cd demo && webpack --no-stats",
30
31
  "build-docs": "node doc-build/build-rules.mjs",
31
32
  "build-example": "npm install --no-save --ignore-scripts grunt grunt-cli gulp through2",
@@ -61,41 +62,42 @@
61
62
  "upgrade": "npx --yes npm-check-updates --upgrade"
62
63
  },
63
64
  "engines": {
64
- "node": ">=16"
65
+ "node": ">=18"
65
66
  },
66
67
  "dependencies": {
67
- "markdown-it": "13.0.1",
68
+ "markdown-it": "13.0.2",
68
69
  "markdownlint-micromark": "0.1.7"
69
70
  },
70
71
  "devDependencies": {
71
- "@babel/core": "7.22.19",
72
- "@babel/preset-env": "7.22.15",
72
+ "@babel/core": "7.23.3",
73
+ "@babel/preset-env": "7.23.3",
74
+ "@hyperjump/json-schema": "1.6.5",
73
75
  "ava": "5.3.1",
74
76
  "babel-loader": "9.1.3",
75
77
  "c8": "8.0.1",
76
78
  "character-entities": "2.0.2",
77
- "eslint": "8.49.0",
79
+ "eslint": "8.53.0",
78
80
  "eslint-plugin-es": "4.1.0",
79
- "eslint-plugin-jsdoc": "46.8.0",
80
- "eslint-plugin-n": "16.1.0",
81
- "eslint-plugin-regexp": "1.15.0",
82
- "eslint-plugin-unicorn": "48.0.1",
81
+ "eslint-plugin-jsdoc": "46.9.0",
82
+ "eslint-plugin-n": "16.3.1",
83
+ "eslint-plugin-regexp": "2.1.1",
84
+ "eslint-plugin-unicorn": "49.0.0",
83
85
  "gemoji": "8.1.0",
84
- "globby": "13.2.2",
86
+ "globby": "14.0.0",
85
87
  "js-yaml": "4.1.0",
88
+ "json-schema-to-typescript": "13.1.1",
86
89
  "markdown-it-for-inline": "0.1.1",
87
90
  "markdown-it-sub": "1.0.0",
88
91
  "markdown-it-sup": "1.0.0",
89
- "markdownlint-rule-helpers": "0.22.0",
92
+ "markdownlint-rule-helpers": "0.23.0",
90
93
  "npm-run-all": "4.1.5",
91
94
  "strip-json-comments": "5.0.1",
92
95
  "terser-webpack-plugin": "5.3.9",
93
96
  "toml": "3.0.0",
94
- "tv4": "1.3.0",
95
97
  "typescript": "5.2.2",
96
- "webpack": "5.88.2",
98
+ "webpack": "5.89.0",
97
99
  "webpack-cli": "5.1.4",
98
- "yaml": "2.3.2"
100
+ "yaml": "2.3.4"
99
101
  },
100
102
  "keywords": [
101
103
  "markdown",