markdownlint 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/md037.js CHANGED
@@ -3,9 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError } = require("../helpers");
6
-
7
- const emphasisStartTextRe = /^(\S{1,3})(\s+)\S/;
8
- const emphasisEndTextRe = /\S(\s+)(\S{1,3})$/;
6
+ const { filterByPredicate } = require("../helpers/micromark.cjs");
9
7
 
10
8
  module.exports = {
11
9
  "names": [ "MD037", "no-space-in-emphasis" ],
@@ -19,16 +17,11 @@ module.exports = {
19
17
  for (const marker of [ "_", "__", "___", "*", "**", "***" ]) {
20
18
  emphasisTokensByMarker.set(marker, []);
21
19
  }
22
- const pending = [ ...parsers.micromark.tokens ];
23
- let token = null;
24
- while ((token = pending.shift())) {
25
-
26
- // Use reparsed children of htmlFlow tokens
27
- if (token.type === "htmlFlow") {
28
- pending.unshift(...token.htmlFlowChildren);
29
- continue;
30
- }
31
- pending.push(...token.children);
20
+ const tokens = filterByPredicate(
21
+ parsers.micromark.tokens,
22
+ (token) => token.children.some((child) => child.type === "data")
23
+ );
24
+ for (const token of tokens) {
32
25
 
33
26
  // Build lists of bare tokens for each emphasis marker type
34
27
  for (const emphasisTokens of emphasisTokensByMarker.values()) {
@@ -45,51 +38,51 @@ module.exports = {
45
38
  }
46
39
 
47
40
  // Process bare tokens for each emphasis marker type
48
- for (const emphasisTokens of emphasisTokensByMarker.values()) {
41
+ for (const entry of emphasisTokensByMarker.entries()) {
42
+ const [ marker, emphasisTokens ] = entry;
49
43
  for (let i = 0; i + 1 < emphasisTokens.length; i += 2) {
50
44
 
51
45
  // Process start token of start/end pair
52
46
  const startToken = emphasisTokens[i];
53
- const startText =
54
- lines[startToken.startLine - 1].slice(startToken.startColumn - 1);
55
- const startMatch = startText.match(emphasisStartTextRe);
47
+ const startLine = lines[startToken.startLine - 1];
48
+ const startSlice = startLine.slice(startToken.endColumn - 1);
49
+ const startMatch = startSlice.match(/^\s+\S/);
56
50
  if (startMatch) {
57
- const [ startContext, startMarker, startSpaces ] = startMatch;
58
- if ((startMarker === startToken.text) && (startSpaces.length > 0)) {
59
- addError(
60
- onError,
61
- startToken.startLine,
62
- undefined,
63
- startContext,
64
- [ startToken.startColumn, startContext.length ],
65
- {
66
- "editColumn": startToken.endColumn,
67
- "deleteCount": startSpaces.length
68
- }
69
- );
70
- }
51
+ const [ startSpaceCharacter ] = startMatch;
52
+ const startContext = `${marker}${startSpaceCharacter}`;
53
+ addError(
54
+ onError,
55
+ startToken.startLine,
56
+ undefined,
57
+ startContext,
58
+ [ startToken.startColumn, startContext.length ],
59
+ {
60
+ "editColumn": startToken.endColumn,
61
+ "deleteCount": startSpaceCharacter.length - 1
62
+ }
63
+ );
71
64
  }
72
65
 
73
66
  // Process end token of start/end pair
74
67
  const endToken = emphasisTokens[i + 1];
75
- const endText =
76
- lines[endToken.startLine - 1].slice(0, endToken.endColumn - 1);
77
- const endMatch = endText.match(emphasisEndTextRe);
68
+ const endLine = lines[endToken.startLine - 1];
69
+ const endSlice = endLine.slice(0, endToken.startColumn - 1);
70
+ const endMatch = endSlice.match(/\S\s+$/);
78
71
  if (endMatch) {
79
- const [ endContext, endSpace, endMarker ] = endMatch;
80
- if ((endMarker === endToken.text) && (endSpace.length > 0)) {
81
- addError(
82
- onError,
83
- endToken.startLine,
84
- undefined,
85
- endContext,
86
- [ endToken.endColumn - endContext.length, endContext.length ],
87
- {
88
- "editColumn": endToken.startColumn - endSpace.length,
89
- "deleteCount": endSpace.length
90
- }
91
- );
92
- }
72
+ const [ endSpaceCharacter ] = endMatch;
73
+ const endContext = `${endSpaceCharacter}${marker}`;
74
+ addError(
75
+ onError,
76
+ endToken.startLine,
77
+ undefined,
78
+ endContext,
79
+ [ endToken.endColumn - endContext.length, endContext.length ],
80
+ {
81
+ "editColumn":
82
+ endToken.startColumn - (endSpaceCharacter.length - 1),
83
+ "deleteCount": endSpaceCharacter.length - 1
84
+ }
85
+ );
93
86
  }
94
87
  }
95
88
  }
package/lib/md043.js CHANGED
@@ -11,52 +11,54 @@ module.exports = {
11
11
  "tags": [ "headings", "headers" ],
12
12
  "function": function MD043(params, onError) {
13
13
  const requiredHeadings = params.config.headings || params.config.headers;
14
+ if (!Array.isArray(requiredHeadings)) {
15
+ // Nothing to check; avoid doing any work
16
+ return;
17
+ }
14
18
  const matchCase = params.config.match_case || false;
15
- if (Array.isArray(requiredHeadings)) {
16
- const levels = {};
17
- for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
18
- levels["h" + level] = "######".substr(-level);
19
- }
20
- let i = 0;
21
- let matchAny = false;
22
- let hasError = false;
23
- let anyHeadings = false;
24
- const getExpected = () => requiredHeadings[i++] || "[None]";
25
- const handleCase = (str) => (matchCase ? str : str.toLowerCase());
26
- forEachHeading(params, (heading, content) => {
27
- if (!hasError) {
28
- anyHeadings = true;
29
- const actual = levels[heading.tag] + " " + content;
30
- const expected = getExpected();
31
- if (expected === "*") {
32
- const nextExpected = getExpected();
33
- if (handleCase(nextExpected) !== handleCase(actual)) {
34
- matchAny = true;
35
- i--;
36
- }
37
- } else if (expected === "+") {
19
+ const levels = {};
20
+ for (const level of [ 1, 2, 3, 4, 5, 6 ]) {
21
+ levels["h" + level] = "######".substr(-level);
22
+ }
23
+ let i = 0;
24
+ let matchAny = false;
25
+ let hasError = false;
26
+ let anyHeadings = false;
27
+ const getExpected = () => requiredHeadings[i++] || "[None]";
28
+ const handleCase = (str) => (matchCase ? str : str.toLowerCase());
29
+ forEachHeading(params, (heading, content) => {
30
+ if (!hasError) {
31
+ anyHeadings = true;
32
+ const actual = levels[heading.tag] + " " + content;
33
+ const expected = getExpected();
34
+ if (expected === "*") {
35
+ const nextExpected = getExpected();
36
+ if (handleCase(nextExpected) !== handleCase(actual)) {
38
37
  matchAny = true;
39
- } else if (handleCase(expected) === handleCase(actual)) {
40
- matchAny = false;
41
- } else if (matchAny) {
42
38
  i--;
43
- } else {
44
- addErrorDetailIf(onError, heading.lineNumber,
45
- expected, actual);
46
- hasError = true;
47
39
  }
40
+ } else if (expected === "+") {
41
+ matchAny = true;
42
+ } else if (handleCase(expected) === handleCase(actual)) {
43
+ matchAny = false;
44
+ } else if (matchAny) {
45
+ i--;
46
+ } else {
47
+ addErrorDetailIf(onError, heading.lineNumber,
48
+ expected, actual);
49
+ hasError = true;
48
50
  }
49
- });
50
- const extraHeadings = requiredHeadings.length - i;
51
- if (
52
- !hasError &&
53
- ((extraHeadings > 1) ||
54
- ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
55
- (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
56
- ) {
57
- addErrorContext(onError, params.lines.length,
58
- requiredHeadings[i]);
59
51
  }
52
+ });
53
+ const extraHeadings = requiredHeadings.length - i;
54
+ if (
55
+ !hasError &&
56
+ ((extraHeadings > 1) ||
57
+ ((extraHeadings === 1) && (requiredHeadings[i] !== "*"))) &&
58
+ (anyHeadings || !requiredHeadings.every((heading) => heading === "*"))
59
+ ) {
60
+ addErrorContext(onError, params.lines.length,
61
+ requiredHeadings[i]);
60
62
  }
61
63
  }
62
64
  };
package/lib/md044.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { addErrorDetailIf, escapeForRegExp, newLineRe, withinAnyRange } =
5
+ const { addErrorDetailIf, escapeForRegExp, withinAnyRange } =
6
6
  require("../helpers");
7
7
  const { filterByPredicate, filterByTypes, parse } =
8
8
  require("../helpers/micromark.cjs");
@@ -19,46 +19,32 @@ module.exports = {
19
19
  let names = params.config.names;
20
20
  names = Array.isArray(names) ? names : [];
21
21
  names.sort((a, b) => (b.length - a.length) || a.localeCompare(b));
22
+ if (names.length === 0) {
23
+ // Nothing to check; avoid doing any work
24
+ return;
25
+ }
22
26
  const codeBlocks = params.config.code_blocks;
23
27
  const includeCodeBlocks =
24
28
  (codeBlocks === undefined) ? true : !!codeBlocks;
25
29
  const htmlElements = params.config.html_elements;
26
30
  const includeHtmlElements =
27
31
  (htmlElements === undefined) ? true : !!htmlElements;
28
- const scannedTypes = new Set([ "data", "htmlFlowData" ]);
32
+ const scannedTypes = new Set([ "data" ]);
29
33
  if (includeCodeBlocks) {
30
34
  scannedTypes.add("codeFlowValue");
31
35
  scannedTypes.add("codeTextData");
32
36
  }
33
- const tokenAdjustments = new Map();
37
+ if (includeHtmlElements) {
38
+ scannedTypes.add("htmlFlowData");
39
+ scannedTypes.add("htmlTextData");
40
+ }
34
41
  const contentTokens =
35
42
  filterByPredicate(
36
43
  params.parsers.micromark.tokens,
37
44
  (token) => scannedTypes.has(token.type),
38
- (token) => {
39
- let { children } = token;
40
- const { startLine, text } = token;
41
- if (!includeHtmlElements && (token.type === "htmlFlow")) {
42
- if (text.startsWith("<!--")) {
43
- // Remove comment content
44
- children = [];
45
- } else {
46
- // Re-parse to get htmlText elements for detailed tokenization
47
- const htmlTextLines =
48
- `<md044>\n${text}\n</md044>`.split(newLineRe);
49
- children = parse(htmlTextLines.join(""));
50
- const reTokens = [ ...children ];
51
- for (const reToken of reTokens) {
52
- tokenAdjustments.set(reToken, {
53
- htmlTextLines,
54
- startLine
55
- });
56
- reTokens.push(...reToken.children);
57
- }
58
- }
59
- }
60
- return children.filter((t) => !ignoredChildTypes.has(t.type));
61
- }
45
+ (token) => (
46
+ token.children.filter((t) => !ignoredChildTypes.has(t.type))
47
+ )
62
48
  );
63
49
  const exclusions = [];
64
50
  const autoLinked = new Set();
@@ -96,22 +82,10 @@ module.exports = {
96
82
  autoLinked.add(token);
97
83
  }
98
84
  if (!withinAnyRange(urlRanges, lineIndex, index, length)) {
99
- let lineNumber = token.startLine;
100
- let column = index;
101
- if (tokenAdjustments.has(token)) {
102
- const { htmlTextLines, startLine } =
103
- tokenAdjustments.get(token);
104
- let lineDelta = 0;
105
- while (htmlTextLines[lineDelta].length <= column) {
106
- column -= htmlTextLines[lineDelta].length;
107
- lineDelta++;
108
- }
109
- lineNumber = startLine + lineDelta - 1;
110
- }
111
- column++;
85
+ const column = index + 1;
112
86
  addErrorDetailIf(
113
87
  onError,
114
- lineNumber,
88
+ token.startLine,
115
89
  name,
116
90
  nameMatch,
117
91
  null,
@@ -3,15 +3,18 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError, emphasisOrStrongStyleFor } = require("../helpers");
6
- const { filterByTypes, tokenIfType } = require("../helpers/micromark.cjs");
6
+ const { filterByPredicate, tokenIfType } = require("../helpers/micromark.cjs");
7
7
 
8
8
  const intrawordRe = /\w/;
9
9
 
10
10
  const impl =
11
11
  (params, onError, type, asterisk, underline, style = "consistent") => {
12
12
  const { lines, parsers } = params;
13
- const emphasisTokens =
14
- filterByTypes(parsers.micromark.tokens, [ type ]);
13
+ const emphasisTokens = filterByPredicate(
14
+ parsers.micromark.tokens,
15
+ (token) => token.type === type,
16
+ (token) => ((token.type === "htmlFlow") ? [] : token.children)
17
+ );
15
18
  for (const token of emphasisTokens) {
16
19
  const { children } = token;
17
20
  const childType = `${type}Sequence`;
package/lib/md051.js CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
 
5
5
  const { addError, addErrorDetailIf } = require("../helpers");
6
- const { filterByHtmlTokens, filterByTypes, getHtmlTagInfo } =
6
+ const { filterByPredicate, filterByTypes, getHtmlTagInfo } =
7
7
  require("../helpers/micromark.cjs");
8
8
 
9
9
  // Regular expression for identifying HTML anchor names
@@ -11,16 +11,28 @@ const idRe = /\sid\s*=\s*['"]?([^'"\s>]+)/iu;
11
11
  const nameRe = /\sname\s*=\s*['"]?([^'"\s>]+)/iu;
12
12
  const anchorRe = /\{(#[a-z\d]+(?:[-_][a-z\d]+)*)\}/gu;
13
13
 
14
+ // Sets for filtering heading tokens during conversion
15
+ const childrenExclude = new Set([ "image", "reference", "resource" ]);
16
+ const tokensInclude = new Set([ "codeTextData", "data" ]);
17
+
18
+ /**
19
+ * @typedef {import("../helpers/micromark.cjs").Token} Token
20
+ */
21
+
14
22
  /**
15
23
  * Converts a Markdown heading into an HTML fragment according to the rules
16
24
  * used by GitHub.
17
25
  *
18
- * @param {Object} headingText Heading text token.
26
+ * @param {Token} headingText Heading text token.
19
27
  * @returns {string} Fragment string for heading.
20
28
  */
21
29
  function convertHeadingToHTMLFragment(headingText) {
22
30
  const inlineText =
23
- filterByTypes(headingText.children, [ "codeTextData", "data" ])
31
+ filterByPredicate(
32
+ headingText.children,
33
+ (token) => tokensInclude.has(token.type),
34
+ (token) => (childrenExclude.has(token.type) ? [] : token.children)
35
+ )
24
36
  .map((token) => token.text)
25
37
  .join("");
26
38
  return "#" + encodeURIComponent(
@@ -38,6 +50,18 @@ function convertHeadingToHTMLFragment(headingText) {
38
50
  );
39
51
  }
40
52
 
53
+ /**
54
+ * Unescapes the text of a String-type micromark Token.
55
+ *
56
+ * @param {Token} token String-type micromark Token.
57
+ * @returns {string} Unescaped token text.
58
+ */
59
+ function unescapeStringTokenText(token) {
60
+ return filterByTypes(token.children, [ "characterEscapeValue", "data" ])
61
+ .map((child) => child.text)
62
+ .join("");
63
+ }
64
+
41
65
  module.exports = {
42
66
  "names": [ "MD051", "link-fragments" ],
43
67
  "description": "Link fragments should be valid",
@@ -53,22 +77,24 @@ module.exports = {
53
77
  );
54
78
  for (const headingText of headingTexts) {
55
79
  const fragment = convertHeadingToHTMLFragment(headingText);
56
- const count = fragments.get(fragment) || 0;
57
- if (count) {
58
- fragments.set(`${fragment}-${count}`, 0);
59
- }
60
- fragments.set(fragment, count + 1);
61
- let match = null;
62
- while ((match = anchorRe.exec(headingText.text)) !== null) {
63
- const [ , anchor ] = match;
64
- if (!fragments.has(anchor)) {
65
- fragments.set(anchor, 1);
80
+ if (fragment !== "#") {
81
+ const count = fragments.get(fragment) || 0;
82
+ if (count) {
83
+ fragments.set(`${fragment}-${count}`, 0);
84
+ }
85
+ fragments.set(fragment, count + 1);
86
+ let match = null;
87
+ while ((match = anchorRe.exec(headingText.text)) !== null) {
88
+ const [ , anchor ] = match;
89
+ if (!fragments.has(anchor)) {
90
+ fragments.set(anchor, 1);
91
+ }
66
92
  }
67
93
  }
68
94
  }
69
95
 
70
96
  // Process HTML anchors
71
- for (const token of filterByHtmlTokens(tokens)) {
97
+ for (const token of filterByTypes(tokens, [ "htmlText" ])) {
72
98
  const htmlTagInfo = getHtmlTagInfo(token);
73
99
  if (htmlTagInfo && !htmlTagInfo.close) {
74
100
  const anchorMatch = idRe.exec(token.text) ||
@@ -89,10 +115,13 @@ module.exports = {
89
115
  for (const link of links) {
90
116
  const definitions = filterByTypes(link.children, [ definitionType ]);
91
117
  for (const definition of definitions) {
118
+ const { endColumn, startColumn } = definition;
119
+ const text = unescapeStringTokenText(definition);
92
120
  if (
93
- (definition.text.length > 1) &&
94
- definition.text.startsWith("#") &&
95
- !fragments.has(definition.text)
121
+ (text.length > 1) &&
122
+ text.startsWith("#") &&
123
+ !fragments.has(text) &&
124
+ !fragments.has(`#${encodeURIComponent(text.slice(1))}`)
96
125
  ) {
97
126
  // eslint-disable-next-line no-undef-init
98
127
  let context = undefined;
@@ -104,13 +133,13 @@ module.exports = {
104
133
  context = link.text;
105
134
  range = [ link.startColumn, link.endColumn - link.startColumn ];
106
135
  fixInfo = {
107
- "editColumn": definition.startColumn,
108
- "deleteCount": definition.endColumn - definition.startColumn
136
+ "editColumn": startColumn,
137
+ "deleteCount": endColumn - startColumn
109
138
  };
110
139
  }
111
- const definitionTextLower = definition.text.toLowerCase();
140
+ const textLower = text.toLowerCase();
112
141
  const mixedCaseKey = [ ...fragments.keys() ]
113
- .find((key) => definitionTextLower === key.toLowerCase());
142
+ .find((key) => textLower === key.toLowerCase());
114
143
  if (mixedCaseKey) {
115
144
  // @ts-ignore
116
145
  (fixInfo || {}).insertText = mixedCaseKey;
@@ -118,7 +147,7 @@ module.exports = {
118
147
  onError,
119
148
  link.startLine,
120
149
  mixedCaseKey,
121
- definition.text,
150
+ text,
122
151
  undefined,
123
152
  context,
124
153
  range,
package/lib/md052.js CHANGED
@@ -11,10 +11,14 @@ module.exports = {
11
11
  "Reference links and images should use a label that is defined",
12
12
  "tags": [ "images", "links" ],
13
13
  "function": function MD052(params, onError) {
14
- const { lines } = params;
15
- const { references, definitions } = referenceLinkImageData();
14
+ const { config, lines } = params;
15
+ const shortcutSyntax = config.shortcut_syntax || false;
16
+ const { definitions, references, shortcuts } = referenceLinkImageData();
17
+ const entries = shortcutSyntax ?
18
+ [ ...references.entries(), ...shortcuts.entries() ] :
19
+ references.entries();
16
20
  // Look for links/images that use an undefined link reference
17
- for (const reference of references.entries()) {
21
+ for (const reference of entries) {
18
22
  const [ label, datas ] = reference;
19
23
  if (!definitions.has(label)) {
20
24
  for (const data of datas) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdownlint",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "A Node.js style checker and lint tool for Markdown/CommonMark files.",
5
5
  "type": "commonjs",
6
6
  "main": "./lib/markdownlint.js",
@@ -52,7 +52,7 @@
52
52
  "lint-test-repos": "ava --timeout=10m test/markdownlint-test-repos.js",
53
53
  "serial-config-docs": "npm run build-config && npm run build-docs",
54
54
  "serial-declaration-demo": "npm run build-declaration && npm-run-all --continue-on-error --parallel build-demo test-declaration",
55
- "test": "ava test/markdownlint-test.js test/markdownlint-test-config.js test/markdownlint-test-custom-rules.js test/markdownlint-test-helpers.js test/markdownlint-test-micromark.mjs test/markdownlint-test-result-object.js test/markdownlint-test-scenarios.js",
55
+ "test": "ava --timeout=30s test/markdownlint-test.js test/markdownlint-test-config.js test/markdownlint-test-custom-rules.js test/markdownlint-test-helpers.js test/markdownlint-test-micromark.mjs test/markdownlint-test-result-object.js test/markdownlint-test-scenarios.js",
56
56
  "test-cover": "c8 --100 npm test",
57
57
  "test-declaration": "cd example/typescript && tsc && node type-check.js",
58
58
  "test-extra": "ava --timeout=5m test/markdownlint-test-extra-parse.js test/markdownlint-test-extra-type.js",
@@ -68,16 +68,16 @@
68
68
  "markdownlint-micromark": "0.1.7"
69
69
  },
70
70
  "devDependencies": {
71
- "@babel/core": "7.22.10",
72
- "@babel/preset-env": "7.22.10",
71
+ "@babel/core": "7.22.15",
72
+ "@babel/preset-env": "7.22.15",
73
73
  "ava": "5.3.1",
74
74
  "babel-loader": "9.1.3",
75
75
  "c8": "8.0.1",
76
76
  "character-entities": "2.0.2",
77
- "eslint": "8.46.0",
77
+ "eslint": "8.48.0",
78
78
  "eslint-plugin-es": "4.1.0",
79
- "eslint-plugin-jsdoc": "46.4.6",
80
- "eslint-plugin-n": "16.0.1",
79
+ "eslint-plugin-jsdoc": "46.5.1",
80
+ "eslint-plugin-n": "16.0.2",
81
81
  "eslint-plugin-regexp": "1.15.0",
82
82
  "eslint-plugin-unicorn": "48.0.1",
83
83
  "gemoji": "8.1.0",
@@ -86,16 +86,16 @@
86
86
  "markdown-it-for-inline": "0.1.1",
87
87
  "markdown-it-sub": "1.0.0",
88
88
  "markdown-it-sup": "1.0.0",
89
- "markdownlint-rule-helpers": "0.20.0",
89
+ "markdownlint-rule-helpers": "0.21.0",
90
90
  "npm-run-all": "4.1.5",
91
91
  "strip-json-comments": "5.0.1",
92
92
  "terser-webpack-plugin": "5.3.9",
93
93
  "toml": "3.0.0",
94
94
  "tv4": "1.3.0",
95
- "typescript": "5.1.6",
95
+ "typescript": "5.2.2",
96
96
  "webpack": "5.88.2",
97
97
  "webpack-cli": "5.1.4",
98
- "yaml": "2.3.1"
98
+ "yaml": "2.3.2"
99
99
  },
100
100
  "keywords": [
101
101
  "markdown",
@@ -279,7 +279,10 @@
279
279
  "MD051": true,
280
280
 
281
281
  // MD052/reference-links-images - Reference links and images should use a label that is defined
282
- "MD052": true,
282
+ "MD052": {
283
+ // Include shortcut syntax
284
+ "shortcut_syntax": false
285
+ },
283
286
 
284
287
  // MD053/link-image-reference-definitions - Link and image reference definitions should be needed
285
288
  "MD053": {
@@ -252,7 +252,9 @@ MD050:
252
252
  MD051: true
253
253
 
254
254
  # MD052/reference-links-images - Reference links and images should use a label that is defined
255
- MD052: true
255
+ MD052:
256
+ # Include shortcut syntax
257
+ shortcut_syntax: false
256
258
 
257
259
  # MD053/link-image-reference-definitions - Link and image reference definitions should be needed
258
260
  MD053:
@@ -937,8 +937,19 @@
937
937
  },
938
938
  "MD052": {
939
939
  "description": "MD052/reference-links-images - Reference links and images should use a label that is defined",
940
- "type": "boolean",
941
- "default": true
940
+ "type": [
941
+ "boolean",
942
+ "object"
943
+ ],
944
+ "default": true,
945
+ "properties": {
946
+ "shortcut_syntax": {
947
+ "description": "Include shortcut syntax",
948
+ "type": "boolean",
949
+ "default": false
950
+ }
951
+ },
952
+ "additionalProperties": false
942
953
  },
943
954
  "reference-links-images": {
944
955
  "$ref": "#/properties/MD052"