markdownlint 0.25.0 → 0.26.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.
- package/CONTRIBUTING.md +2 -0
- package/LICENSE +1 -1
- package/README.md +125 -83
- package/demo/markdownlint-browser.js +1719 -1269
- package/doc/Rules.md +133 -15
- package/helpers/LICENSE +1 -1
- package/helpers/helpers.js +427 -192
- package/helpers/package.json +6 -2
- package/lib/cache.js +15 -25
- package/lib/constants.js +1 -1
- package/lib/markdownlint.d.ts +21 -17
- package/lib/markdownlint.js +237 -146
- package/lib/md004.js +4 -4
- package/lib/md005.js +4 -4
- package/lib/md006.js +4 -4
- package/lib/md007.js +4 -4
- package/lib/md009.js +20 -13
- package/lib/md010.js +15 -2
- package/lib/md011.js +2 -2
- package/lib/md013.js +4 -5
- package/lib/md014.js +4 -4
- package/lib/md021.js +3 -1
- package/lib/md026.js +1 -1
- package/lib/md027.js +2 -2
- package/lib/md028.js +2 -2
- package/lib/md029.js +5 -4
- package/lib/md030.js +4 -4
- package/lib/md031.js +2 -2
- package/lib/md032.js +3 -2
- package/lib/md033.js +2 -3
- package/lib/md034.js +2 -2
- package/lib/md035.js +7 -2
- package/lib/md036.js +2 -2
- package/lib/md038.js +7 -4
- package/lib/md039.js +6 -4
- package/lib/md042.js +17 -10
- package/lib/md043.js +2 -2
- package/lib/md044.js +20 -9
- package/lib/md046.js +14 -13
- package/lib/md048.js +13 -14
- package/lib/md049-md050.js +73 -0
- package/lib/md051.js +87 -0
- package/lib/md052.js +35 -0
- package/lib/md053.js +52 -0
- package/lib/rules.js +6 -4
- package/package.json +36 -29
- package/schema/.markdownlint.jsonc +15 -2
- package/schema/.markdownlint.yaml +14 -1
- package/schema/markdownlint-config-schema.json +119 -737
- package/lib/md049.js +0 -45
- package/lib/md050.js +0 -45
package/lib/markdownlint.js
CHANGED
|
@@ -20,7 +20,7 @@ const dynamicRequire = (typeof __non_webpack_require__ === "undefined") ? requir
|
|
|
20
20
|
*
|
|
21
21
|
* @param {Rule[]} ruleList List of rules.
|
|
22
22
|
* @param {boolean} synchronous Whether to execute synchronously.
|
|
23
|
-
* @returns {
|
|
23
|
+
* @returns {Error | null} Error message if validation fails.
|
|
24
24
|
*/
|
|
25
25
|
function validateRuleList(ruleList, synchronous) {
|
|
26
26
|
let result = null;
|
|
@@ -29,32 +29,32 @@ function validateRuleList(ruleList, synchronous) {
|
|
|
29
29
|
return result;
|
|
30
30
|
}
|
|
31
31
|
const allIds = {};
|
|
32
|
-
|
|
32
|
+
for (const [ index, rule ] of ruleList.entries()) {
|
|
33
33
|
const customIndex = index - rules.length;
|
|
34
|
-
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
34
|
+
// eslint-disable-next-line no-inner-declarations, jsdoc/require-jsdoc
|
|
35
35
|
function newError(property) {
|
|
36
36
|
return new Error(
|
|
37
37
|
"Property '" + property + "' of custom rule at index " +
|
|
38
38
|
customIndex + " is incorrect.");
|
|
39
39
|
}
|
|
40
|
-
[ "names", "tags" ]
|
|
40
|
+
for (const property of [ "names", "tags" ]) {
|
|
41
41
|
const value = rule[property];
|
|
42
42
|
if (!result &&
|
|
43
43
|
(!value || !Array.isArray(value) || (value.length === 0) ||
|
|
44
44
|
!value.every(helpers.isString) || value.some(helpers.isEmptyString))) {
|
|
45
45
|
result = newError(property);
|
|
46
46
|
}
|
|
47
|
-
}
|
|
48
|
-
[
|
|
47
|
+
}
|
|
48
|
+
for (const propertyInfo of [
|
|
49
49
|
[ "description", "string" ],
|
|
50
50
|
[ "function", "function" ]
|
|
51
|
-
]
|
|
51
|
+
]) {
|
|
52
52
|
const property = propertyInfo[0];
|
|
53
53
|
const value = rule[property];
|
|
54
54
|
if (!result && (!value || (typeof value !== propertyInfo[1]))) {
|
|
55
55
|
result = newError(property);
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|
|
58
58
|
if (
|
|
59
59
|
!result &&
|
|
60
60
|
rule.information &&
|
|
@@ -76,24 +76,24 @@ function validateRuleList(ruleList, synchronous) {
|
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
if (!result) {
|
|
79
|
-
rule.names
|
|
79
|
+
for (const name of rule.names) {
|
|
80
80
|
const nameUpper = name.toUpperCase();
|
|
81
81
|
if (!result && (allIds[nameUpper] !== undefined)) {
|
|
82
82
|
result = new Error("Name '" + name + "' of custom rule at index " +
|
|
83
83
|
customIndex + " is already used as a name or tag.");
|
|
84
84
|
}
|
|
85
85
|
allIds[nameUpper] = true;
|
|
86
|
-
}
|
|
87
|
-
rule.tags
|
|
86
|
+
}
|
|
87
|
+
for (const tag of rule.tags) {
|
|
88
88
|
const tagUpper = tag.toUpperCase();
|
|
89
89
|
if (!result && allIds[tagUpper]) {
|
|
90
90
|
result = new Error("Tag '" + tag + "' of custom rule at index " +
|
|
91
91
|
customIndex + " is already used as a name.");
|
|
92
92
|
}
|
|
93
93
|
allIds[tagUpper] = false;
|
|
94
|
-
}
|
|
94
|
+
}
|
|
95
95
|
}
|
|
96
|
-
}
|
|
96
|
+
}
|
|
97
97
|
return result;
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -111,10 +111,10 @@ function newResults(ruleList) {
|
|
|
111
111
|
const results = [];
|
|
112
112
|
const keys = Object.keys(lintResults);
|
|
113
113
|
keys.sort();
|
|
114
|
-
|
|
114
|
+
for (const file of keys) {
|
|
115
115
|
const fileResults = lintResults[file];
|
|
116
116
|
if (Array.isArray(fileResults)) {
|
|
117
|
-
|
|
117
|
+
for (const result of fileResults) {
|
|
118
118
|
const ruleMoniker = result.ruleNames ?
|
|
119
119
|
result.ruleNames.join("/") :
|
|
120
120
|
(result.ruleName + "/" + result.ruleAlias);
|
|
@@ -129,30 +129,32 @@ function newResults(ruleList) {
|
|
|
129
129
|
(result.errorContext ?
|
|
130
130
|
" [Context: \"" + result.errorContext + "\"]" :
|
|
131
131
|
""));
|
|
132
|
-
}
|
|
132
|
+
}
|
|
133
133
|
} else {
|
|
134
134
|
if (!ruleNameToRule) {
|
|
135
135
|
ruleNameToRule = {};
|
|
136
|
-
|
|
136
|
+
for (const rule of ruleList) {
|
|
137
137
|
const ruleName = rule.names[0].toUpperCase();
|
|
138
138
|
ruleNameToRule[ruleName] = rule;
|
|
139
|
-
}
|
|
139
|
+
}
|
|
140
140
|
}
|
|
141
|
-
Object.
|
|
141
|
+
for (const [ ruleName, ruleResults ] of Object.entries(fileResults)) {
|
|
142
142
|
const rule = ruleNameToRule[ruleName.toUpperCase()];
|
|
143
|
-
const ruleResults
|
|
144
|
-
|
|
143
|
+
for (const lineNumber of ruleResults) {
|
|
144
|
+
// @ts-ignore
|
|
145
145
|
const nameIndex = Math.min(useAlias ? 1 : 0, rule.names.length - 1);
|
|
146
146
|
const result =
|
|
147
147
|
file + ": " +
|
|
148
148
|
lineNumber + ": " +
|
|
149
|
+
// @ts-ignore
|
|
149
150
|
rule.names[nameIndex] + " " +
|
|
151
|
+
// @ts-ignore
|
|
150
152
|
rule.description;
|
|
151
153
|
results.push(result);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
154
156
|
}
|
|
155
|
-
}
|
|
157
|
+
}
|
|
156
158
|
return results.join("\n");
|
|
157
159
|
}
|
|
158
160
|
Object.defineProperty(lintResults, "toString", { "value": toString });
|
|
@@ -188,15 +190,40 @@ function removeFrontMatter(content, frontMatter) {
|
|
|
188
190
|
}
|
|
189
191
|
|
|
190
192
|
/**
|
|
191
|
-
*
|
|
193
|
+
* Freeze all freeze-able members of a token and its children.
|
|
194
|
+
*
|
|
195
|
+
* @param {MarkdownItToken} token A markdown-it token.
|
|
196
|
+
* @returns {void}
|
|
197
|
+
*/
|
|
198
|
+
function freezeToken(token) {
|
|
199
|
+
if (token.attrs) {
|
|
200
|
+
for (const attr of token.attrs) {
|
|
201
|
+
Object.freeze(attr);
|
|
202
|
+
}
|
|
203
|
+
Object.freeze(token.attrs);
|
|
204
|
+
}
|
|
205
|
+
if (token.children) {
|
|
206
|
+
for (const child of token.children) {
|
|
207
|
+
freezeToken(child);
|
|
208
|
+
}
|
|
209
|
+
Object.freeze(token.children);
|
|
210
|
+
}
|
|
211
|
+
if (token.map) {
|
|
212
|
+
Object.freeze(token.map);
|
|
213
|
+
}
|
|
214
|
+
Object.freeze(token);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Annotate tokens with line/lineNumber and freeze them.
|
|
192
219
|
*
|
|
193
220
|
* @param {MarkdownItToken[]} tokens Array of markdown-it tokens.
|
|
194
221
|
* @param {string[]} lines Lines of Markdown content.
|
|
195
222
|
* @returns {void}
|
|
196
223
|
*/
|
|
197
|
-
function
|
|
224
|
+
function annotateAndFreezeTokens(tokens, lines) {
|
|
198
225
|
let trMap = null;
|
|
199
|
-
|
|
226
|
+
for (const token of tokens) {
|
|
200
227
|
// Provide missing maps for table content
|
|
201
228
|
if (token.type === "tr_open") {
|
|
202
229
|
trMap = token.map;
|
|
@@ -219,16 +246,17 @@ function annotateTokens(tokens, lines) {
|
|
|
219
246
|
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
|
220
247
|
token.map[1]--;
|
|
221
248
|
}
|
|
222
|
-
|
|
223
|
-
|
|
249
|
+
}
|
|
250
|
+
// Annotate children with lineNumber
|
|
251
|
+
if (token.children) {
|
|
224
252
|
const codeSpanExtraLines = [];
|
|
225
|
-
|
|
226
|
-
token.content,
|
|
227
|
-
function handleInlineCodeSpan(code) {
|
|
253
|
+
if (token.children.some((child) => child.type === "code_inline")) {
|
|
254
|
+
helpers.forEachInlineCodeSpan(token.content, (code) => {
|
|
228
255
|
codeSpanExtraLines.push(code.split(helpers.newLineRe).length - 1);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
let lineNumber = token.lineNumber;
|
|
259
|
+
for (const child of token.children) {
|
|
232
260
|
child.lineNumber = lineNumber;
|
|
233
261
|
child.line = lines[lineNumber - 1];
|
|
234
262
|
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
|
|
@@ -236,9 +264,11 @@ function annotateTokens(tokens, lines) {
|
|
|
236
264
|
} else if (child.type === "code_inline") {
|
|
237
265
|
lineNumber += codeSpanExtraLines.shift();
|
|
238
266
|
}
|
|
239
|
-
}
|
|
267
|
+
}
|
|
240
268
|
}
|
|
241
|
-
|
|
269
|
+
freezeToken(token);
|
|
270
|
+
}
|
|
271
|
+
Object.freeze(tokens);
|
|
242
272
|
}
|
|
243
273
|
|
|
244
274
|
/**
|
|
@@ -250,24 +280,24 @@ function annotateTokens(tokens, lines) {
|
|
|
250
280
|
function mapAliasToRuleNames(ruleList) {
|
|
251
281
|
const aliasToRuleNames = {};
|
|
252
282
|
// const tagToRuleNames = {};
|
|
253
|
-
|
|
283
|
+
for (const rule of ruleList) {
|
|
254
284
|
const ruleName = rule.names[0].toUpperCase();
|
|
255
285
|
// The following is useful for updating README.md:
|
|
256
286
|
// console.log(
|
|
257
287
|
// "* **[" + ruleName + "](doc/Rules.md#" + ruleName.toLowerCase() +
|
|
258
288
|
// ")** *" + rule.names.slice(1).join("/") + "* - " + rule.description);
|
|
259
|
-
rule.names
|
|
289
|
+
for (const name of rule.names) {
|
|
260
290
|
const nameUpper = name.toUpperCase();
|
|
261
291
|
aliasToRuleNames[nameUpper] = [ ruleName ];
|
|
262
|
-
}
|
|
263
|
-
rule.tags
|
|
292
|
+
}
|
|
293
|
+
for (const tag of rule.tags) {
|
|
264
294
|
const tagUpper = tag.toUpperCase();
|
|
265
295
|
const ruleNames = aliasToRuleNames[tagUpper] || [];
|
|
266
296
|
ruleNames.push(ruleName);
|
|
267
297
|
aliasToRuleNames[tagUpper] = ruleNames;
|
|
268
298
|
// tagToRuleNames[tag] = ruleName;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
271
301
|
// The following is useful for updating README.md:
|
|
272
302
|
// Object.keys(tagToRuleNames).sort().forEach(function forTag(tag) {
|
|
273
303
|
// console.log("* **" + tag + "** - " +
|
|
@@ -292,14 +322,14 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
292
322
|
);
|
|
293
323
|
const ruleDefault = (defaultKey.length === 0) || !!config[defaultKey[0]];
|
|
294
324
|
const effectiveConfig = {};
|
|
295
|
-
|
|
325
|
+
for (const rule of ruleList) {
|
|
296
326
|
const ruleName = rule.names[0].toUpperCase();
|
|
297
327
|
effectiveConfig[ruleName] = ruleDefault;
|
|
298
|
-
}
|
|
299
|
-
|
|
328
|
+
}
|
|
329
|
+
for (const ruleName of deprecatedRuleNames) {
|
|
300
330
|
effectiveConfig[ruleName] = false;
|
|
301
|
-
}
|
|
302
|
-
Object.keys(config)
|
|
331
|
+
}
|
|
332
|
+
for (const key of Object.keys(config)) {
|
|
303
333
|
let value = config[key];
|
|
304
334
|
if (value) {
|
|
305
335
|
if (!(value instanceof Object)) {
|
|
@@ -309,13 +339,46 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
309
339
|
value = false;
|
|
310
340
|
}
|
|
311
341
|
const keyUpper = key.toUpperCase();
|
|
312
|
-
(aliasToRuleNames[keyUpper] || [])
|
|
342
|
+
for (const ruleName of (aliasToRuleNames[keyUpper] || [])) {
|
|
313
343
|
effectiveConfig[ruleName] = value;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
316
346
|
return effectiveConfig;
|
|
317
347
|
}
|
|
318
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Parse the content of a configuration file.
|
|
351
|
+
*
|
|
352
|
+
* @param {string} name Name of the configuration file.
|
|
353
|
+
* @param {string} content Configuration content.
|
|
354
|
+
* @param {ConfigurationParser[] | null} [parsers] Parsing function(s).
|
|
355
|
+
* @returns {Object} Configuration object and error message.
|
|
356
|
+
*/
|
|
357
|
+
function parseConfiguration(name, content, parsers) {
|
|
358
|
+
let config = null;
|
|
359
|
+
let message = "";
|
|
360
|
+
const errors = [];
|
|
361
|
+
let index = 0;
|
|
362
|
+
// Try each parser
|
|
363
|
+
(parsers || [ JSON.parse ]).every((parser) => {
|
|
364
|
+
try {
|
|
365
|
+
config = parser(content);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
errors.push(`Parser ${index++}: ${error.message}`);
|
|
368
|
+
}
|
|
369
|
+
return !config;
|
|
370
|
+
});
|
|
371
|
+
// Message if unable to parse
|
|
372
|
+
if (!config) {
|
|
373
|
+
errors.unshift(`Unable to parse '${name}'`);
|
|
374
|
+
message = errors.join("; ");
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
config,
|
|
378
|
+
message
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
319
382
|
/**
|
|
320
383
|
* Create a mapping of enabled rules per line.
|
|
321
384
|
*
|
|
@@ -324,6 +387,7 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
324
387
|
* @param {string[]} frontMatterLines List of front matter lines.
|
|
325
388
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
326
389
|
* @param {Configuration} config Configuration object.
|
|
390
|
+
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
|
327
391
|
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
|
328
392
|
* names.
|
|
329
393
|
* @returns {Object} Effective configuration and enabled rules per line number.
|
|
@@ -334,6 +398,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
334
398
|
frontMatterLines,
|
|
335
399
|
noInlineConfig,
|
|
336
400
|
config,
|
|
401
|
+
configParsers,
|
|
337
402
|
aliasToRuleNames) {
|
|
338
403
|
// Shared variables
|
|
339
404
|
let enabledRules = {};
|
|
@@ -343,31 +408,36 @@ function getEnabledRulesPerLineNumber(
|
|
|
343
408
|
// Helper functions
|
|
344
409
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
345
410
|
function handleInlineConfig(input, forEachMatch, forEachLine) {
|
|
346
|
-
input.
|
|
411
|
+
for (const [ lineIndex, line ] of input.entries()) {
|
|
347
412
|
if (!noInlineConfig) {
|
|
348
413
|
let match = null;
|
|
349
|
-
while ((match = helpers.
|
|
350
|
-
const action =
|
|
351
|
-
const
|
|
414
|
+
while ((match = helpers.inlineCommentStartRe.exec(line))) {
|
|
415
|
+
const action = match[2].toUpperCase();
|
|
416
|
+
const startIndex = match.index + match[1].length;
|
|
417
|
+
const endIndex = line.indexOf("-->", startIndex);
|
|
418
|
+
if (endIndex === -1) {
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
const parameter = line.slice(startIndex, endIndex);
|
|
352
422
|
forEachMatch(action, parameter, lineIndex + 1);
|
|
353
423
|
}
|
|
354
424
|
}
|
|
355
425
|
if (forEachLine) {
|
|
356
426
|
forEachLine();
|
|
357
427
|
}
|
|
358
|
-
}
|
|
428
|
+
}
|
|
359
429
|
}
|
|
360
430
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
361
431
|
function configureFile(action, parameter) {
|
|
362
432
|
if (action === "CONFIGURE-FILE") {
|
|
363
|
-
|
|
364
|
-
|
|
433
|
+
const { "config": parsed } = parseConfiguration(
|
|
434
|
+
"CONFIGURE-FILE", parameter, configParsers
|
|
435
|
+
);
|
|
436
|
+
if (parsed) {
|
|
365
437
|
config = {
|
|
366
438
|
...config,
|
|
367
|
-
...
|
|
439
|
+
...parsed
|
|
368
440
|
};
|
|
369
|
-
} catch {
|
|
370
|
-
// Ignore parse errors for inline configuration
|
|
371
441
|
}
|
|
372
442
|
}
|
|
373
443
|
}
|
|
@@ -375,14 +445,13 @@ function getEnabledRulesPerLineNumber(
|
|
|
375
445
|
function applyEnableDisable(action, parameter, state) {
|
|
376
446
|
state = { ...state };
|
|
377
447
|
const enabled = (action.startsWith("ENABLE"));
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
(aliasToRuleNames[nameUpper] || []).forEach((ruleName) => {
|
|
448
|
+
const trimmed = parameter && parameter.trim();
|
|
449
|
+
const items = trimmed ? trimmed.toUpperCase().split(/\s+/) : allRuleNames;
|
|
450
|
+
for (const nameUpper of items) {
|
|
451
|
+
for (const ruleName of (aliasToRuleNames[nameUpper] || [])) {
|
|
383
452
|
state[ruleName] = enabled;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
386
455
|
return state;
|
|
387
456
|
}
|
|
388
457
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
@@ -406,9 +475,12 @@ function getEnabledRulesPerLineNumber(
|
|
|
406
475
|
enabledRulesPerLineNumber.push(enabledRules);
|
|
407
476
|
}
|
|
408
477
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
409
|
-
function
|
|
410
|
-
|
|
411
|
-
|
|
478
|
+
function disableLineNextLine(action, parameter, lineNumber) {
|
|
479
|
+
const disableLine = (action === "DISABLE-LINE");
|
|
480
|
+
const disableNextLine = (action === "DISABLE-NEXT-LINE");
|
|
481
|
+
if (disableLine || disableNextLine) {
|
|
482
|
+
const nextLineNumber =
|
|
483
|
+
frontMatterLines.length + lineNumber + (disableNextLine ? 1 : 0);
|
|
412
484
|
enabledRulesPerLineNumber[nextLineNumber] =
|
|
413
485
|
applyEnableDisable(
|
|
414
486
|
action,
|
|
@@ -421,15 +493,15 @@ function getEnabledRulesPerLineNumber(
|
|
|
421
493
|
handleInlineConfig([ lines.join("\n") ], configureFile);
|
|
422
494
|
const effectiveConfig = getEffectiveConfig(
|
|
423
495
|
ruleList, config, aliasToRuleNames);
|
|
424
|
-
|
|
496
|
+
for (const rule of ruleList) {
|
|
425
497
|
const ruleName = rule.names[0].toUpperCase();
|
|
426
498
|
allRuleNames.push(ruleName);
|
|
427
499
|
enabledRules[ruleName] = !!effectiveConfig[ruleName];
|
|
428
|
-
}
|
|
500
|
+
}
|
|
429
501
|
capturedRules = enabledRules;
|
|
430
502
|
handleInlineConfig(lines, enableDisableFile);
|
|
431
503
|
handleInlineConfig(lines, captureRestoreEnableDisable, updateLineState);
|
|
432
|
-
handleInlineConfig(lines,
|
|
504
|
+
handleInlineConfig(lines, disableLineNextLine);
|
|
433
505
|
// Return results
|
|
434
506
|
return {
|
|
435
507
|
effectiveConfig,
|
|
@@ -445,6 +517,7 @@ function getEnabledRulesPerLineNumber(
|
|
|
445
517
|
* @param {string} content Markdown content.
|
|
446
518
|
* @param {Object} md Instance of markdown-it.
|
|
447
519
|
* @param {Configuration} config Configuration object.
|
|
520
|
+
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
|
448
521
|
* @param {RegExp} frontMatter Regular expression for front matter.
|
|
449
522
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
450
523
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
@@ -458,6 +531,7 @@ function lintContent(
|
|
|
458
531
|
content,
|
|
459
532
|
md,
|
|
460
533
|
config,
|
|
534
|
+
configParsers,
|
|
461
535
|
frontMatter,
|
|
462
536
|
handleRuleFailures,
|
|
463
537
|
noInlineConfig,
|
|
@@ -467,42 +541,59 @@ function lintContent(
|
|
|
467
541
|
content = content.replace(/^\uFEFF/, "");
|
|
468
542
|
// Remove front matter
|
|
469
543
|
const removeFrontMatterResult = removeFrontMatter(content, frontMatter);
|
|
470
|
-
const frontMatterLines = removeFrontMatterResult
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
// Parse content into tokens and lines
|
|
474
|
-
const tokens = md.parse(content, {});
|
|
475
|
-
const lines = content.split(helpers.newLineRe);
|
|
476
|
-
annotateTokens(tokens, lines);
|
|
477
|
-
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
|
544
|
+
const { frontMatterLines } = removeFrontMatterResult;
|
|
545
|
+
content = removeFrontMatterResult.content;
|
|
546
|
+
// Get enabled rules per line (with HTML comments present)
|
|
478
547
|
const { effectiveConfig, enabledRulesPerLineNumber } =
|
|
479
548
|
getEnabledRulesPerLineNumber(
|
|
480
549
|
ruleList,
|
|
481
|
-
|
|
550
|
+
content.split(helpers.newLineRe),
|
|
482
551
|
frontMatterLines,
|
|
483
552
|
noInlineConfig,
|
|
484
553
|
config,
|
|
485
|
-
|
|
554
|
+
configParsers,
|
|
555
|
+
mapAliasToRuleNames(ruleList)
|
|
486
556
|
);
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
557
|
+
// Hide the content of HTML comments from rules, etc.
|
|
558
|
+
content = helpers.clearHtmlCommentText(content);
|
|
559
|
+
// Parse content into tokens and lines
|
|
560
|
+
const tokens = md.parse(content, {});
|
|
561
|
+
const lines = content.split(helpers.newLineRe);
|
|
562
|
+
annotateAndFreezeTokens(tokens, lines);
|
|
563
|
+
// Create (frozen) parameters for rules
|
|
564
|
+
const paramsBase = {
|
|
565
|
+
name,
|
|
566
|
+
tokens,
|
|
567
|
+
"lines": Object.freeze(lines),
|
|
568
|
+
"frontMatterLines": Object.freeze(frontMatterLines)
|
|
493
569
|
};
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
helpers.codeBlockAndSpanRanges(
|
|
498
|
-
|
|
570
|
+
const lineMetadata =
|
|
571
|
+
helpers.getLineMetadata(paramsBase);
|
|
572
|
+
const codeBlockAndSpanRanges =
|
|
573
|
+
helpers.codeBlockAndSpanRanges(paramsBase, lineMetadata);
|
|
574
|
+
const flattenedLists =
|
|
575
|
+
helpers.flattenLists(paramsBase.tokens);
|
|
576
|
+
const htmlElementRanges =
|
|
577
|
+
helpers.htmlElementRanges(paramsBase, lineMetadata);
|
|
578
|
+
const referenceLinkImageData =
|
|
579
|
+
helpers.getReferenceLinkImageData(lineMetadata);
|
|
580
|
+
cache.set({
|
|
581
|
+
codeBlockAndSpanRanges,
|
|
582
|
+
flattenedLists,
|
|
583
|
+
htmlElementRanges,
|
|
584
|
+
lineMetadata,
|
|
585
|
+
referenceLinkImageData
|
|
586
|
+
});
|
|
499
587
|
// Function to run for each rule
|
|
500
588
|
let results = [];
|
|
501
589
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
502
590
|
function forRule(rule) {
|
|
503
591
|
// Configure rule
|
|
504
592
|
const ruleName = rule.names[0].toUpperCase();
|
|
505
|
-
params
|
|
593
|
+
const params = {
|
|
594
|
+
...paramsBase,
|
|
595
|
+
"config": effectiveConfig[ruleName]
|
|
596
|
+
};
|
|
506
597
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
507
598
|
function throwError(property) {
|
|
508
599
|
throw new Error(
|
|
@@ -697,6 +788,7 @@ function lintContent(
|
|
|
697
788
|
* @param {string} file Path of file to lint.
|
|
698
789
|
* @param {Object} md Instance of markdown-it.
|
|
699
790
|
* @param {Configuration} config Configuration object.
|
|
791
|
+
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
|
700
792
|
* @param {RegExp} frontMatter Regular expression for front matter.
|
|
701
793
|
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
|
|
702
794
|
* @param {boolean} noInlineConfig Whether to allow inline configuration.
|
|
@@ -711,6 +803,7 @@ function lintFile(
|
|
|
711
803
|
file,
|
|
712
804
|
md,
|
|
713
805
|
config,
|
|
806
|
+
configParsers,
|
|
714
807
|
frontMatter,
|
|
715
808
|
handleRuleFailures,
|
|
716
809
|
noInlineConfig,
|
|
@@ -723,8 +816,19 @@ function lintFile(
|
|
|
723
816
|
if (err) {
|
|
724
817
|
return callback(err);
|
|
725
818
|
}
|
|
726
|
-
return lintContent(
|
|
727
|
-
|
|
819
|
+
return lintContent(
|
|
820
|
+
ruleList,
|
|
821
|
+
file,
|
|
822
|
+
content,
|
|
823
|
+
md,
|
|
824
|
+
config,
|
|
825
|
+
configParsers,
|
|
826
|
+
frontMatter,
|
|
827
|
+
handleRuleFailures,
|
|
828
|
+
noInlineConfig,
|
|
829
|
+
resultVersion,
|
|
830
|
+
callback
|
|
831
|
+
);
|
|
728
832
|
}
|
|
729
833
|
// Make a/synchronous call to read file
|
|
730
834
|
if (synchronous) {
|
|
@@ -750,7 +854,8 @@ function lintInput(options, synchronous, callback) {
|
|
|
750
854
|
const ruleList = rules.concat(options.customRules || []);
|
|
751
855
|
const ruleErr = validateRuleList(ruleList, synchronous);
|
|
752
856
|
if (ruleErr) {
|
|
753
|
-
|
|
857
|
+
callback(ruleErr);
|
|
858
|
+
return;
|
|
754
859
|
}
|
|
755
860
|
let files = [];
|
|
756
861
|
if (Array.isArray(options.files)) {
|
|
@@ -761,18 +866,19 @@ function lintInput(options, synchronous, callback) {
|
|
|
761
866
|
const strings = options.strings || {};
|
|
762
867
|
const stringsKeys = Object.keys(strings);
|
|
763
868
|
const config = options.config || { "default": true };
|
|
869
|
+
const configParsers = options.configParsers || null;
|
|
764
870
|
const frontMatter = (options.frontMatter === undefined) ?
|
|
765
871
|
helpers.frontMatterRe : options.frontMatter;
|
|
766
872
|
const handleRuleFailures = !!options.handleRuleFailures;
|
|
767
873
|
const noInlineConfig = !!options.noInlineConfig;
|
|
768
874
|
const resultVersion = (options.resultVersion === undefined) ?
|
|
769
|
-
|
|
875
|
+
3 : options.resultVersion;
|
|
770
876
|
const md = markdownIt({ "html": true });
|
|
771
877
|
const markdownItPlugins = options.markdownItPlugins || [];
|
|
772
|
-
|
|
878
|
+
for (const plugin of markdownItPlugins) {
|
|
773
879
|
// @ts-ignore
|
|
774
880
|
md.use(...plugin);
|
|
775
|
-
}
|
|
881
|
+
}
|
|
776
882
|
const fs = options.fs || require("fs");
|
|
777
883
|
const results = newResults(ruleList);
|
|
778
884
|
let done = false;
|
|
@@ -804,6 +910,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
804
910
|
currentItem,
|
|
805
911
|
md,
|
|
806
912
|
config,
|
|
913
|
+
configParsers,
|
|
807
914
|
frontMatter,
|
|
808
915
|
handleRuleFailures,
|
|
809
916
|
noInlineConfig,
|
|
@@ -812,16 +919,16 @@ function lintInput(options, synchronous, callback) {
|
|
|
812
919
|
synchronous,
|
|
813
920
|
lintWorkerCallback
|
|
814
921
|
);
|
|
815
|
-
} else if (stringsKeys.
|
|
922
|
+
} else if ((currentItem = stringsKeys.shift())) {
|
|
816
923
|
// Lint next string
|
|
817
924
|
concurrency++;
|
|
818
|
-
currentItem = stringsKeys.shift();
|
|
819
925
|
lintContent(
|
|
820
926
|
ruleList,
|
|
821
927
|
currentItem,
|
|
822
928
|
strings[currentItem] || "",
|
|
823
929
|
md,
|
|
824
930
|
config,
|
|
931
|
+
configParsers,
|
|
825
932
|
frontMatter,
|
|
826
933
|
handleRuleFailures,
|
|
827
934
|
noInlineConfig,
|
|
@@ -852,7 +959,6 @@ function lintInput(options, synchronous, callback) {
|
|
|
852
959
|
lintWorker();
|
|
853
960
|
lintWorker();
|
|
854
961
|
}
|
|
855
|
-
return null;
|
|
856
962
|
}
|
|
857
963
|
|
|
858
964
|
/**
|
|
@@ -875,6 +981,7 @@ const markdownlintPromisify = promisify && promisify(markdownlint);
|
|
|
875
981
|
* @returns {Promise<LintResults>} Results object.
|
|
876
982
|
*/
|
|
877
983
|
function markdownlintPromise(options) {
|
|
984
|
+
// @ts-ignore
|
|
878
985
|
return markdownlintPromisify(options);
|
|
879
986
|
}
|
|
880
987
|
|
|
@@ -885,49 +992,17 @@ function markdownlintPromise(options) {
|
|
|
885
992
|
* @returns {LintResults} Results object.
|
|
886
993
|
*/
|
|
887
994
|
function markdownlintSync(options) {
|
|
888
|
-
let results =
|
|
995
|
+
let results = {};
|
|
889
996
|
lintInput(options, true, function callback(error, res) {
|
|
890
997
|
if (error) {
|
|
891
998
|
throw error;
|
|
892
999
|
}
|
|
893
1000
|
results = res;
|
|
894
1001
|
});
|
|
1002
|
+
// @ts-ignore
|
|
895
1003
|
return results;
|
|
896
1004
|
}
|
|
897
1005
|
|
|
898
|
-
/**
|
|
899
|
-
* Parse the content of a configuration file.
|
|
900
|
-
*
|
|
901
|
-
* @param {string} name Name of the configuration file.
|
|
902
|
-
* @param {string} content Configuration content.
|
|
903
|
-
* @param {ConfigurationParser[]} parsers Parsing function(s).
|
|
904
|
-
* @returns {Object} Configuration object and error message.
|
|
905
|
-
*/
|
|
906
|
-
function parseConfiguration(name, content, parsers) {
|
|
907
|
-
let config = null;
|
|
908
|
-
let message = "";
|
|
909
|
-
const errors = [];
|
|
910
|
-
let index = 0;
|
|
911
|
-
// Try each parser
|
|
912
|
-
(parsers || [ JSON.parse ]).every((parser) => {
|
|
913
|
-
try {
|
|
914
|
-
config = parser(content);
|
|
915
|
-
} catch (error) {
|
|
916
|
-
errors.push(`Parser ${index++}: ${error.message}`);
|
|
917
|
-
}
|
|
918
|
-
return !config;
|
|
919
|
-
});
|
|
920
|
-
// Message if unable to parse
|
|
921
|
-
if (!config) {
|
|
922
|
-
errors.unshift(`Unable to parse '${name}'`);
|
|
923
|
-
message = errors.join("; ");
|
|
924
|
-
}
|
|
925
|
-
return {
|
|
926
|
-
config,
|
|
927
|
-
message
|
|
928
|
-
};
|
|
929
|
-
}
|
|
930
|
-
|
|
931
1006
|
/**
|
|
932
1007
|
* Resolve referenced "extends" path in a configuration file
|
|
933
1008
|
* using path.resolve() with require.resolve() as a fallback.
|
|
@@ -935,7 +1010,7 @@ function parseConfiguration(name, content, parsers) {
|
|
|
935
1010
|
* @param {string} configFile Configuration file name.
|
|
936
1011
|
* @param {string} referenceId Referenced identifier to resolve.
|
|
937
1012
|
* @param {Object} fs File system implementation.
|
|
938
|
-
* @param {ResolveConfigExtendsCallback}
|
|
1013
|
+
* @param {ResolveConfigExtendsCallback} callback Callback (err, result)
|
|
939
1014
|
* function.
|
|
940
1015
|
* @returns {void}
|
|
941
1016
|
*/
|
|
@@ -1005,6 +1080,7 @@ function readConfig(file, parsers, fs, callback) {
|
|
|
1005
1080
|
} else {
|
|
1006
1081
|
// @ts-ignore
|
|
1007
1082
|
callback = parsers;
|
|
1083
|
+
// @ts-ignore
|
|
1008
1084
|
parsers = null;
|
|
1009
1085
|
}
|
|
1010
1086
|
}
|
|
@@ -1012,14 +1088,18 @@ function readConfig(file, parsers, fs, callback) {
|
|
|
1012
1088
|
fs = require("fs");
|
|
1013
1089
|
}
|
|
1014
1090
|
// Read file
|
|
1091
|
+
const os = require("os");
|
|
1092
|
+
file = helpers.expandTildePath(file, os);
|
|
1015
1093
|
fs.readFile(file, "utf8", (err, content) => {
|
|
1016
1094
|
if (err) {
|
|
1095
|
+
// @ts-ignore
|
|
1017
1096
|
return callback(err);
|
|
1018
1097
|
}
|
|
1019
1098
|
// Try to parse file
|
|
1020
1099
|
// @ts-ignore
|
|
1021
1100
|
const { config, message } = parseConfiguration(file, content, parsers);
|
|
1022
1101
|
if (!config) {
|
|
1102
|
+
// @ts-ignore
|
|
1023
1103
|
return callback(new Error(message));
|
|
1024
1104
|
}
|
|
1025
1105
|
// Extend configuration
|
|
@@ -1028,16 +1108,19 @@ function readConfig(file, parsers, fs, callback) {
|
|
|
1028
1108
|
delete config.extends;
|
|
1029
1109
|
return resolveConfigExtends(
|
|
1030
1110
|
file,
|
|
1031
|
-
configExtends,
|
|
1111
|
+
helpers.expandTildePath(configExtends, os),
|
|
1032
1112
|
fs,
|
|
1033
1113
|
(_, resolvedExtends) => readConfig(
|
|
1114
|
+
// @ts-ignore
|
|
1034
1115
|
resolvedExtends,
|
|
1035
1116
|
parsers,
|
|
1036
1117
|
fs,
|
|
1037
1118
|
(errr, extendsConfig) => {
|
|
1038
1119
|
if (errr) {
|
|
1120
|
+
// @ts-ignore
|
|
1039
1121
|
return callback(errr);
|
|
1040
1122
|
}
|
|
1123
|
+
// @ts-ignore
|
|
1041
1124
|
return callback(null, {
|
|
1042
1125
|
...extendsConfig,
|
|
1043
1126
|
...config
|
|
@@ -1046,6 +1129,7 @@ function readConfig(file, parsers, fs, callback) {
|
|
|
1046
1129
|
)
|
|
1047
1130
|
);
|
|
1048
1131
|
}
|
|
1132
|
+
// @ts-ignore
|
|
1049
1133
|
return callback(null, config);
|
|
1050
1134
|
});
|
|
1051
1135
|
}
|
|
@@ -1079,6 +1163,8 @@ function readConfigSync(file, parsers, fs) {
|
|
|
1079
1163
|
fs = require("fs");
|
|
1080
1164
|
}
|
|
1081
1165
|
// Read file
|
|
1166
|
+
const os = require("os");
|
|
1167
|
+
file = helpers.expandTildePath(file, os);
|
|
1082
1168
|
const content = fs.readFileSync(file, "utf8");
|
|
1083
1169
|
// Try to parse file
|
|
1084
1170
|
const { config, message } = parseConfiguration(file, content, parsers);
|
|
@@ -1089,7 +1175,11 @@ function readConfigSync(file, parsers, fs) {
|
|
|
1089
1175
|
const configExtends = config.extends;
|
|
1090
1176
|
if (configExtends) {
|
|
1091
1177
|
delete config.extends;
|
|
1092
|
-
const resolvedExtends = resolveConfigExtendsSync(
|
|
1178
|
+
const resolvedExtends = resolveConfigExtendsSync(
|
|
1179
|
+
file,
|
|
1180
|
+
helpers.expandTildePath(configExtends, os),
|
|
1181
|
+
fs
|
|
1182
|
+
);
|
|
1093
1183
|
return {
|
|
1094
1184
|
...readConfigSync(resolvedExtends, parsers, fs),
|
|
1095
1185
|
...config
|
|
@@ -1206,16 +1296,17 @@ module.exports = markdownlint;
|
|
|
1206
1296
|
* Configuration options.
|
|
1207
1297
|
*
|
|
1208
1298
|
* @typedef {Object} Options
|
|
1209
|
-
* @property {string[] | string} [files] Files to lint.
|
|
1210
|
-
* @property {Object.<string, string>} [strings] Strings to lint.
|
|
1211
1299
|
* @property {Configuration} [config] Configuration object.
|
|
1300
|
+
* @property {ConfigurationParser[]} [configParsers] Configuration parsers.
|
|
1212
1301
|
* @property {Rule[] | Rule} [customRules] Custom rules.
|
|
1302
|
+
* @property {string[] | string} [files] Files to lint.
|
|
1213
1303
|
* @property {RegExp} [frontMatter] Front matter pattern.
|
|
1304
|
+
* @property {Object} [fs] File system implementation.
|
|
1214
1305
|
* @property {boolean} [handleRuleFailures] True to catch exceptions.
|
|
1306
|
+
* @property {Plugin[]} [markdownItPlugins] Additional plugins.
|
|
1215
1307
|
* @property {boolean} [noInlineConfig] True to ignore HTML directives.
|
|
1216
1308
|
* @property {number} [resultVersion] Results object version.
|
|
1217
|
-
* @property {
|
|
1218
|
-
* @property {Object} [fs] File system implementation.
|
|
1309
|
+
* @property {Object.<string, string>} [strings] Strings to lint.
|
|
1219
1310
|
*/
|
|
1220
1311
|
|
|
1221
1312
|
/**
|