markdownlint 0.23.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.
@@ -0,0 +1,23 @@
1
+ # Using markdownlint with Prettier
2
+
3
+ [`Prettier`](https://prettier.io) is a popular code formatter. For the most part,
4
+ it works seamlessly with `markdownlint`. Special situations are documented
5
+ below.
6
+
7
+ ## List item indentation
8
+
9
+ The default settings of `markdownlint` and `Prettier` are compatible and don't
10
+ result in any linting violations. If `Prettier` is used with `--tab-width` set
11
+ to `4`, the following `markdownlint` configuration can be used:
12
+
13
+ ```json
14
+ {
15
+ "MD007": {
16
+ "indent": 4
17
+ },
18
+ "MD030": {
19
+ "ul_single": 3,
20
+ "ul_multi": 3
21
+ }
22
+ }
23
+ ```
package/doc/Rules.md CHANGED
@@ -329,6 +329,8 @@ issue for multi-markdown parsers, which require 4-space indents. More informatio
329
329
  <https://cirosantilli.com/markdown-style-guide#indentation-of-content-inside-lists>
330
330
  and <http://support.markedapp.com/discussions/problems/21-sub-lists-not-indenting>.
331
331
 
332
+ Note: See [Prettier.md](Prettier.md) for compatibility information.
333
+
332
334
  <a name="md009"></a>
333
335
 
334
336
  ## MD009 - Trailing spaces
@@ -388,7 +390,7 @@ Tags: whitespace, hard_tab
388
390
 
389
391
  Aliases: no-hard-tabs
390
392
 
391
- Parameters: code_blocks (boolean; default true)
393
+ Parameters: code_blocks, spaces_per_tab (boolean; default true, number; default 1)
392
394
 
393
395
  Fixable: Most violations can be fixed by tooling
394
396
 
@@ -420,6 +422,9 @@ You have the option to exclude this rule for code blocks. To do so, set the
420
422
  `code_blocks` parameter to `false`. Code blocks are included by default since
421
423
  handling of tabs by tools is often inconsistent (ex: using 4 vs. 8 spaces).
422
424
 
425
+ If you would like the fixer to change tabs to x spaces, then configure the `spaces_per_tab`
426
+ parameter to the number x. The default value would be 1.
427
+
423
428
  Rationale: Hard tabs are often rendered inconsistently by different editors and
424
429
  can be harder to work with than spaces.
425
430
 
@@ -1180,6 +1185,8 @@ for your selected document style.
1180
1185
 
1181
1186
  Rationale: Violations of this rule can lead to improperly rendered content.
1182
1187
 
1188
+ Note: See [Prettier.md](Prettier.md) for compatibility information.
1189
+
1183
1190
  <a name="md031"></a>
1184
1191
 
1185
1192
  ## MD031 - Fenced code blocks should be surrounded by blank lines
@@ -1569,6 +1576,14 @@ echo Hello world
1569
1576
  ```
1570
1577
  ````
1571
1578
 
1579
+ To display a code block without syntax highlighting, use:
1580
+
1581
+ ````markdown
1582
+ ```text
1583
+ Plain text in a code block
1584
+ ```
1585
+ ````
1586
+
1572
1587
  Rationale: Specifying a language improves content rendering by using the
1573
1588
  correct syntax highlighting for code. More information:
1574
1589
  <https://cirosantilli.com/markdown-style-guide#option-code-fenced>.
@@ -29,10 +29,11 @@ module.exports.orderedListItemMarkerRe = /^[\s>]*0*(\d+)[.)]/;
29
29
  const emphasisMarkersRe = /[_*]/g;
30
30
 
31
31
  // Regular expression for inline links and shortcut reference links
32
- const linkRe = /\[(?:[^[\]]|\[[^\]]*\])*\](?:\(\S*\))?/g;
32
+ const linkRe = /(\[(?:[^[\]]|\[[^\]]*\])*\])(\(\S*\)|\[\S*\])?/g;
33
+ module.exports.linkRe = linkRe;
33
34
 
34
- // readFile options for reading with the UTF-8 encoding
35
- module.exports.utf8Encoding = "utf8";
35
+ // Regular expression for link reference definition lines
36
+ module.exports.linkReferenceRe = /^ {0,3}\[[^\]]+]:\s.*$/;
36
37
 
37
38
  // All punctuation characters (normal and full-width)
38
39
  const allPunctuation = ".,;:!?。,;:!?";
@@ -302,14 +303,14 @@ module.exports.forEachLine = function forEachLine(lineMetadata, handler) {
302
303
  };
303
304
 
304
305
  // Returns (nested) lists as a flat array (in order)
305
- module.exports.flattenLists = function flattenLists(params) {
306
+ module.exports.flattenLists = function flattenLists(tokens) {
306
307
  const flattenedLists = [];
307
308
  const stack = [];
308
309
  let current = null;
309
310
  let nesting = 0;
310
311
  const nestingStack = [];
311
312
  let lastWithMap = { "map": [ 0, 1 ] };
312
- params.tokens.forEach((token) => {
313
+ tokens.forEach((token) => {
313
314
  if (isMathBlock(token) && token.map[1]) {
314
315
  // markdown-it-texmath plugin does not account for math_block_end
315
316
  token.map[1]++;
@@ -519,6 +520,47 @@ module.exports.addErrorContext = function addErrorContext(
519
520
  addError(onError, lineNumber, null, context, range, fixInfo);
520
521
  };
521
522
 
523
+ /**
524
+ * Returns an array of code span ranges.
525
+ *
526
+ * @param {string[]} lines Lines to scan for code span ranges.
527
+ * @returns {number[][]} Array of ranges (line, index, length).
528
+ */
529
+ module.exports.inlineCodeSpanRanges = (lines) => {
530
+ const exclusions = [];
531
+ forEachInlineCodeSpan(
532
+ lines.join("\n"),
533
+ (code, lineIndex, columnIndex) => {
534
+ const codeLines = code.split(newLineRe);
535
+ // eslint-disable-next-line unicorn/no-for-loop
536
+ for (let i = 0; i < codeLines.length; i++) {
537
+ exclusions.push(
538
+ [ lineIndex + i, columnIndex, codeLines[i].length ]
539
+ );
540
+ columnIndex = 0;
541
+ }
542
+ }
543
+ );
544
+ return exclusions;
545
+ };
546
+
547
+ /**
548
+ * Determines whether the specified range overlaps another range.
549
+ *
550
+ * @param {number[][]} ranges Array of ranges (line, index, length).
551
+ * @param {number} lineIndex Line index to check.
552
+ * @param {number} index Index to check.
553
+ * @param {number} length Length to check.
554
+ * @returns {boolean} True iff the specified range overlaps.
555
+ */
556
+ module.exports.overlapsAnyRange = (ranges, lineIndex, index, length) => (
557
+ !ranges.every((span) => (
558
+ (lineIndex !== span[0]) ||
559
+ (index + length < span[1]) ||
560
+ (index > span[1] + span[2])
561
+ ))
562
+ );
563
+
522
564
  // Returns a range object for a line by applying a RegExp
523
565
  module.exports.rangeFromRegExp = function rangeFromRegExp(line, regexp) {
524
566
  let range = null;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint-rule-helpers",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "A collection of markdownlint helper functions for custom rules",
5
5
  "main": "helpers.js",
6
6
  "author": "David Anson (https://dlaa.me/)",
package/lib/cache.js CHANGED
@@ -2,14 +2,6 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- let lineMetadata = null;
6
- module.exports.lineMetadata = (value) => {
7
- if (value) {
8
- lineMetadata = value;
9
- }
10
- return lineMetadata;
11
- };
12
-
13
5
  let flattenedLists = null;
14
6
  module.exports.flattenedLists = (value) => {
15
7
  if (value) {
@@ -18,7 +10,24 @@ module.exports.flattenedLists = (value) => {
18
10
  return flattenedLists;
19
11
  };
20
12
 
13
+ let inlineCodeSpanRanges = null;
14
+ module.exports.inlineCodeSpanRanges = (value) => {
15
+ if (value) {
16
+ inlineCodeSpanRanges = value;
17
+ }
18
+ return inlineCodeSpanRanges;
19
+ };
20
+
21
+ let lineMetadata = null;
22
+ module.exports.lineMetadata = (value) => {
23
+ if (value) {
24
+ lineMetadata = value;
25
+ }
26
+ return lineMetadata;
27
+ };
28
+
21
29
  module.exports.clear = () => {
22
- lineMetadata = null;
23
30
  flattenedLists = null;
31
+ inlineCodeSpanRanges = null;
32
+ lineMetadata = null;
24
33
  };
@@ -8,7 +8,7 @@ export = markdownlint;
8
8
  */
9
9
  declare function markdownlint(options: Options, callback: LintCallback): void;
10
10
  declare namespace markdownlint {
11
- export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback };
11
+ export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
12
12
  }
13
13
  /**
14
14
  * Configuration options.
@@ -52,9 +52,13 @@ type Options = {
52
52
  * Additional plugins.
53
53
  */
54
54
  markdownItPlugins?: Plugin[];
55
+ /**
56
+ * File system implementation.
57
+ */
58
+ fs?: any;
55
59
  };
56
60
  /**
57
- * Called with the result of the lint operation.
61
+ * Called with the result of the lint function.
58
62
  */
59
63
  type LintCallback = (err: Error | null, results?: LintResults) => void;
60
64
  /**
@@ -70,18 +74,21 @@ declare function markdownlintSync(options: Options): LintResults;
70
74
  * @param {string} file Configuration file name.
71
75
  * @param {ConfigurationParser[] | ReadConfigCallback} parsers Parsing
72
76
  * function(s).
77
+ * @param {Object} [fs] File system implementation.
73
78
  * @param {ReadConfigCallback} [callback] Callback (err, result) function.
74
79
  * @returns {void}
75
80
  */
76
- declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, callback?: ReadConfigCallback): void;
81
+ declare function readConfig(file: string, parsers: ConfigurationParser[] | ReadConfigCallback, fs?: any, callback?: ReadConfigCallback): void;
77
82
  /**
78
83
  * Read specified configuration file synchronously.
79
84
  *
80
85
  * @param {string} file Configuration file name.
81
86
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
87
+ * @param {Object} [fs] File system implementation.
82
88
  * @returns {Configuration} Configuration object.
89
+ * @throws An Error if processing fails.
83
90
  */
84
- declare function readConfigSync(file: string, parsers?: ConfigurationParser[]): Configuration;
91
+ declare function readConfigSync(file: string, parsers?: ConfigurationParser[], fs?: any): Configuration;
85
92
  /**
86
93
  * Gets the (semantic) version of the library.
87
94
  *
@@ -316,6 +323,10 @@ type LintError = {
316
323
  * Fix information.
317
324
  */
318
325
  type FixInfo = {
326
+ /**
327
+ * Line number (1-based).
328
+ */
329
+ lineNumber?: number;
319
330
  /**
320
331
  * Column of the fix (1-based).
321
332
  */
@@ -334,20 +345,24 @@ type FixInfo = {
334
345
  * {@link ../schema/markdownlint-config-schema.json}.
335
346
  */
336
347
  type Configuration = {
337
- [x: string]: any;
348
+ [x: string]: RuleConfiguration;
338
349
  };
339
350
  /**
340
351
  * Rule configuration.
341
352
  */
342
- type RuleConfiguration = any;
353
+ type RuleConfiguration = boolean | any;
343
354
  /**
344
355
  * Parses a configuration string and returns a configuration object.
345
356
  */
346
357
  type ConfigurationParser = (text: string) => Configuration;
347
358
  /**
348
- * Called with the result of the readConfig operation.
359
+ * Called with the result of the readConfig function.
349
360
  */
350
361
  type ReadConfigCallback = (err: Error | null, config?: Configuration) => void;
362
+ /**
363
+ * Called with the result of the resolveConfigExtends function.
364
+ */
365
+ type ResolveConfigExtendsCallback = (err: Error | null, path?: string) => void;
351
366
  /**
352
367
  * Lint specified Markdown files.
353
368
  *
@@ -360,6 +375,7 @@ declare function markdownlintPromise(options: Options): Promise<LintResults>;
360
375
  *
361
376
  * @param {string} file Configuration file name.
362
377
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
378
+ * @param {Object} [fs] File system implementation.
363
379
  * @returns {Promise<Configuration>} Configuration object.
364
380
  */
365
- declare function readConfigPromise(file: string, parsers?: ConfigurationParser[]): Promise<Configuration>;
381
+ declare function readConfigPromise(file: string, parsers?: ConfigurationParser[], fs?: any): Promise<Configuration>;
@@ -2,7 +2,6 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const fs = require("fs");
6
5
  const path = require("path");
7
6
  const { promisify } = require("util");
8
7
  const markdownIt = require("markdown-it");
@@ -190,7 +189,7 @@ function annotateTokens(tokens, lines) {
190
189
  (token.type === "thead_open") ||
191
190
  (token.type === "tbody_open")
192
191
  ) {
193
- tableMap = token.map.slice();
192
+ tableMap = [ ...token.map ];
194
193
  } else if (
195
194
  (token.type === "tr_close") &&
196
195
  tableMap
@@ -203,7 +202,7 @@ function annotateTokens(tokens, lines) {
203
202
  tableMap = null;
204
203
  }
205
204
  if (tableMap && !token.map) {
206
- token.map = tableMap.slice();
205
+ token.map = [ ...tableMap ];
207
206
  }
208
207
  // Update token metadata
209
208
  if (token.map) {
@@ -516,7 +515,8 @@ function lintContent(
516
515
  frontMatterLines
517
516
  };
518
517
  cache.lineMetadata(helpers.getLineMetadata(params));
519
- cache.flattenedLists(helpers.flattenLists(params));
518
+ cache.flattenedLists(helpers.flattenLists(params.tokens));
519
+ cache.inlineCodeSpanRanges(helpers.inlineCodeSpanRanges(params.lines));
520
520
  // Function to run for each rule
521
521
  const result = (resultVersion === 0) ? {} : [];
522
522
  // eslint-disable-next-line jsdoc/require-jsdoc
@@ -684,6 +684,7 @@ function lintContent(
684
684
  * @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
685
685
  * @param {boolean} noInlineConfig Whether to allow inline configuration.
686
686
  * @param {number} resultVersion Version of the LintResults object to return.
687
+ * @param {Object} fs File system implementation.
687
688
  * @param {boolean} synchronous Whether to execute synchronously.
688
689
  * @param {Function} callback Callback (err, result) function.
689
690
  * @returns {void}
@@ -697,6 +698,7 @@ function lintFile(
697
698
  handleRuleFailures,
698
699
  noInlineConfig,
699
700
  resultVersion,
701
+ fs,
700
702
  synchronous,
701
703
  callback) {
702
704
  // eslint-disable-next-line jsdoc/require-jsdoc
@@ -709,10 +711,9 @@ function lintFile(
709
711
  }
710
712
  // Make a/synchronous call to read file
711
713
  if (synchronous) {
712
- // @ts-ignore
713
- lintContentWrapper(null, fs.readFileSync(file, helpers.utf8Encoding));
714
+ lintContentWrapper(null, fs.readFileSync(file, "utf8"));
714
715
  } else {
715
- fs.readFile(file, helpers.utf8Encoding, lintContentWrapper);
716
+ fs.readFile(file, "utf8", lintContentWrapper);
716
717
  }
717
718
  }
718
719
 
@@ -736,7 +737,7 @@ function lintInput(options, synchronous, callback) {
736
737
  }
737
738
  let files = [];
738
739
  if (Array.isArray(options.files)) {
739
- files = options.files.slice();
740
+ files = [ ...options.files ];
740
741
  } else if (options.files) {
741
742
  files = [ String(options.files) ];
742
743
  }
@@ -755,6 +756,7 @@ function lintInput(options, synchronous, callback) {
755
756
  // @ts-ignore
756
757
  md.use(...plugin);
757
758
  });
759
+ const fs = options.fs || require("fs");
758
760
  const results = newResults(ruleList);
759
761
  let done = false;
760
762
  // Linting of strings is always synchronous
@@ -794,6 +796,7 @@ function lintInput(options, synchronous, callback) {
794
796
  handleRuleFailures,
795
797
  noInlineConfig,
796
798
  resultVersion,
799
+ fs,
797
800
  synchronous,
798
801
  syncCallback
799
802
  );
@@ -818,6 +821,7 @@ function lintInput(options, synchronous, callback) {
818
821
  handleRuleFailures,
819
822
  noInlineConfig,
820
823
  resultVersion,
824
+ fs,
821
825
  synchronous,
822
826
  (err, result) => {
823
827
  concurrency--;
@@ -928,17 +932,47 @@ function parseConfiguration(name, content, parsers) {
928
932
  *
929
933
  * @param {string} configFile Configuration file name.
930
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.
931
966
  * @returns {string} Resolved path to file.
932
967
  */
933
- function resolveConfigExtends(configFile, referenceId) {
968
+ function resolveConfigExtendsSync(configFile, referenceId, fs) {
934
969
  const configFileDirname = path.dirname(configFile);
935
970
  const resolvedExtendsFile = path.resolve(configFileDirname, referenceId);
936
971
  try {
937
- if (fs.statSync(resolvedExtendsFile).isFile()) {
938
- return resolvedExtendsFile;
939
- }
972
+ fs.accessSync(resolvedExtendsFile);
973
+ return resolvedExtendsFile;
940
974
  } catch {
941
- // If not a file or fs.statSync throws, try require.resolve
975
+ // Not a file, try require.resolve
942
976
  }
943
977
  try {
944
978
  return dynamicRequire.resolve(
@@ -946,7 +980,7 @@ function resolveConfigExtends(configFile, referenceId) {
946
980
  { "paths": [ configFileDirname ] }
947
981
  );
948
982
  } catch {
949
- // If require.resolve throws, return resolvedExtendsFile
983
+ // Unable to resolve, return resolvedExtendsFile
950
984
  }
951
985
  return resolvedExtendsFile;
952
986
  }
@@ -957,17 +991,26 @@ function resolveConfigExtends(configFile, referenceId) {
957
991
  * @param {string} file Configuration file name.
958
992
  * @param {ConfigurationParser[] | ReadConfigCallback} parsers Parsing
959
993
  * function(s).
994
+ * @param {Object} [fs] File system implementation.
960
995
  * @param {ReadConfigCallback} [callback] Callback (err, result) function.
961
996
  * @returns {void}
962
997
  */
963
- function readConfig(file, parsers, callback) {
998
+ function readConfig(file, parsers, fs, callback) {
964
999
  if (!callback) {
965
- // @ts-ignore
966
- callback = parsers;
967
- 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");
968
1011
  }
969
1012
  // Read file
970
- fs.readFile(file, helpers.utf8Encoding, (err, content) => {
1013
+ fs.readFile(file, "utf8", (err, content) => {
971
1014
  if (err) {
972
1015
  return callback(err);
973
1016
  }
@@ -981,16 +1024,25 @@ function readConfig(file, parsers, callback) {
981
1024
  const configExtends = config.extends;
982
1025
  if (configExtends) {
983
1026
  delete config.extends;
984
- const resolvedExtends = resolveConfigExtends(file, configExtends);
985
- return readConfig(resolvedExtends, parsers, (errr, extendsConfig) => {
986
- if (errr) {
987
- return callback(errr);
988
- }
989
- return callback(null, {
990
- ...extendsConfig,
991
- ...config
992
- });
993
- });
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
+ );
994
1046
  }
995
1047
  return callback(null, config);
996
1048
  });
@@ -1003,11 +1055,12 @@ const readConfigPromisify = promisify && promisify(readConfig);
1003
1055
  *
1004
1056
  * @param {string} file Configuration file name.
1005
1057
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
1058
+ * @param {Object} [fs] File system implementation.
1006
1059
  * @returns {Promise<Configuration>} Configuration object.
1007
1060
  */
1008
- function readConfigPromise(file, parsers) {
1061
+ function readConfigPromise(file, parsers, fs) {
1009
1062
  // @ts-ignore
1010
- return readConfigPromisify(file, parsers);
1063
+ return readConfigPromisify(file, parsers, fs);
1011
1064
  }
1012
1065
 
1013
1066
  /**
@@ -1015,12 +1068,16 @@ function readConfigPromise(file, parsers) {
1015
1068
  *
1016
1069
  * @param {string} file Configuration file name.
1017
1070
  * @param {ConfigurationParser[]} [parsers] Parsing function(s).
1071
+ * @param {Object} [fs] File system implementation.
1018
1072
  * @returns {Configuration} Configuration object.
1073
+ * @throws An Error if processing fails.
1019
1074
  */
1020
- function readConfigSync(file, parsers) {
1075
+ function readConfigSync(file, parsers, fs) {
1076
+ if (!fs) {
1077
+ fs = require("fs");
1078
+ }
1021
1079
  // Read file
1022
- // @ts-ignore
1023
- const content = fs.readFileSync(file, helpers.utf8Encoding);
1080
+ const content = fs.readFileSync(file, "utf8");
1024
1081
  // Try to parse file
1025
1082
  const { config, message } = parseConfiguration(file, content, parsers);
1026
1083
  if (!config) {
@@ -1030,9 +1087,9 @@ function readConfigSync(file, parsers) {
1030
1087
  const configExtends = config.extends;
1031
1088
  if (configExtends) {
1032
1089
  delete config.extends;
1033
- const resolvedExtends = resolveConfigExtends(file, configExtends);
1090
+ const resolvedExtends = resolveConfigExtendsSync(file, configExtends, fs);
1034
1091
  return {
1035
- ...readConfigSync(resolvedExtends, parsers),
1092
+ ...readConfigSync(resolvedExtends, parsers, fs),
1036
1093
  ...config
1037
1094
  };
1038
1095
  }
@@ -1155,6 +1212,7 @@ module.exports = markdownlint;
1155
1212
  * @property {boolean} [noInlineConfig] True to ignore HTML directives.
1156
1213
  * @property {number} [resultVersion] Results object version.
1157
1214
  * @property {Plugin[]} [markdownItPlugins] Additional plugins.
1215
+ * @property {Object} [fs] File system implementation.
1158
1216
  */
1159
1217
 
1160
1218
  /**
@@ -1196,13 +1254,14 @@ module.exports = markdownlint;
1196
1254
  * Fix information.
1197
1255
  *
1198
1256
  * @typedef {Object} FixInfo
1257
+ * @property {number} [lineNumber] Line number (1-based).
1199
1258
  * @property {number} [editColumn] Column of the fix (1-based).
1200
1259
  * @property {number} [deleteCount] Count of characters to delete.
1201
1260
  * @property {string} [insertText] Text to insert (after deleting).
1202
1261
  */
1203
1262
 
1204
1263
  /**
1205
- * Called with the result of the lint operation.
1264
+ * Called with the result of the lint function.
1206
1265
  *
1207
1266
  * @callback LintCallback
1208
1267
  * @param {Error | null} err Error object or null.
@@ -1232,10 +1291,19 @@ module.exports = markdownlint;
1232
1291
  */
1233
1292
 
1234
1293
  /**
1235
- * Called with the result of the readConfig operation.
1294
+ * Called with the result of the readConfig function.
1236
1295
  *
1237
1296
  * @callback ReadConfigCallback
1238
1297
  * @param {Error | null} err Error object or null.
1239
1298
  * @param {Configuration} [config] Configuration object.
1240
1299
  * @returns {void}
1241
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/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
  }