markdownlint 0.39.0 → 0.40.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/CHANGELOG.md +5 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +8 -9
- package/doc/Rules.md +74 -21
- package/doc/md051.md +10 -5
- package/doc/md060.md +64 -16
- package/helpers/README.md +2 -2
- package/lib/configuration-strict.d.ts +116 -108
- package/lib/constants.mjs +1 -1
- package/lib/md011.mjs +8 -6
- package/lib/md013.mjs +14 -27
- package/lib/md060.mjs +60 -11
- package/lib/micromark-parse.mjs +2 -3
- package/package.json +11 -10
- package/schema/.markdownlint.jsonc +56 -54
- package/schema/.markdownlint.yaml +55 -53
- package/schema/markdownlint-config-schema-strict.json +120 -110
- package/schema/markdownlint-config-schema.json +120 -110
package/lib/constants.mjs
CHANGED
package/lib/md011.mjs
CHANGED
|
@@ -4,6 +4,8 @@ import { addError, hasOverlap } from "../helpers/helpers.cjs";
|
|
|
4
4
|
import { addRangeToSet } from "../helpers/micromark-helpers.cjs";
|
|
5
5
|
import { filterByTypesCached } from "./cache.mjs";
|
|
6
6
|
|
|
7
|
+
/** @typedef {import("micromark-extension-math")} */
|
|
8
|
+
|
|
7
9
|
const reversedLinkRe = /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;
|
|
8
10
|
|
|
9
11
|
/** @type {import("markdownlint").Rule} */
|
|
@@ -13,14 +15,14 @@ export default {
|
|
|
13
15
|
"tags": [ "links" ],
|
|
14
16
|
"parser": "micromark",
|
|
15
17
|
"function": function MD011(params, onError) {
|
|
16
|
-
const
|
|
17
|
-
for (const
|
|
18
|
-
addRangeToSet(
|
|
18
|
+
const ignoreBlockLineNumbers = new Set();
|
|
19
|
+
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "mathFlow" ])) {
|
|
20
|
+
addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine);
|
|
19
21
|
}
|
|
20
|
-
const
|
|
22
|
+
const ignoreTexts = filterByTypesCached([ "codeText", "mathText" ]);
|
|
21
23
|
for (const [ lineIndex, line ] of params.lines.entries()) {
|
|
22
24
|
const lineNumber = lineIndex + 1;
|
|
23
|
-
if (!
|
|
25
|
+
if (!ignoreBlockLineNumbers.has(lineNumber)) {
|
|
24
26
|
let match = null;
|
|
25
27
|
while ((match = reversedLinkRe.exec(line)) !== null) {
|
|
26
28
|
const [ reversedLink, preChar, linkText, linkDestination ] = match;
|
|
@@ -32,7 +34,7 @@ export default {
|
|
|
32
34
|
const length = match[0].length - preChar.length;
|
|
33
35
|
/** @type {import("../helpers/helpers.cjs").FileRange} */
|
|
34
36
|
const range = { "startLine": lineNumber, "startColumn": column, "endLine": lineNumber, "endColumn": column + length - 1 };
|
|
35
|
-
if (!
|
|
37
|
+
if (!ignoreTexts.some((ignoreText) => hasOverlap(ignoreText, range))) {
|
|
36
38
|
addError(
|
|
37
39
|
onError,
|
|
38
40
|
lineNumber,
|
package/lib/md013.mjs
CHANGED
|
@@ -4,10 +4,8 @@ import { addErrorDetailIf } from "../helpers/helpers.cjs";
|
|
|
4
4
|
import { filterByTypesCached, getReferenceLinkImageData } from "./cache.mjs";
|
|
5
5
|
import { addRangeToSet, getDescendantsByType } from "../helpers/micromark-helpers.cjs";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const longLineRePostfixStrict = "}.+$";
|
|
10
|
-
const sternModeRe = /^(?:[#>\s]*\s)?\S*$/;
|
|
7
|
+
// Regular expression for a line that is not wrappable
|
|
8
|
+
const notWrappableRe = /^(?:[#>\s]*\s)?\S*$/;
|
|
11
9
|
|
|
12
10
|
/** @typedef {import("micromark-extension-gfm-autolink-literal")} */
|
|
13
11
|
/** @typedef {import("micromark-extension-gfm-table")} */
|
|
@@ -20,20 +18,10 @@ export default {
|
|
|
20
18
|
"parser": "micromark",
|
|
21
19
|
"function": function MD013(params, onError) {
|
|
22
20
|
const lineLength = Number(params.config.line_length || 80);
|
|
23
|
-
const headingLineLength =
|
|
24
|
-
|
|
25
|
-
const codeLineLength =
|
|
26
|
-
Number(params.config.code_block_line_length || lineLength);
|
|
21
|
+
const headingLineLength = Number(params.config.heading_line_length || lineLength);
|
|
22
|
+
const codeLineLength = Number(params.config.code_block_line_length || lineLength);
|
|
27
23
|
const strict = !!params.config.strict;
|
|
28
24
|
const stern = !!params.config.stern;
|
|
29
|
-
const longLineRePostfix =
|
|
30
|
-
(strict || stern) ? longLineRePostfixStrict : longLineRePostfixRelaxed;
|
|
31
|
-
const longLineRe =
|
|
32
|
-
new RegExp(longLineRePrefix + lineLength + longLineRePostfix);
|
|
33
|
-
const longHeadingLineRe =
|
|
34
|
-
new RegExp(longLineRePrefix + headingLineLength + longLineRePostfix);
|
|
35
|
-
const longCodeLineRe =
|
|
36
|
-
new RegExp(longLineRePrefix + codeLineLength + longLineRePostfix);
|
|
37
25
|
const codeBlocks = params.config.code_blocks;
|
|
38
26
|
const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
|
|
39
27
|
const tables = params.config.tables;
|
|
@@ -75,28 +63,27 @@ export default {
|
|
|
75
63
|
const isHeading = headingLineNumbers.has(lineNumber);
|
|
76
64
|
const inCode = codeBlockLineNumbers.has(lineNumber);
|
|
77
65
|
const inTable = tableLineNumbers.has(lineNumber);
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if ((includeCodeBlocks || !inCode) &&
|
|
66
|
+
const maxLength = inCode ? codeLineLength : (isHeading ? headingLineLength : lineLength);
|
|
67
|
+
// If not strict/stern, the last run of non-whitespace is allowed to go
|
|
68
|
+
// beyond the limit as long as it begins within the limit
|
|
69
|
+
const text = (strict || stern) ? line : line.replace(/\S*$/u, "#");
|
|
70
|
+
if ((maxLength > 0) &&
|
|
71
|
+
(includeCodeBlocks || !inCode) &&
|
|
85
72
|
(includeTables || !inTable) &&
|
|
86
73
|
(includeHeadings || !isHeading) &&
|
|
87
74
|
!definitionLineIndices.has(lineIndex) &&
|
|
88
75
|
(strict ||
|
|
89
|
-
(!(stern &&
|
|
76
|
+
(!(stern && notWrappableRe.test(line)) &&
|
|
90
77
|
!linkOnlyLineNumbers.has(lineNumber))) &&
|
|
91
|
-
|
|
78
|
+
(text.length > maxLength)) {
|
|
92
79
|
addErrorDetailIf(
|
|
93
80
|
onError,
|
|
94
81
|
lineNumber,
|
|
95
|
-
|
|
82
|
+
maxLength,
|
|
96
83
|
line.length,
|
|
97
84
|
undefined,
|
|
98
85
|
undefined,
|
|
99
|
-
[
|
|
86
|
+
[ maxLength + 1, line.length - maxLength ]
|
|
100
87
|
);
|
|
101
88
|
}
|
|
102
89
|
}
|
package/lib/md060.mjs
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { filterByTypes } from "../helpers/micromark-helpers.cjs";
|
|
4
4
|
import { filterByTypesCached } from "./cache.mjs";
|
|
5
|
+
import stringWidth from "string-width";
|
|
5
6
|
|
|
6
7
|
/** @typedef {import("micromark-extension-gfm-table")} */
|
|
8
|
+
/** @typedef {import("markdownlint").MicromarkToken} MicromarkToken */
|
|
7
9
|
/** @typedef {import("markdownlint").RuleOnErrorInfo} RuleOnErrorInfo */
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -22,6 +24,56 @@ function addError(errors, lineNumber, column, detail) {
|
|
|
22
24
|
});
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* @typedef Column
|
|
29
|
+
* @property {number} actual Actual column (1-based).
|
|
30
|
+
* @property {number} effective Effective column (1-based).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Gets a list of table cell divider columns.
|
|
35
|
+
*
|
|
36
|
+
* @param {readonly string[]} lines File/string lines.
|
|
37
|
+
* @param {MicromarkToken} row Micromark row token.
|
|
38
|
+
* @returns {Column[]} Divider columns.
|
|
39
|
+
*/
|
|
40
|
+
function getTableDividerColumns(lines, row) {
|
|
41
|
+
return filterByTypes(
|
|
42
|
+
row.children,
|
|
43
|
+
[ "tableCellDivider" ]
|
|
44
|
+
).map(
|
|
45
|
+
(divider) => ({
|
|
46
|
+
"actual": divider.startColumn,
|
|
47
|
+
"effective": stringWidth(lines[row.startLine - 1].slice(0, divider.startColumn - 1))
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Checks the specified table rows for consistency with the "aligned" style.
|
|
54
|
+
*
|
|
55
|
+
* @param {readonly string[]} lines File/string lines.
|
|
56
|
+
* @param {MicromarkToken[]} rows Micromark row tokens.
|
|
57
|
+
* @param {string} detail Detail message.
|
|
58
|
+
* @returns {RuleOnErrorInfo[]} List of errors.
|
|
59
|
+
*/
|
|
60
|
+
function checkStyleAligned(lines, rows, detail) {
|
|
61
|
+
/** @type {RuleOnErrorInfo[]} */
|
|
62
|
+
const errorInfos = [];
|
|
63
|
+
const headerRow = rows[0];
|
|
64
|
+
const headerDividerColumns = getTableDividerColumns(lines, headerRow);
|
|
65
|
+
for (const row of rows.slice(1)) {
|
|
66
|
+
const remainingHeaderDividerColumns = new Set(headerDividerColumns.map((column) => column.effective));
|
|
67
|
+
const rowDividerColumns = getTableDividerColumns(lines, row);
|
|
68
|
+
for (const dividerColumn of rowDividerColumns) {
|
|
69
|
+
if ((remainingHeaderDividerColumns.size > 0) && !remainingHeaderDividerColumns.delete(dividerColumn.effective)) {
|
|
70
|
+
addError(errorInfos, row.startLine, dividerColumn.actual, detail);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return errorInfos;
|
|
75
|
+
}
|
|
76
|
+
|
|
25
77
|
/** @type {import("markdownlint").Rule} */
|
|
26
78
|
export default {
|
|
27
79
|
"names": [ "MD060", "table-column-style" ],
|
|
@@ -33,27 +85,19 @@ export default {
|
|
|
33
85
|
const styleAlignedAllowed = (style === "any") || (style === "aligned");
|
|
34
86
|
const styleCompactAllowed = (style === "any") || (style === "compact");
|
|
35
87
|
const styleTightAllowed = (style === "any") || (style === "tight");
|
|
88
|
+
const alignedDelimiter = !!params.config.aligned_delimiter;
|
|
89
|
+
const lines = params.lines;
|
|
36
90
|
|
|
37
91
|
// Scan all tables/rows
|
|
38
92
|
const tables = filterByTypesCached([ "table" ]);
|
|
39
93
|
for (const table of tables) {
|
|
40
94
|
const rows = filterByTypes(table.children, [ "tableDelimiterRow", "tableRow" ]);
|
|
41
|
-
const headingRow = rows[0];
|
|
42
95
|
|
|
43
96
|
// Determine errors for style "aligned"
|
|
44
97
|
/** @type {RuleOnErrorInfo[]} */
|
|
45
98
|
const errorsIfAligned = [];
|
|
46
99
|
if (styleAlignedAllowed) {
|
|
47
|
-
|
|
48
|
-
for (const row of rows.slice(1)) {
|
|
49
|
-
const remainingHeadingDividerColumns = new Set(headingDividerColumns);
|
|
50
|
-
const rowDividerColumns = filterByTypes(row.children, [ "tableCellDivider" ]).map((divider) => divider.startColumn);
|
|
51
|
-
for (const dividerColumn of rowDividerColumns) {
|
|
52
|
-
if ((remainingHeadingDividerColumns.size > 0) && !remainingHeadingDividerColumns.delete(dividerColumn)) {
|
|
53
|
-
addError(errorsIfAligned, row.startLine, dividerColumn, "Table pipe does not align with heading for style \"aligned\"");
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
100
|
+
errorsIfAligned.push(...checkStyleAligned(lines, rows, "Table pipe does not align with header for style \"aligned\""));
|
|
57
101
|
}
|
|
58
102
|
|
|
59
103
|
// Determine errors for styles "compact" and "tight"
|
|
@@ -65,6 +109,11 @@ export default {
|
|
|
65
109
|
(styleCompactAllowed || styleTightAllowed) &&
|
|
66
110
|
!(styleAlignedAllowed && (errorsIfAligned.length === 0))
|
|
67
111
|
) {
|
|
112
|
+
if (alignedDelimiter) {
|
|
113
|
+
const errorInfos = checkStyleAligned(lines, rows.slice(0, 2), "Table pipe does not align with header for option \"aligned_delimiter\"");
|
|
114
|
+
errorsIfCompact.push(...errorInfos);
|
|
115
|
+
errorsIfTight.push(...errorInfos);
|
|
116
|
+
}
|
|
68
117
|
for (const row of rows) {
|
|
69
118
|
const tokensOfInterest = filterByTypes(row.children, [ "tableCellDivider", "tableContent", "whitespace" ]);
|
|
70
119
|
for (let i = 0; i < tokensOfInterest.length; i++) {
|
package/lib/micromark-parse.mjs
CHANGED
|
@@ -142,11 +142,10 @@ export function getEvents(
|
|
|
142
142
|
.trim();
|
|
143
143
|
if ((text.length > 0) && !text.includes("]")) {
|
|
144
144
|
/** @type {Event[]} */
|
|
145
|
-
const artificialEvents = [
|
|
146
|
-
artificialEvents.push(
|
|
145
|
+
const artificialEvents = [
|
|
147
146
|
[ "enter", undefinedReferenceType, tokenizeContext ],
|
|
148
147
|
[ "enter", undefinedReference, tokenizeContext ]
|
|
149
|
-
|
|
148
|
+
];
|
|
150
149
|
for (const event of eventsToReplicate) {
|
|
151
150
|
const [ kind, token ] = event;
|
|
152
151
|
// Copy token because the current object will get modified by the parser
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "markdownlint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "A Node.js style checker and lint tool for Markdown/CommonMark files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -80,23 +80,24 @@
|
|
|
80
80
|
"micromark-extension-gfm-footnote": "2.1.0",
|
|
81
81
|
"micromark-extension-gfm-table": "2.1.1",
|
|
82
82
|
"micromark-extension-math": "3.1.0",
|
|
83
|
-
"micromark-util-types": "2.0.2"
|
|
83
|
+
"micromark-util-types": "2.0.2",
|
|
84
|
+
"string-width": "8.1.0"
|
|
84
85
|
},
|
|
85
86
|
"devDependencies": {
|
|
86
|
-
"@eslint/js": "9.
|
|
87
|
-
"@stylistic/eslint-plugin": "5.
|
|
87
|
+
"@eslint/js": "9.39.1",
|
|
88
|
+
"@stylistic/eslint-plugin": "5.6.1",
|
|
88
89
|
"ajv": "8.17.1",
|
|
89
90
|
"ava": "6.4.1",
|
|
90
91
|
"c8": "10.1.3",
|
|
91
92
|
"character-entities": "2.0.2",
|
|
92
|
-
"eslint": "9.
|
|
93
|
-
"eslint-plugin-jsdoc": "61.
|
|
93
|
+
"eslint": "9.39.1",
|
|
94
|
+
"eslint-plugin-jsdoc": "61.4.1",
|
|
94
95
|
"eslint-plugin-n": "17.23.1",
|
|
95
96
|
"eslint-plugin-regexp": "2.10.0",
|
|
96
|
-
"eslint-plugin-unicorn": "
|
|
97
|
+
"eslint-plugin-unicorn": "62.0.0",
|
|
97
98
|
"gemoji": "8.1.0",
|
|
98
|
-
"globby": "
|
|
99
|
-
"js-yaml": "4.1.
|
|
99
|
+
"globby": "16.0.0",
|
|
100
|
+
"js-yaml": "4.1.1",
|
|
100
101
|
"json-schema-to-typescript": "15.0.4",
|
|
101
102
|
"jsonc-parser": "3.3.1",
|
|
102
103
|
"markdown-it": "14.1.0",
|
|
@@ -109,7 +110,7 @@
|
|
|
109
110
|
"terser-webpack-plugin": "5.3.14",
|
|
110
111
|
"toml": "3.0.0",
|
|
111
112
|
"typescript": "5.9.3",
|
|
112
|
-
"webpack": "5.
|
|
113
|
+
"webpack": "5.103.0",
|
|
113
114
|
"webpack-cli": "6.0.1"
|
|
114
115
|
},
|
|
115
116
|
"keywords": [
|