markdownlint 0.21.1 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,14 +2,18 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const fs = require("fs");
6
5
  const path = require("path");
7
- const util = require("util");
6
+ const { promisify } = require("util");
8
7
  const markdownIt = require("markdown-it");
9
8
  const rules = require("./rules");
10
9
  const helpers = require("../helpers");
11
10
  const cache = require("./cache");
12
11
 
12
+ // @ts-ignore
13
+ // eslint-disable-next-line camelcase, max-len, no-inline-comments, no-undef
14
+ const dynamicRequire = (typeof __non_webpack_require__ === "undefined") ? require : /* c8 ignore next */ __non_webpack_require__;
15
+ // Capture native require implementation for dynamic loading of modules
16
+
13
17
  const deprecatedRuleNames = [ "MD002", "MD006" ];
14
18
 
15
19
  /**
@@ -51,10 +55,12 @@ function validateRuleList(ruleList) {
51
55
  result = newError(property);
52
56
  }
53
57
  });
54
- if (!result && rule.information) {
55
- if (Object.getPrototypeOf(rule.information) !== URL.prototype) {
56
- result = newError("information");
57
- }
58
+ if (
59
+ !result &&
60
+ rule.information &&
61
+ (Object.getPrototypeOf(rule.information) !== URL.prototype)
62
+ ) {
63
+ result = newError("information");
58
64
  }
59
65
  if (!result) {
60
66
  rule.names.forEach(function forName(name) {
@@ -176,18 +182,27 @@ function removeFrontMatter(content, frontMatter) {
176
182
  * @returns {void}
177
183
  */
178
184
  function annotateTokens(tokens, lines) {
179
- let tbodyMap = null;
185
+ let tableMap = null;
180
186
  tokens.forEach(function forToken(token) {
181
- // Handle missing maps for table body
182
- if (token.type === "tbody_open") {
183
- tbodyMap = token.map.slice();
184
- } else if ((token.type === "tr_close") && tbodyMap) {
185
- tbodyMap[0]++;
186
- } else if (token.type === "tbody_close") {
187
- tbodyMap = null;
187
+ // Handle missing maps for table head/body
188
+ if (
189
+ (token.type === "thead_open") ||
190
+ (token.type === "tbody_open")
191
+ ) {
192
+ tableMap = [ ...token.map ];
193
+ } else if (
194
+ (token.type === "tr_close") &&
195
+ tableMap
196
+ ) {
197
+ tableMap[0]++;
198
+ } else if (
199
+ (token.type === "thead_close") ||
200
+ (token.type === "tbody_close")
201
+ ) {
202
+ tableMap = null;
188
203
  }
189
- if (tbodyMap && !token.map) {
190
- token.map = tbodyMap.slice();
204
+ if (tableMap && !token.map) {
205
+ token.map = [ ...tableMap ];
191
206
  }
192
207
  // Update token metadata
193
208
  if (token.map) {
@@ -322,13 +337,13 @@ function getEnabledRulesPerLineNumber(
322
337
  // eslint-disable-next-line jsdoc/require-jsdoc
323
338
  function handleInlineConfig(perLine, forEachMatch, forEachLine) {
324
339
  const input = perLine ? lines : [ lines.join("\n") ];
325
- input.forEach((line) => {
340
+ input.forEach((line, lineIndex) => {
326
341
  if (!noInlineConfig) {
327
342
  let match = null;
328
343
  while ((match = helpers.inlineCommentRe.exec(line))) {
329
344
  const action = (match[1] || match[3]).toUpperCase();
330
345
  const parameter = match[2] || match[4];
331
- forEachMatch(action, parameter);
346
+ forEachMatch(action, parameter, lineIndex + 1);
332
347
  }
333
348
  }
334
349
  if (forEachLine) {
@@ -351,21 +366,21 @@ function getEnabledRulesPerLineNumber(
351
366
  }
352
367
  }
353
368
  // eslint-disable-next-line jsdoc/require-jsdoc
354
- function applyEnableDisable(action, parameter) {
369
+ function applyEnableDisable(action, parameter, state) {
355
370
  const enabled = (action.startsWith("ENABLE"));
356
371
  const items = parameter ?
357
372
  parameter.trim().toUpperCase().split(/\s+/) :
358
373
  allRuleNames;
359
374
  items.forEach((nameUpper) => {
360
375
  (aliasToRuleNames[nameUpper] || []).forEach((ruleName) => {
361
- enabledRules[ruleName] = enabled;
376
+ state[ruleName] = enabled;
362
377
  });
363
378
  });
364
379
  }
365
380
  // eslint-disable-next-line jsdoc/require-jsdoc
366
381
  function enableDisableFile(action, parameter) {
367
382
  if ((action === "ENABLE-FILE") || (action === "DISABLE-FILE")) {
368
- applyEnableDisable(action, parameter);
383
+ applyEnableDisable(action, parameter, enabledRules);
369
384
  }
370
385
  }
371
386
  // eslint-disable-next-line jsdoc/require-jsdoc
@@ -376,12 +391,22 @@ function getEnabledRulesPerLineNumber(
376
391
  enabledRules = { ...capturedRules };
377
392
  } else if ((action === "ENABLE") || (action === "DISABLE")) {
378
393
  enabledRules = { ...enabledRules };
379
- applyEnableDisable(action, parameter);
394
+ applyEnableDisable(action, parameter, enabledRules);
380
395
  }
381
396
  }
382
397
  // eslint-disable-next-line jsdoc/require-jsdoc
383
398
  function updateLineState() {
384
- enabledRulesPerLineNumber.push(enabledRules);
399
+ enabledRulesPerLineNumber.push({ ...enabledRules });
400
+ }
401
+ // eslint-disable-next-line jsdoc/require-jsdoc
402
+ function disableNextLine(action, parameter, lineNumber) {
403
+ if (action === "DISABLE-NEXT-LINE") {
404
+ applyEnableDisable(
405
+ action,
406
+ parameter,
407
+ enabledRulesPerLineNumber[lineNumber + 1] || {}
408
+ );
409
+ }
385
410
  }
386
411
  // Handle inline comments
387
412
  handleInlineConfig(false, configureFile);
@@ -395,6 +420,7 @@ function getEnabledRulesPerLineNumber(
395
420
  capturedRules = enabledRules;
396
421
  handleInlineConfig(true, enableDisableFile);
397
422
  handleInlineConfig(true, captureRestoreEnableDisable, updateLineState);
423
+ handleInlineConfig(true, disableNextLine);
398
424
  // Return results
399
425
  return {
400
426
  effectiveConfig,
@@ -439,8 +465,8 @@ function uniqueFilterForSortedErrors(value, index, array) {
439
465
  *
440
466
  * @param {Rule[]} ruleList List of rules.
441
467
  * @param {string} name Identifier for the content.
442
- * @param {string} content Markdown content
443
- * @param {Object} md markdown-it instance.
468
+ * @param {string} content Markdown content.
469
+ * @param {Object} md Instance of markdown-it.
444
470
  * @param {Configuration} config Configuration object.
445
471
  * @param {RegExp} frontMatter Regular expression for front matter.
446
472
  * @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
@@ -489,7 +515,8 @@ function lintContent(
489
515
  frontMatterLines
490
516
  };
491
517
  cache.lineMetadata(helpers.getLineMetadata(params));
492
- cache.flattenedLists(helpers.flattenLists(params));
518
+ cache.flattenedLists(helpers.flattenLists(params.tokens));
519
+ cache.inlineCodeSpanRanges(helpers.inlineCodeSpanRanges(params.lines));
493
520
  // Function to run for each rule
494
521
  const result = (resultVersion === 0) ? {} : [];
495
522
  // eslint-disable-next-line jsdoc/require-jsdoc
@@ -651,12 +678,13 @@ function lintContent(
651
678
  *
652
679
  * @param {Rule[]} ruleList List of rules.
653
680
  * @param {string} file Path of file to lint.
654
- * @param {Object} md markdown-it instance.
681
+ * @param {Object} md Instance of markdown-it.
655
682
  * @param {Configuration} config Configuration object.
656
683
  * @param {RegExp} frontMatter Regular expression for front matter.
657
684
  * @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
658
685
  * @param {boolean} noInlineConfig Whether to allow inline configuration.
659
686
  * @param {number} resultVersion Version of the LintResults object to return.
687
+ * @param {Object} fs File system implementation.
660
688
  * @param {boolean} synchronous Whether to execute synchronously.
661
689
  * @param {Function} callback Callback (err, result) function.
662
690
  * @returns {void}
@@ -670,6 +698,7 @@ function lintFile(
670
698
  handleRuleFailures,
671
699
  noInlineConfig,
672
700
  resultVersion,
701
+ fs,
673
702
  synchronous,
674
703
  callback) {
675
704
  // eslint-disable-next-line jsdoc/require-jsdoc
@@ -682,10 +711,9 @@ function lintFile(
682
711
  }
683
712
  // Make a/synchronous call to read file
684
713
  if (synchronous) {
685
- // @ts-ignore
686
- lintContentWrapper(null, fs.readFileSync(file, helpers.utf8Encoding));
714
+ lintContentWrapper(null, fs.readFileSync(file, "utf8"));
687
715
  } else {
688
- fs.readFile(file, helpers.utf8Encoding, lintContentWrapper);
716
+ fs.readFile(file, "utf8", lintContentWrapper);
689
717
  }
690
718
  }
691
719
 
@@ -701,6 +729,7 @@ function lintInput(options, synchronous, callback) {
701
729
  // Normalize inputs
702
730
  options = options || {};
703
731
  callback = callback || function noop() {};
732
+ // eslint-disable-next-line unicorn/prefer-spread
704
733
  const ruleList = rules.concat(options.customRules || []);
705
734
  const ruleErr = validateRuleList(ruleList);
706
735
  if (ruleErr) {
@@ -708,7 +737,7 @@ function lintInput(options, synchronous, callback) {
708
737
  }
709
738
  let files = [];
710
739
  if (Array.isArray(options.files)) {
711
- files = options.files.slice();
740
+ files = [ ...options.files ];
712
741
  } else if (options.files) {
713
742
  files = [ String(options.files) ];
714
743
  }
@@ -727,6 +756,7 @@ function lintInput(options, synchronous, callback) {
727
756
  // @ts-ignore
728
757
  md.use(...plugin);
729
758
  });
759
+ const fs = options.fs || require("fs");
730
760
  const results = newResults(ruleList);
731
761
  let done = false;
732
762
  // Linting of strings is always synchronous
@@ -766,6 +796,7 @@ function lintInput(options, synchronous, callback) {
766
796
  handleRuleFailures,
767
797
  noInlineConfig,
768
798
  resultVersion,
799
+ fs,
769
800
  synchronous,
770
801
  syncCallback
771
802
  );
@@ -790,6 +821,7 @@ function lintInput(options, synchronous, callback) {
790
821
  handleRuleFailures,
791
822
  noInlineConfig,
792
823
  resultVersion,
824
+ fs,
793
825
  synchronous,
794
826
  (err, result) => {
795
827
  concurrency--;
@@ -833,7 +865,7 @@ function markdownlint(options, callback) {
833
865
  return lintInput(options, false, callback);
834
866
  }
835
867
 
836
- const markdownlintPromisify = util.promisify(markdownlint);
868
+ const markdownlintPromisify = promisify && promisify(markdownlint);
837
869
 
838
870
  /**
839
871
  * Lint specified Markdown files.
@@ -894,23 +926,91 @@ function parseConfiguration(name, content, parsers) {
894
926
  };
895
927
  }
896
928
 
929
+ /**
930
+ * Resolve referenced "extends" path in a configuration file
931
+ * using path.resolve() with require.resolve() as a fallback.
932
+ *
933
+ * @param {string} configFile Configuration file name.
934
+ * @param {string} referenceId Referenced identifier to resolve.
935
+ * @param {Object} fs File system implementation.
936
+ * @param {ResolveConfigExtendsCallback} [callback] Callback (err, result)
937
+ * function.
938
+ * @returns {void}
939
+ */
940
+ function resolveConfigExtends(configFile, referenceId, fs, callback) {
941
+ const configFileDirname = path.dirname(configFile);
942
+ const resolvedExtendsFile = path.resolve(configFileDirname, referenceId);
943
+ fs.access(resolvedExtendsFile, (err) => {
944
+ if (err) {
945
+ // Not a file, try require.resolve
946
+ try {
947
+ return callback(null, dynamicRequire.resolve(
948
+ referenceId,
949
+ { "paths": [ configFileDirname ] }
950
+ ));
951
+ } catch {
952
+ // Unable to resolve, use resolvedExtendsFile
953
+ }
954
+ }
955
+ return callback(null, resolvedExtendsFile);
956
+ });
957
+ }
958
+
959
+ /**
960
+ * Resolve referenced "extends" path in a configuration file
961
+ * using path.resolve() with require.resolve() as a fallback.
962
+ *
963
+ * @param {string} configFile Configuration file name.
964
+ * @param {string} referenceId Referenced identifier to resolve.
965
+ * @param {Object} fs File system implementation.
966
+ * @returns {string} Resolved path to file.
967
+ */
968
+ function resolveConfigExtendsSync(configFile, referenceId, fs) {
969
+ const configFileDirname = path.dirname(configFile);
970
+ const resolvedExtendsFile = path.resolve(configFileDirname, referenceId);
971
+ try {
972
+ fs.accessSync(resolvedExtendsFile);
973
+ return resolvedExtendsFile;
974
+ } catch {
975
+ // Not a file, try require.resolve
976
+ }
977
+ try {
978
+ return dynamicRequire.resolve(
979
+ referenceId,
980
+ { "paths": [ configFileDirname ] }
981
+ );
982
+ } catch {
983
+ // Unable to resolve, return resolvedExtendsFile
984
+ }
985
+ return resolvedExtendsFile;
986
+ }
987
+
897
988
  /**
898
989
  * Read specified configuration file.
899
990
  *
900
991
  * @param {string} file Configuration file name.
901
992
  * @param {ConfigurationParser[] | ReadConfigCallback} parsers Parsing
902
993
  * function(s).
994
+ * @param {Object} [fs] File system implementation.
903
995
  * @param {ReadConfigCallback} [callback] Callback (err, result) function.
904
996
  * @returns {void}
905
997
  */
906
- function readConfig(file, parsers, callback) {
998
+ function readConfig(file, parsers, fs, callback) {
907
999
  if (!callback) {
908
- // @ts-ignore
909
- callback = parsers;
910
- parsers = null;
1000
+ if (fs) {
1001
+ callback = fs;
1002
+ fs = null;
1003
+ } else {
1004
+ // @ts-ignore
1005
+ callback = parsers;
1006
+ parsers = null;
1007
+ }
1008
+ }
1009
+ if (!fs) {
1010
+ fs = require("fs");
911
1011
  }
912
1012
  // Read file
913
- fs.readFile(file, helpers.utf8Encoding, (err, content) => {
1013
+ fs.readFile(file, "utf8", (err, content) => {
914
1014
  if (err) {
915
1015
  return callback(err);
916
1016
  }
@@ -924,33 +1024,43 @@ function readConfig(file, parsers, callback) {
924
1024
  const configExtends = config.extends;
925
1025
  if (configExtends) {
926
1026
  delete config.extends;
927
- const extendsFile = path.resolve(path.dirname(file), configExtends);
928
- return readConfig(extendsFile, parsers, (errr, extendsConfig) => {
929
- if (errr) {
930
- return callback(errr);
931
- }
932
- return callback(null, {
933
- ...extendsConfig,
934
- ...config
935
- });
936
- });
1027
+ return resolveConfigExtends(
1028
+ file,
1029
+ configExtends,
1030
+ fs,
1031
+ (_, resolvedExtends) => readConfig(
1032
+ resolvedExtends,
1033
+ parsers,
1034
+ fs,
1035
+ (errr, extendsConfig) => {
1036
+ if (errr) {
1037
+ return callback(errr);
1038
+ }
1039
+ return callback(null, {
1040
+ ...extendsConfig,
1041
+ ...config
1042
+ });
1043
+ }
1044
+ )
1045
+ );
937
1046
  }
938
1047
  return callback(null, config);
939
1048
  });
940
1049
  }
941
1050
 
942
- const readConfigPromisify = util.promisify(readConfig);
1051
+ const readConfigPromisify = promisify && promisify(readConfig);
943
1052
 
944
1053
  /**
945
1054
  * Read specified configuration file.
946
1055
  *
947
1056
  * @param {string} file Configuration file name.
948
1057
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
1058
+ * @param {Object} [fs] File system implementation.
949
1059
  * @returns {Promise<Configuration>} Configuration object.
950
1060
  */
951
- function readConfigPromise(file, parsers) {
1061
+ function readConfigPromise(file, parsers, fs) {
952
1062
  // @ts-ignore
953
- return readConfigPromisify(file, parsers);
1063
+ return readConfigPromisify(file, parsers, fs);
954
1064
  }
955
1065
 
956
1066
  /**
@@ -958,12 +1068,16 @@ function readConfigPromise(file, parsers) {
958
1068
  *
959
1069
  * @param {string} file Configuration file name.
960
1070
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
1071
+ * @param {Object} [fs] File system implementation.
961
1072
  * @returns {Configuration} Configuration object.
1073
+ * @throws An Error if processing fails.
962
1074
  */
963
- function readConfigSync(file, parsers) {
1075
+ function readConfigSync(file, parsers, fs) {
1076
+ if (!fs) {
1077
+ fs = require("fs");
1078
+ }
964
1079
  // Read file
965
- // @ts-ignore
966
- const content = fs.readFileSync(file, helpers.utf8Encoding);
1080
+ const content = fs.readFileSync(file, "utf8");
967
1081
  // Try to parse file
968
1082
  const { config, message } = parseConfiguration(file, content, parsers);
969
1083
  if (!config) {
@@ -973,11 +1087,9 @@ function readConfigSync(file, parsers) {
973
1087
  const configExtends = config.extends;
974
1088
  if (configExtends) {
975
1089
  delete config.extends;
1090
+ const resolvedExtends = resolveConfigExtendsSync(file, configExtends, fs);
976
1091
  return {
977
- ...readConfigSync(
978
- path.resolve(path.dirname(file), configExtends),
979
- parsers
980
- ),
1092
+ ...readConfigSync(resolvedExtends, parsers, fs),
981
1093
  ...config
982
1094
  };
983
1095
  }
@@ -1020,7 +1132,7 @@ module.exports = markdownlint;
1020
1132
  *
1021
1133
  * @typedef {Object} RuleParams
1022
1134
  * @property {string} name File/string name.
1023
- * @property {MarkdownItToken[]} tokens markdown-it token objects.
1135
+ * @property {MarkdownItToken[]} tokens Token objects from markdown-it.
1024
1136
  * @property {string[]} lines File/string lines.
1025
1137
  * @property {string[]} frontMatterLines Front matter lines.
1026
1138
  * @property {RuleConfiguration} config Rule configuration.
@@ -1100,10 +1212,11 @@ module.exports = markdownlint;
1100
1212
  * @property {boolean} [noInlineConfig] True to ignore HTML directives.
1101
1213
  * @property {number} [resultVersion] Results object version.
1102
1214
  * @property {Plugin[]} [markdownItPlugins] Additional plugins.
1215
+ * @property {Object} [fs] File system implementation.
1103
1216
  */
1104
1217
 
1105
1218
  /**
1106
- * markdown-it plugin.
1219
+ * A markdown-it plugin.
1107
1220
  *
1108
1221
  * @typedef {Array} Plugin
1109
1222
  */
@@ -1134,20 +1247,21 @@ module.exports = markdownlint;
1134
1247
  * @property {string} errorDetail Detail about the error.
1135
1248
  * @property {string} errorContext Context for the error.
1136
1249
  * @property {number[]} errorRange Column number (1-based) and length.
1137
- * @property {FixInfo} fixInfo Fix information.
1250
+ * @property {FixInfo} [fixInfo] Fix information.
1138
1251
  */
1139
1252
 
1140
1253
  /**
1141
1254
  * Fix information.
1142
1255
  *
1143
1256
  * @typedef {Object} FixInfo
1257
+ * @property {number} [lineNumber] Line number (1-based).
1144
1258
  * @property {number} [editColumn] Column of the fix (1-based).
1145
1259
  * @property {number} [deleteCount] Count of characters to delete.
1146
1260
  * @property {string} [insertText] Text to insert (after deleting).
1147
1261
  */
1148
1262
 
1149
1263
  /**
1150
- * Called with the result of the lint operation.
1264
+ * Called with the result of the lint function.
1151
1265
  *
1152
1266
  * @callback LintCallback
1153
1267
  * @param {Error | null} err Error object or null.
@@ -1177,10 +1291,19 @@ module.exports = markdownlint;
1177
1291
  */
1178
1292
 
1179
1293
  /**
1180
- * Called with the result of the readConfig operation.
1294
+ * Called with the result of the readConfig function.
1181
1295
  *
1182
1296
  * @callback ReadConfigCallback
1183
1297
  * @param {Error | null} err Error object or null.
1184
1298
  * @param {Configuration} [config] Configuration object.
1185
1299
  * @returns {void}
1186
1300
  */
1301
+
1302
+ /**
1303
+ * Called with the result of the resolveConfigExtends function.
1304
+ *
1305
+ * @callback ResolveConfigExtendsCallback
1306
+ * @param {Error | null} err Error object or null.
1307
+ * @param {string} [path] Resolved path to file.
1308
+ * @returns {void}
1309
+ */
package/lib/md002.js CHANGED
@@ -6,7 +6,7 @@ const { addErrorDetailIf } = require("../helpers");
6
6
 
7
7
  module.exports = {
8
8
  "names": [ "MD002", "first-heading-h1", "first-header-h1" ],
9
- "description": "First heading should be a top level heading",
9
+ "description": "First heading should be a top-level heading",
10
10
  "tags": [ "headings", "headers" ],
11
11
  "function": function MD002(params, onError) {
12
12
  const level = Number(params.config.level || 1);
package/lib/md004.js CHANGED
@@ -2,10 +2,22 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, listItemMarkerRe,
6
- rangeFromRegExp, unorderedListStyleFor } = require("../helpers");
5
+ const { addErrorDetailIf, listItemMarkerRe, unorderedListStyleFor } =
6
+ require("../helpers");
7
7
  const { flattenedLists } = require("./cache");
8
8
 
9
+ const expectedStyleToMarker = {
10
+ "dash": "-",
11
+ "plus": "+",
12
+ "asterisk": "*"
13
+ };
14
+ const differentItemStyle = {
15
+ "dash": "plus",
16
+ "plus": "asterisk",
17
+ "asterisk": "dash"
18
+ };
19
+ const validStyles = Object.keys(expectedStyleToMarker);
20
+
9
21
  module.exports = {
10
22
  "names": [ "MD004", "ul-style" ],
11
23
  "description": "Unordered list style",
@@ -23,19 +35,40 @@ module.exports = {
23
35
  const itemStyle = unorderedListStyleFor(item);
24
36
  if (style === "sublist") {
25
37
  const nesting = list.nesting;
26
- if (!nestingStyles[nesting] &&
27
- (itemStyle !== nestingStyles[nesting - 1])) {
28
- nestingStyles[nesting] = itemStyle;
29
- } else {
30
- addErrorDetailIf(onError, item.lineNumber,
31
- nestingStyles[nesting], itemStyle, null, null,
32
- rangeFromRegExp(item.line, listItemMarkerRe));
38
+ if (!nestingStyles[nesting]) {
39
+ nestingStyles[nesting] =
40
+ (itemStyle === nestingStyles[nesting - 1]) ?
41
+ differentItemStyle[itemStyle] :
42
+ itemStyle;
33
43
  }
34
- } else {
35
- addErrorDetailIf(onError, item.lineNumber,
36
- expectedStyle, itemStyle, null, null,
37
- rangeFromRegExp(item.line, listItemMarkerRe));
44
+ expectedStyle = nestingStyles[nesting];
45
+ }
46
+ if (!validStyles.includes(expectedStyle)) {
47
+ expectedStyle = validStyles[0];
48
+ }
49
+ let range = null;
50
+ let fixInfo = null;
51
+ const match = item.line.match(listItemMarkerRe);
52
+ if (match) {
53
+ const column = match.index + 1;
54
+ const length = match[0].length;
55
+ range = [ column, length ];
56
+ fixInfo = {
57
+ "editColumn": match[1].length + 1,
58
+ "deleteCount": 1,
59
+ "insertText": expectedStyleToMarker[expectedStyle]
60
+ };
38
61
  }
62
+ addErrorDetailIf(
63
+ onError,
64
+ item.lineNumber,
65
+ expectedStyle,
66
+ itemStyle,
67
+ null,
68
+ null,
69
+ range,
70
+ fixInfo
71
+ );
39
72
  });
40
73
  }
41
74
  });
package/lib/md006.js CHANGED
@@ -25,7 +25,7 @@ module.exports = {
25
25
  null,
26
26
  rangeFromRegExp(line, listItemMarkerRe),
27
27
  {
28
- "deleteCount": line.length - line.trimLeft().length
28
+ "deleteCount": line.length - line.trimStart().length
29
29
  });
30
30
  });
31
31
  }
package/lib/md009.js CHANGED
@@ -47,30 +47,32 @@ module.exports = {
47
47
  codeInlineLineNumbers.sort(numericSortAscending);
48
48
  }
49
49
  const expected = (brSpaces < 2) ? 0 : brSpaces;
50
- let inFencedCode = 0;
51
- forEachLine(lineMetadata(), (line, lineIndex, inCode, onFence) => {
52
- inFencedCode += onFence;
50
+ forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
53
51
  const lineNumber = lineIndex + 1;
54
- const trailingSpaces = line.length - line.trimRight().length;
55
- if ((!inCode || inFencedCode) && trailingSpaces &&
56
- !includesSorted(listItemLineNumbers, lineNumber)) {
57
- if ((expected !== trailingSpaces) ||
58
- (strict &&
59
- (!includesSorted(paragraphLineNumbers, lineNumber) ||
60
- includesSorted(codeInlineLineNumbers, lineNumber)))) {
61
- const column = line.length - trailingSpaces + 1;
62
- addError(
63
- onError,
64
- lineNumber,
65
- "Expected: " + (expected === 0 ? "" : "0 or ") +
66
- expected + "; Actual: " + trailingSpaces,
67
- null,
68
- [ column, trailingSpaces ],
69
- {
70
- "editColumn": column,
71
- "deleteCount": trailingSpaces
72
- });
73
- }
52
+ const trailingSpaces = line.length - line.trimEnd().length;
53
+ if (
54
+ trailingSpaces &&
55
+ !inCode &&
56
+ !includesSorted(listItemLineNumbers, lineNumber) &&
57
+ (
58
+ (expected !== trailingSpaces) ||
59
+ (strict &&
60
+ (!includesSorted(paragraphLineNumbers, lineNumber) ||
61
+ includesSorted(codeInlineLineNumbers, lineNumber)))
62
+ )
63
+ ) {
64
+ const column = line.length - trailingSpaces + 1;
65
+ addError(
66
+ onError,
67
+ lineNumber,
68
+ "Expected: " + (expected === 0 ? "" : "0 or ") +
69
+ expected + "; Actual: " + trailingSpaces,
70
+ null,
71
+ [ column, trailingSpaces ],
72
+ {
73
+ "editColumn": column,
74
+ "deleteCount": trailingSpaces
75
+ });
74
76
  }
75
77
  });
76
78
  }
package/lib/md010.js CHANGED
@@ -14,6 +14,10 @@ module.exports = {
14
14
  "function": function MD010(params, onError) {
15
15
  const codeBlocks = params.config.code_blocks;
16
16
  const includeCodeBlocks = (codeBlocks === undefined) ? true : !!codeBlocks;
17
+ const spacesPerTab = params.config.spaces_per_tab;
18
+ const spaceMultiplier = (spacesPerTab === undefined) ?
19
+ 1 :
20
+ Math.max(0, Number(spacesPerTab));
17
21
  forEachLine(lineMetadata(), (line, lineIndex, inCode) => {
18
22
  if (!inCode || includeCodeBlocks) {
19
23
  let match = null;
@@ -29,7 +33,7 @@ module.exports = {
29
33
  {
30
34
  "editColumn": column,
31
35
  "deleteCount": length,
32
- "insertText": "".padEnd(length)
36
+ "insertText": "".padEnd(length * spaceMultiplier)
33
37
  });
34
38
  }
35
39
  }