markdownlint 0.12.0 → 0.14.2
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/.eslintrc.json +52 -275
- package/.travis.yml +2 -2
- package/LICENSE +1 -1
- package/README.md +54 -27
- package/doc/CustomRules.md +7 -2
- package/doc/Rules.md +120 -33
- package/example/standalone.js +2 -2
- package/helpers/LICENSE +21 -0
- package/helpers/README.md +50 -0
- package/{lib/shared.js → helpers/helpers.js} +91 -82
- package/helpers/package.json +18 -0
- package/lib/cache.js +24 -0
- package/lib/markdownlint.js +78 -45
- package/lib/md001.js +3 -3
- package/lib/md002.js +2 -2
- package/lib/md003.js +5 -4
- package/lib/md004.js +11 -9
- package/lib/md005.js +12 -10
- package/lib/md006.js +7 -5
- package/lib/md007.js +7 -5
- package/lib/md009.js +10 -8
- package/lib/md010.js +5 -5
- package/lib/md011.js +4 -4
- package/lib/md012.js +4 -3
- package/lib/md013.js +41 -32
- package/lib/md014.js +7 -6
- package/lib/md018.js +6 -4
- package/lib/md019.js +6 -5
- package/lib/md020.js +5 -4
- package/lib/md021.js +6 -5
- package/lib/md022.js +22 -22
- package/lib/md023.js +5 -4
- package/lib/md024.js +6 -6
- package/lib/md025.js +11 -5
- package/lib/md026.js +4 -4
- package/lib/md027.js +6 -6
- package/lib/md028.js +2 -2
- package/lib/md029.js +17 -9
- package/lib/md030.js +8 -6
- package/lib/md031.js +7 -6
- package/lib/md032.js +10 -12
- package/lib/md033.js +27 -26
- package/lib/md034.js +15 -10
- package/lib/md035.js +3 -3
- package/lib/md036.js +2 -2
- package/lib/md037.js +12 -9
- package/lib/md038.js +6 -5
- package/lib/md039.js +7 -6
- package/lib/md040.js +3 -3
- package/lib/md041.js +17 -19
- package/lib/md042.js +5 -4
- package/lib/md043.js +5 -4
- package/lib/md044.js +14 -13
- package/lib/md045.js +3 -3
- package/lib/md046.js +32 -0
- package/lib/md047.js +18 -0
- package/lib/rules.js +3 -1
- package/package.json +12 -11
- package/schema/build-config-schema.js +36 -2
- package/schema/markdownlint-config-schema.json +178 -14
- package/style/cirosantilli.json +1 -1
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
module.exports.newLineRe = /\r[\n\u0085]?|[\n\u2424\u2028\u0085]/;
|
|
8
8
|
|
|
9
9
|
// Regular expression for matching common front matter (YAML and TOML)
|
|
10
|
-
module.exports.frontMatterRe =
|
|
10
|
+
module.exports.frontMatterRe =
|
|
11
|
+
/((^---$[^]*?^---$)|(^\+\+\+$[^]*?^(\+\+\+|\.\.\.)$))(\r\n|\r|\n|$)/m;
|
|
11
12
|
|
|
12
13
|
// Regular expression for matching inline disable/enable comments
|
|
13
14
|
const inlineCommentRe =
|
|
@@ -23,31 +24,6 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
|
|
|
23
24
|
// readFile options for reading with the UTF-8 encoding
|
|
24
25
|
module.exports.utf8Encoding = { "encoding": "utf8" };
|
|
25
26
|
|
|
26
|
-
// Trims whitespace from the left (start) of a string
|
|
27
|
-
function trimLeft(str) {
|
|
28
|
-
return str.replace(/^\s*/, "");
|
|
29
|
-
}
|
|
30
|
-
module.exports.trimLeft = trimLeft;
|
|
31
|
-
|
|
32
|
-
// Trims whitespace from the right (end) of a string
|
|
33
|
-
module.exports.trimRight = function trimRight(str) {
|
|
34
|
-
return str.replace(/\s*$/, "");
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Applies key/value pairs from src to dst, returning dst
|
|
38
|
-
function assign(dst, src) {
|
|
39
|
-
Object.keys(src).forEach(function forKey(key) {
|
|
40
|
-
dst[key] = src[key];
|
|
41
|
-
});
|
|
42
|
-
return dst;
|
|
43
|
-
}
|
|
44
|
-
module.exports.assign = assign;
|
|
45
|
-
|
|
46
|
-
// Clones the key/value pairs of obj, returning the clone
|
|
47
|
-
module.exports.clone = function clone(obj) {
|
|
48
|
-
return assign({}, obj);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
27
|
// Returns true iff the input is a number
|
|
52
28
|
module.exports.isNumber = function isNumber(obj) {
|
|
53
29
|
return typeof obj === "number";
|
|
@@ -63,6 +39,31 @@ module.exports.isEmptyString = function isEmptyString(str) {
|
|
|
63
39
|
return str.length === 0;
|
|
64
40
|
};
|
|
65
41
|
|
|
42
|
+
// Returns true iff the input line is blank (no content)
|
|
43
|
+
// Example: Contains nothing, whitespace, or comments
|
|
44
|
+
const blankLineRe = />|(?:<!--.*?-->)/g;
|
|
45
|
+
module.exports.isBlankLine = function isBlankLine(line) {
|
|
46
|
+
return !line || !line.trim() || !line.replace(blankLineRe, "").trim();
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Returns true iff the sorted array contains the specified element
|
|
50
|
+
module.exports.includesSorted = function includesSorted(array, element) {
|
|
51
|
+
let left = 0;
|
|
52
|
+
let right = array.length - 1;
|
|
53
|
+
while (left <= right) {
|
|
54
|
+
/* eslint-disable no-bitwise */
|
|
55
|
+
const mid = (left + right) >> 1;
|
|
56
|
+
if (array[mid] < element) {
|
|
57
|
+
left = mid + 1;
|
|
58
|
+
} else if (array[mid] > element) {
|
|
59
|
+
right = mid - 1;
|
|
60
|
+
} else {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
};
|
|
66
|
+
|
|
66
67
|
// Replaces the text of all properly-formatted HTML comments with whitespace
|
|
67
68
|
// This preserves the line/column information for the rest of the document
|
|
68
69
|
// Trailing whitespace is avoided with a '\' character in the last column
|
|
@@ -75,13 +76,13 @@ module.exports.clearHtmlCommentText = function clearHtmlCommentText(text) {
|
|
|
75
76
|
let j = text.indexOf(htmlCommentEnd, i);
|
|
76
77
|
if (j === -1) {
|
|
77
78
|
j = text.length;
|
|
78
|
-
text += "
|
|
79
|
+
text += "\\\n";
|
|
79
80
|
}
|
|
80
81
|
const comment = text.slice(i + htmlCommentBegin.length, j);
|
|
81
82
|
if ((comment.length > 0) &&
|
|
82
83
|
(comment[0] !== ">") &&
|
|
83
84
|
(comment[comment.length - 1] !== "-") &&
|
|
84
|
-
|
|
85
|
+
!comment.includes("--") &&
|
|
85
86
|
(text.slice(i, j + htmlCommentEnd.length)
|
|
86
87
|
.search(inlineCommentRe) === -1)) {
|
|
87
88
|
const blanks = comment
|
|
@@ -100,10 +101,23 @@ module.exports.escapeForRegExp = function escapeForRegExp(str) {
|
|
|
100
101
|
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
101
102
|
};
|
|
102
103
|
|
|
104
|
+
// Un-escapes Markdown content (simple algorithm; not a parser)
|
|
105
|
+
const escapedMarkdownRe = /\\./g;
|
|
106
|
+
module.exports.unescapeMarkdown =
|
|
107
|
+
function unescapeMarkdown(markdown, replacement) {
|
|
108
|
+
return markdown.replace(escapedMarkdownRe, (match) => {
|
|
109
|
+
const char = match[1];
|
|
110
|
+
if ("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".includes(char)) {
|
|
111
|
+
return replacement || char;
|
|
112
|
+
}
|
|
113
|
+
return match;
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
103
117
|
// Returns the indent for a token
|
|
104
118
|
function indentFor(token) {
|
|
105
119
|
const line = token.line.replace(/^[\s>]*(> |>)/, "");
|
|
106
|
-
return line.length - trimLeft(
|
|
120
|
+
return line.length - line.trimLeft().length;
|
|
107
121
|
}
|
|
108
122
|
module.exports.indentFor = indentFor;
|
|
109
123
|
|
|
@@ -119,24 +133,17 @@ module.exports.headingStyleFor = function headingStyleFor(token) {
|
|
|
119
133
|
};
|
|
120
134
|
|
|
121
135
|
// Calls the provided function for each matching token
|
|
122
|
-
function filterTokens(params, type,
|
|
136
|
+
function filterTokens(params, type, handler) {
|
|
123
137
|
params.tokens.forEach(function forToken(token) {
|
|
124
138
|
if (token.type === type) {
|
|
125
|
-
|
|
139
|
+
handler(token);
|
|
126
140
|
}
|
|
127
141
|
});
|
|
128
142
|
}
|
|
129
143
|
module.exports.filterTokens = filterTokens;
|
|
130
144
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
function makeTokenCache(params) {
|
|
134
|
-
if (!params) {
|
|
135
|
-
tokenCache = null;
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Initialize line metadata array
|
|
145
|
+
// Get line metadata array
|
|
146
|
+
module.exports.getLineMetadata = function getLineMetadata(params) {
|
|
140
147
|
const lineMetadata = params.lines.map(function mapLine(line, index) {
|
|
141
148
|
return [ line, index, false, 0, false ];
|
|
142
149
|
});
|
|
@@ -157,8 +164,19 @@ function makeTokenCache(params) {
|
|
|
157
164
|
lineMetadata[i][4] = true;
|
|
158
165
|
}
|
|
159
166
|
});
|
|
167
|
+
return lineMetadata;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Calls the provided function for each line (with context)
|
|
171
|
+
module.exports.forEachLine = function forEachLine(lineMetadata, handler) {
|
|
172
|
+
lineMetadata.forEach(function forMetadata(metadata) {
|
|
173
|
+
// Parameters: line, lineIndex, inCode, onFence, inTable
|
|
174
|
+
handler(...metadata);
|
|
175
|
+
});
|
|
176
|
+
};
|
|
160
177
|
|
|
161
|
-
|
|
178
|
+
// Returns (nested) lists as a flat array (in order)
|
|
179
|
+
module.exports.flattenLists = function flattenLists(params) {
|
|
162
180
|
const flattenedLists = [];
|
|
163
181
|
const stack = [];
|
|
164
182
|
let current = null;
|
|
@@ -195,38 +213,23 @@ function makeTokenCache(params) {
|
|
|
195
213
|
lastWithMap = token;
|
|
196
214
|
}
|
|
197
215
|
});
|
|
198
|
-
|
|
199
|
-
// Cache results
|
|
200
|
-
tokenCache = {
|
|
201
|
-
"params": params,
|
|
202
|
-
"lineMetadata": lineMetadata,
|
|
203
|
-
"flattenedLists": flattenedLists
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
module.exports.makeTokenCache = makeTokenCache;
|
|
207
|
-
|
|
208
|
-
// Calls the provided function for each line (with context)
|
|
209
|
-
module.exports.forEachLine = function forEachLine(callback) {
|
|
210
|
-
tokenCache.lineMetadata.forEach(function forMetadata(metadata) {
|
|
211
|
-
// Parameters: line, lineIndex, inCode, onFence, inTable
|
|
212
|
-
callback.apply(this, metadata);
|
|
213
|
-
});
|
|
216
|
+
return flattenedLists;
|
|
214
217
|
};
|
|
215
218
|
|
|
216
219
|
// Calls the provided function for each specified inline child token
|
|
217
220
|
module.exports.forEachInlineChild =
|
|
218
|
-
function forEachInlineChild(params, type,
|
|
221
|
+
function forEachInlineChild(params, type, handler) {
|
|
219
222
|
filterTokens(params, "inline", function forToken(token) {
|
|
220
223
|
token.children.forEach(function forChild(child) {
|
|
221
224
|
if (child.type === type) {
|
|
222
|
-
|
|
225
|
+
handler(child, token);
|
|
223
226
|
}
|
|
224
227
|
});
|
|
225
228
|
});
|
|
226
229
|
};
|
|
227
230
|
|
|
228
231
|
// Calls the provided function for each heading's content
|
|
229
|
-
module.exports.forEachHeading = function forEachHeading(params,
|
|
232
|
+
module.exports.forEachHeading = function forEachHeading(params, handler) {
|
|
230
233
|
let heading = null;
|
|
231
234
|
params.tokens.forEach(function forToken(token) {
|
|
232
235
|
if (token.type === "heading_open") {
|
|
@@ -234,14 +237,14 @@ module.exports.forEachHeading = function forEachHeading(params, callback) {
|
|
|
234
237
|
} else if (token.type === "heading_close") {
|
|
235
238
|
heading = null;
|
|
236
239
|
} else if ((token.type === "inline") && heading) {
|
|
237
|
-
|
|
240
|
+
handler(heading, token.content);
|
|
238
241
|
}
|
|
239
242
|
});
|
|
240
243
|
};
|
|
241
244
|
|
|
242
245
|
// Calls the provided function for each inline code span's content
|
|
243
246
|
module.exports.forEachInlineCodeSpan =
|
|
244
|
-
function forEachInlineCodeSpan(input,
|
|
247
|
+
function forEachInlineCodeSpan(input, handler) {
|
|
245
248
|
let currentLine = 0;
|
|
246
249
|
let currentColumn = 0;
|
|
247
250
|
let index = 0;
|
|
@@ -265,7 +268,7 @@ module.exports.forEachInlineCodeSpan =
|
|
|
265
268
|
(startColumn >= 0) &&
|
|
266
269
|
(tickCount === currentTicks)) {
|
|
267
270
|
// Found end backticks; invoke callback for code span
|
|
268
|
-
|
|
271
|
+
handler(
|
|
269
272
|
input.substring(startIndex, index - currentTicks),
|
|
270
273
|
startLine, startColumn, tickCount);
|
|
271
274
|
startIndex = -1;
|
|
@@ -302,11 +305,6 @@ module.exports.forEachInlineCodeSpan =
|
|
|
302
305
|
}
|
|
303
306
|
};
|
|
304
307
|
|
|
305
|
-
// Returns (nested) lists as a flat array (in order)
|
|
306
|
-
module.exports.flattenLists = function flattenLists() {
|
|
307
|
-
return tokenCache.flattenedLists;
|
|
308
|
-
};
|
|
309
|
-
|
|
310
308
|
// Adds a generic error object via the onError callback
|
|
311
309
|
function addError(onError, lineNumber, detail, context, range) {
|
|
312
310
|
onError({
|
|
@@ -320,32 +318,32 @@ module.exports.addError = addError;
|
|
|
320
318
|
|
|
321
319
|
// Adds an error object with details conditionally via the onError callback
|
|
322
320
|
module.exports.addErrorDetailIf = function addErrorDetailIf(
|
|
323
|
-
onError, lineNumber, expected, actual, detail, range) {
|
|
321
|
+
onError, lineNumber, expected, actual, detail, context, range) {
|
|
324
322
|
if (expected !== actual) {
|
|
325
323
|
addError(
|
|
326
324
|
onError,
|
|
327
325
|
lineNumber,
|
|
328
326
|
"Expected: " + expected + "; Actual: " + actual +
|
|
329
327
|
(detail ? "; " + detail : ""),
|
|
330
|
-
|
|
328
|
+
context,
|
|
331
329
|
range);
|
|
332
330
|
}
|
|
333
331
|
};
|
|
334
332
|
|
|
335
333
|
// Adds an error object with context via the onError callback
|
|
336
334
|
module.exports.addErrorContext =
|
|
337
|
-
function addErrorContext(onError, lineNumber, context, left, right, range) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
};
|
|
335
|
+
function addErrorContext(onError, lineNumber, context, left, right, range) {
|
|
336
|
+
if (context.length <= 30) {
|
|
337
|
+
// Nothing to do
|
|
338
|
+
} else if (left && right) {
|
|
339
|
+
context = context.substr(0, 15) + "..." + context.substr(-15);
|
|
340
|
+
} else if (right) {
|
|
341
|
+
context = "..." + context.substr(-30);
|
|
342
|
+
} else {
|
|
343
|
+
context = context.substr(0, 30) + "...";
|
|
344
|
+
}
|
|
345
|
+
addError(onError, lineNumber, null, context, range);
|
|
346
|
+
};
|
|
349
347
|
|
|
350
348
|
// Returns a range object for a line by applying a RegExp
|
|
351
349
|
module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
|
@@ -362,3 +360,14 @@ module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
|
|
|
362
360
|
}
|
|
363
361
|
return range;
|
|
364
362
|
};
|
|
363
|
+
|
|
364
|
+
// Determines if the front matter includes a title
|
|
365
|
+
module.exports.frontMatterHasTitle =
|
|
366
|
+
function frontMatterHasTitle(frontMatterLines, frontMatterTitlePattern) {
|
|
367
|
+
const ignoreFrontMatter =
|
|
368
|
+
(frontMatterTitlePattern !== undefined) && !frontMatterTitlePattern;
|
|
369
|
+
const frontMatterTitleRe =
|
|
370
|
+
new RegExp(frontMatterTitlePattern || "^\\s*title\\s*[:=]", "i");
|
|
371
|
+
return !ignoreFrontMatter &&
|
|
372
|
+
frontMatterLines.some((line) => frontMatterTitleRe.test(line));
|
|
373
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "markdownlint-rule-helpers",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "A collection of markdownlint helper functions for custom rules",
|
|
5
|
+
"main": "helpers.js",
|
|
6
|
+
"author": "David Anson (https://dlaa.me/)",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/DavidAnson/markdownlint",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/DavidAnson/markdownlint.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": "https://github.com/DavidAnson/markdownlint/issues",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"markdownlint",
|
|
16
|
+
"markdownlint-rule"
|
|
17
|
+
]
|
|
18
|
+
}
|
package/lib/cache.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
let lineMetadata = null;
|
|
6
|
+
module.exports.lineMetadata = (value) => {
|
|
7
|
+
if (value) {
|
|
8
|
+
lineMetadata = value;
|
|
9
|
+
}
|
|
10
|
+
return lineMetadata;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let flattenedLists = null;
|
|
14
|
+
module.exports.flattenedLists = (value) => {
|
|
15
|
+
if (value) {
|
|
16
|
+
flattenedLists = value;
|
|
17
|
+
}
|
|
18
|
+
return flattenedLists;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
module.exports.clear = () => {
|
|
22
|
+
lineMetadata = null;
|
|
23
|
+
flattenedLists = null;
|
|
24
|
+
};
|
package/lib/markdownlint.js
CHANGED
|
@@ -7,7 +7,10 @@ const path = require("path");
|
|
|
7
7
|
const { URL } = require("url");
|
|
8
8
|
const markdownIt = require("markdown-it");
|
|
9
9
|
const rules = require("./rules");
|
|
10
|
-
const
|
|
10
|
+
const helpers = require("../helpers");
|
|
11
|
+
const cache = require("./cache");
|
|
12
|
+
|
|
13
|
+
const deprecatedRuleNames = [ "MD002" ];
|
|
11
14
|
|
|
12
15
|
// Validates the list of rules for structure and reuse
|
|
13
16
|
function validateRuleList(ruleList) {
|
|
@@ -28,7 +31,7 @@ function validateRuleList(ruleList) {
|
|
|
28
31
|
const value = rule[property];
|
|
29
32
|
if (!result &&
|
|
30
33
|
(!value || !Array.isArray(value) || (value.length === 0) ||
|
|
31
|
-
!value.every(
|
|
34
|
+
!value.every(helpers.isString) || value.some(helpers.isEmptyString))) {
|
|
32
35
|
result = newError(property);
|
|
33
36
|
}
|
|
34
37
|
});
|
|
@@ -72,7 +75,7 @@ function validateRuleList(ruleList) {
|
|
|
72
75
|
// Class for results with toString for pretty display
|
|
73
76
|
function newResults(ruleList) {
|
|
74
77
|
function Results() {}
|
|
75
|
-
Results.prototype.toString = function
|
|
78
|
+
Results.prototype.toString = function toString(useAlias) {
|
|
76
79
|
const that = this;
|
|
77
80
|
let ruleNameToRule = null;
|
|
78
81
|
const results = [];
|
|
@@ -131,7 +134,7 @@ function removeFrontMatter(content, frontMatter) {
|
|
|
131
134
|
if (frontMatterMatch && !frontMatterMatch.index) {
|
|
132
135
|
const contentMatched = frontMatterMatch[0];
|
|
133
136
|
content = content.slice(contentMatched.length);
|
|
134
|
-
frontMatterLines = contentMatched.split(
|
|
137
|
+
frontMatterLines = contentMatched.split(helpers.newLineRe);
|
|
135
138
|
if (frontMatterLines.length &&
|
|
136
139
|
(frontMatterLines[frontMatterLines.length - 1] === "")) {
|
|
137
140
|
frontMatterLines.length--;
|
|
@@ -164,7 +167,7 @@ function annotateTokens(tokens, lines) {
|
|
|
164
167
|
token.line = lines[token.map[0]];
|
|
165
168
|
token.lineNumber = token.map[0] + 1;
|
|
166
169
|
// Trim bottom of token to exclude whitespace lines
|
|
167
|
-
while (token.map[1] && !(lines[token.map[1] - 1].trim())) {
|
|
170
|
+
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
|
168
171
|
token.map[1]--;
|
|
169
172
|
}
|
|
170
173
|
// Annotate children with lineNumber
|
|
@@ -212,16 +215,19 @@ function mapAliasToRuleNames(ruleList) {
|
|
|
212
215
|
|
|
213
216
|
// Apply (and normalize) config
|
|
214
217
|
function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
215
|
-
const defaultKey = Object.keys(config).filter(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
+
const defaultKey = Object.keys(config).filter(
|
|
219
|
+
(key) => key.toUpperCase() === "DEFAULT"
|
|
220
|
+
);
|
|
218
221
|
const ruleDefault = (defaultKey.length === 0) || !!config[defaultKey[0]];
|
|
219
222
|
const effectiveConfig = {};
|
|
220
|
-
ruleList.forEach(
|
|
223
|
+
ruleList.forEach((rule) => {
|
|
221
224
|
const ruleName = rule.names[0].toUpperCase();
|
|
222
225
|
effectiveConfig[ruleName] = ruleDefault;
|
|
223
226
|
});
|
|
224
|
-
|
|
227
|
+
deprecatedRuleNames.forEach((ruleName) => {
|
|
228
|
+
effectiveConfig[ruleName] = false;
|
|
229
|
+
});
|
|
230
|
+
Object.keys(config).forEach((key) => {
|
|
225
231
|
let value = config[key];
|
|
226
232
|
if (value) {
|
|
227
233
|
if (!(value instanceof Object)) {
|
|
@@ -231,7 +237,7 @@ function getEffectiveConfig(ruleList, config, aliasToRuleNames) {
|
|
|
231
237
|
value = false;
|
|
232
238
|
}
|
|
233
239
|
const keyUpper = key.toUpperCase();
|
|
234
|
-
(aliasToRuleNames[keyUpper] || []).forEach(
|
|
240
|
+
(aliasToRuleNames[keyUpper] || []).forEach((ruleName) => {
|
|
235
241
|
effectiveConfig[ruleName] = value;
|
|
236
242
|
});
|
|
237
243
|
});
|
|
@@ -263,12 +269,12 @@ function getEnabledRulesPerLineNumber(
|
|
|
263
269
|
const enabledRulesPerLineNumber = new Array(1 + frontMatterLines.length);
|
|
264
270
|
lines.forEach(function forLine(line) {
|
|
265
271
|
if (!noInlineConfig) {
|
|
266
|
-
let match =
|
|
272
|
+
let match = helpers.inlineCommentRe.exec(line);
|
|
267
273
|
if (match) {
|
|
268
|
-
enabledRules =
|
|
274
|
+
enabledRules = { ...enabledRules };
|
|
269
275
|
while (match) {
|
|
270
276
|
forMatch(match);
|
|
271
|
-
match =
|
|
277
|
+
match = helpers.inlineCommentRe.exec(line);
|
|
272
278
|
}
|
|
273
279
|
}
|
|
274
280
|
}
|
|
@@ -295,6 +301,7 @@ function lintContent(
|
|
|
295
301
|
md,
|
|
296
302
|
config,
|
|
297
303
|
frontMatter,
|
|
304
|
+
handleRuleFailures,
|
|
298
305
|
noInlineConfig,
|
|
299
306
|
resultVersion,
|
|
300
307
|
callback) {
|
|
@@ -304,10 +311,10 @@ function lintContent(
|
|
|
304
311
|
const removeFrontMatterResult = removeFrontMatter(content, frontMatter);
|
|
305
312
|
const frontMatterLines = removeFrontMatterResult.frontMatterLines;
|
|
306
313
|
// Ignore the content of HTML comments
|
|
307
|
-
content =
|
|
314
|
+
content = helpers.clearHtmlCommentText(removeFrontMatterResult.content);
|
|
308
315
|
// Parse content into tokens and lines
|
|
309
316
|
const tokens = md.parse(content, {});
|
|
310
|
-
const lines = content.split(
|
|
317
|
+
const lines = content.split(helpers.newLineRe);
|
|
311
318
|
annotateTokens(tokens, lines);
|
|
312
319
|
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
|
313
320
|
const effectiveConfig =
|
|
@@ -322,7 +329,8 @@ function lintContent(
|
|
|
322
329
|
lines,
|
|
323
330
|
frontMatterLines
|
|
324
331
|
};
|
|
325
|
-
|
|
332
|
+
cache.lineMetadata(helpers.getLineMetadata(params));
|
|
333
|
+
cache.flattenedLists(helpers.flattenLists(params));
|
|
326
334
|
// Function to run for each rule
|
|
327
335
|
const result = (resultVersion === 0) ? {} : [];
|
|
328
336
|
function forRule(rule) {
|
|
@@ -337,23 +345,28 @@ function lintContent(
|
|
|
337
345
|
const errors = [];
|
|
338
346
|
function onError(errorInfo) {
|
|
339
347
|
if (!errorInfo ||
|
|
340
|
-
!errorInfo.lineNumber ||
|
|
341
|
-
|
|
348
|
+
!helpers.isNumber(errorInfo.lineNumber) ||
|
|
349
|
+
(errorInfo.lineNumber < 1) ||
|
|
350
|
+
(errorInfo.lineNumber > lines.length)) {
|
|
342
351
|
throwError("lineNumber");
|
|
343
352
|
}
|
|
344
353
|
if (errorInfo.detail &&
|
|
345
|
-
!
|
|
354
|
+
!helpers.isString(errorInfo.detail)) {
|
|
346
355
|
throwError("detail");
|
|
347
356
|
}
|
|
348
357
|
if (errorInfo.context &&
|
|
349
|
-
!
|
|
358
|
+
!helpers.isString(errorInfo.context)) {
|
|
350
359
|
throwError("context");
|
|
351
360
|
}
|
|
352
361
|
if (errorInfo.range &&
|
|
353
362
|
(!Array.isArray(errorInfo.range) ||
|
|
354
363
|
(errorInfo.range.length !== 2) ||
|
|
355
|
-
!
|
|
356
|
-
|
|
364
|
+
!helpers.isNumber(errorInfo.range[0]) ||
|
|
365
|
+
(errorInfo.range[0] < 1) ||
|
|
366
|
+
!helpers.isNumber(errorInfo.range[1]) ||
|
|
367
|
+
(errorInfo.range[1] < 1) ||
|
|
368
|
+
((errorInfo.range[0] + errorInfo.range[1] - 1) >
|
|
369
|
+
lines[errorInfo.lineNumber - 1].length))) {
|
|
357
370
|
throwError("range");
|
|
358
371
|
}
|
|
359
372
|
errors.push({
|
|
@@ -364,7 +377,18 @@ function lintContent(
|
|
|
364
377
|
});
|
|
365
378
|
}
|
|
366
379
|
// Call (possibly external) rule function
|
|
367
|
-
|
|
380
|
+
if (handleRuleFailures) {
|
|
381
|
+
try {
|
|
382
|
+
rule.function(params, onError);
|
|
383
|
+
} catch (ex) {
|
|
384
|
+
onError({
|
|
385
|
+
"lineNumber": 1,
|
|
386
|
+
"detail": `This rule threw an exception: ${ex.message}`
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
rule.function(params, onError);
|
|
391
|
+
}
|
|
368
392
|
// Record any errors (significant performance benefit from length check)
|
|
369
393
|
if (errors.length) {
|
|
370
394
|
errors.sort(lineNumberComparison);
|
|
@@ -406,11 +430,11 @@ function lintContent(
|
|
|
406
430
|
try {
|
|
407
431
|
ruleList.forEach(forRule);
|
|
408
432
|
} catch (ex) {
|
|
409
|
-
|
|
433
|
+
cache.clear();
|
|
410
434
|
return callback(ex);
|
|
411
435
|
}
|
|
412
|
-
|
|
413
|
-
callback(null, result);
|
|
436
|
+
cache.clear();
|
|
437
|
+
return callback(null, result);
|
|
414
438
|
}
|
|
415
439
|
|
|
416
440
|
// Lints a single file
|
|
@@ -420,6 +444,7 @@ function lintFile(
|
|
|
420
444
|
md,
|
|
421
445
|
config,
|
|
422
446
|
frontMatter,
|
|
447
|
+
handleRuleFailures,
|
|
423
448
|
noInlineConfig,
|
|
424
449
|
resultVersion,
|
|
425
450
|
synchronous,
|
|
@@ -428,14 +453,14 @@ function lintFile(
|
|
|
428
453
|
if (err) {
|
|
429
454
|
return callback(err);
|
|
430
455
|
}
|
|
431
|
-
lintContent(ruleList, file, content, md, config, frontMatter,
|
|
432
|
-
noInlineConfig, resultVersion, callback);
|
|
456
|
+
return lintContent(ruleList, file, content, md, config, frontMatter,
|
|
457
|
+
handleRuleFailures, noInlineConfig, resultVersion, callback);
|
|
433
458
|
}
|
|
434
459
|
// Make a/synchronous call to read file
|
|
435
460
|
if (synchronous) {
|
|
436
|
-
lintContentWrapper(null, fs.readFileSync(file,
|
|
461
|
+
lintContentWrapper(null, fs.readFileSync(file, helpers.utf8Encoding));
|
|
437
462
|
} else {
|
|
438
|
-
fs.readFile(file,
|
|
463
|
+
fs.readFile(file, helpers.utf8Encoding, lintContentWrapper);
|
|
439
464
|
}
|
|
440
465
|
}
|
|
441
466
|
|
|
@@ -459,7 +484,8 @@ function lintInput(options, synchronous, callback) {
|
|
|
459
484
|
const stringsKeys = Object.keys(strings);
|
|
460
485
|
const config = options.config || { "default": true };
|
|
461
486
|
const frontMatter = (options.frontMatter === undefined) ?
|
|
462
|
-
|
|
487
|
+
helpers.frontMatterRe : options.frontMatter;
|
|
488
|
+
const handleRuleFailures = !!options.handleRuleFailures;
|
|
463
489
|
const noInlineConfig = !!options.noInlineConfig;
|
|
464
490
|
const resultVersion = (options.resultVersion === undefined) ?
|
|
465
491
|
2 : options.resultVersion;
|
|
@@ -471,6 +497,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
471
497
|
});
|
|
472
498
|
const results = newResults(ruleList);
|
|
473
499
|
// Helper to lint the next string or file
|
|
500
|
+
/* eslint-disable consistent-return */
|
|
474
501
|
function lintNextItem() {
|
|
475
502
|
let iterating = true;
|
|
476
503
|
let item = null;
|
|
@@ -480,9 +507,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
480
507
|
return callback(err);
|
|
481
508
|
}
|
|
482
509
|
results[item] = result;
|
|
483
|
-
|
|
484
|
-
lintNextItem();
|
|
485
|
-
}
|
|
510
|
+
return iterating || lintNextItem();
|
|
486
511
|
}
|
|
487
512
|
while (iterating) {
|
|
488
513
|
if ((item = stringsKeys.shift())) {
|
|
@@ -493,6 +518,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
493
518
|
md,
|
|
494
519
|
config,
|
|
495
520
|
frontMatter,
|
|
521
|
+
handleRuleFailures,
|
|
496
522
|
noInlineConfig,
|
|
497
523
|
resultVersion,
|
|
498
524
|
lintNextItemCallback);
|
|
@@ -504,6 +530,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
504
530
|
md,
|
|
505
531
|
config,
|
|
506
532
|
frontMatter,
|
|
533
|
+
handleRuleFailures,
|
|
507
534
|
noInlineConfig,
|
|
508
535
|
resultVersion,
|
|
509
536
|
synchronous,
|
|
@@ -513,7 +540,7 @@ function lintInput(options, synchronous, callback) {
|
|
|
513
540
|
}
|
|
514
541
|
}
|
|
515
542
|
}
|
|
516
|
-
lintNextItem();
|
|
543
|
+
return lintNextItem();
|
|
517
544
|
}
|
|
518
545
|
|
|
519
546
|
/**
|
|
@@ -584,7 +611,7 @@ function readConfig(file, parsers, callback) {
|
|
|
584
611
|
parsers = null;
|
|
585
612
|
}
|
|
586
613
|
// Read file
|
|
587
|
-
fs.readFile(file,
|
|
614
|
+
fs.readFile(file, helpers.utf8Encoding, (err, content) => {
|
|
588
615
|
if (err) {
|
|
589
616
|
return callback(err);
|
|
590
617
|
}
|
|
@@ -598,15 +625,17 @@ function readConfig(file, parsers, callback) {
|
|
|
598
625
|
if (configExtends) {
|
|
599
626
|
delete config.extends;
|
|
600
627
|
const extendsFile = path.resolve(path.dirname(file), configExtends);
|
|
601
|
-
readConfig(extendsFile, parsers, (errr, extendsConfig) => {
|
|
628
|
+
return readConfig(extendsFile, parsers, (errr, extendsConfig) => {
|
|
602
629
|
if (errr) {
|
|
603
630
|
return callback(errr);
|
|
604
631
|
}
|
|
605
|
-
callback(null,
|
|
632
|
+
return callback(null, {
|
|
633
|
+
...extendsConfig,
|
|
634
|
+
...config
|
|
635
|
+
});
|
|
606
636
|
});
|
|
607
|
-
} else {
|
|
608
|
-
callback(null, config);
|
|
609
637
|
}
|
|
638
|
+
return callback(null, config);
|
|
610
639
|
});
|
|
611
640
|
}
|
|
612
641
|
|
|
@@ -619,7 +648,7 @@ function readConfig(file, parsers, callback) {
|
|
|
619
648
|
*/
|
|
620
649
|
function readConfigSync(file, parsers) {
|
|
621
650
|
// Read file
|
|
622
|
-
const content = fs.readFileSync(file,
|
|
651
|
+
const content = fs.readFileSync(file, helpers.utf8Encoding);
|
|
623
652
|
// Try to parse file
|
|
624
653
|
const { config, message } = parseConfiguration(file, content, parsers);
|
|
625
654
|
if (!config) {
|
|
@@ -629,9 +658,13 @@ function readConfigSync(file, parsers) {
|
|
|
629
658
|
const configExtends = config.extends;
|
|
630
659
|
if (configExtends) {
|
|
631
660
|
delete config.extends;
|
|
632
|
-
return
|
|
633
|
-
readConfigSync(
|
|
634
|
-
|
|
661
|
+
return {
|
|
662
|
+
...readConfigSync(
|
|
663
|
+
path.resolve(path.dirname(file), configExtends),
|
|
664
|
+
parsers
|
|
665
|
+
),
|
|
666
|
+
...config
|
|
667
|
+
};
|
|
635
668
|
}
|
|
636
669
|
return config;
|
|
637
670
|
}
|