Skip to content

Commit 84c87c4

Browse files
authored
feat: add no-missing-atx-heading-space rule (#371)
1 parent b2fc801 commit 84c87c4

4 files changed

Lines changed: 638 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export default defineConfig([
8282
| [`no-empty-links`](./docs/rules/no-empty-links.md) | Disallow empty links | yes |
8383
| [`no-html`](./docs/rules/no-html.md) | Disallow HTML tags | no |
8484
| [`no-invalid-label-refs`](./docs/rules/no-invalid-label-refs.md) | Disallow invalid label references | yes |
85+
| [`no-missing-atx-heading-space`](./docs/rules/no-missing-atx-heading-space.md) | Disallow headings without a space after the hash characters | yes |
8586
| [`no-missing-label-refs`](./docs/rules/no-missing-label-refs.md) | Disallow missing label references | yes |
8687
| [`require-alt-text`](./docs/rules/require-alt-text.md) | Require alternative text for images | yes |
8788
<!-- Rule Table End -->
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# no-missing-atx-heading-space
2+
3+
This rule warns when spaces are missing after the hash characters in an ATX style heading.
4+
5+
## Rule Details
6+
7+
In Markdown, headings can be created using ATX style (using hash (`#`) characters at the beginning of the line) or Setext style (using underlining with equals (`=`) or hyphens (`-`)).
8+
9+
For ATX style headings, a space should be used after the hash characters to improve readability and ensure proper rendering across various Markdown parsers.
10+
11+
This rule is automatically fixable by the `--fix` command line option.
12+
13+
Examples of **incorrect** code for this rule:
14+
15+
```md
16+
<!-- eslint markdown/no-missing-atx-heading-space: "error" -->
17+
18+
#Heading 1
19+
##Heading 2
20+
###Heading 3
21+
```
22+
23+
Examples of **correct** code for this rule:
24+
25+
```md
26+
<!-- eslint markdown/no-missing-atx-heading-space: "error" -->
27+
28+
# Heading 1
29+
## Heading 2
30+
### Heading 3
31+
32+
<h1>#Some Heading</h1>
33+
34+
[#123](link.com)
35+
36+
![#alttext][link.png]
37+
38+
This is a paragraph with a #hashtag, not a heading.
39+
```
40+
41+
## When Not To Use It
42+
43+
You might want to turn this rule off if you're working with a Markdown variant that doesn't require spaces after hash characters in headings.
44+
45+
## Prior Art
46+
47+
- [MD018 - No space after hash on atx style heading](https://github.com/DavidAnson/markdownlint/blob/main/doc/md018.md)
48+
49+
## Further Reading
50+
51+
- [Markdown Syntax: Headings](https://daringfireball.net/projects/markdown/syntax#header)
52+
- [CommonMark Spec: ATX Headings](https://spec.commonmark.org/0.30/#atx-headings)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* @fileoverview Rule to ensure there is a space after hash on ATX style headings in Markdown.
3+
* @author Sweta Tanwar (@SwetaTanwar)
4+
*/
5+
6+
//-----------------------------------------------------------------------------
7+
// Type Definitions
8+
//-----------------------------------------------------------------------------
9+
10+
/**
11+
* @typedef {import("../types.ts").MarkdownRuleDefinition<{ RuleOptions: []; }>}
12+
* NoMissingAtxHeadingSpaceRuleDefinition
13+
*/
14+
15+
//-----------------------------------------------------------------------------
16+
// Helpers
17+
//-----------------------------------------------------------------------------
18+
19+
const headingPattern = /^(#{1,6})(?:[^#\s]|$)/u;
20+
const newLinePattern = /\r?\n/u;
21+
22+
//-----------------------------------------------------------------------------
23+
// Rule Definition
24+
//-----------------------------------------------------------------------------
25+
26+
/** @type {NoMissingAtxHeadingSpaceRuleDefinition} */
27+
export default {
28+
meta: {
29+
type: "problem",
30+
31+
docs: {
32+
recommended: true,
33+
description:
34+
"Disallow headings without a space after the hash characters",
35+
url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-missing-atx-heading-space.md",
36+
},
37+
38+
fixable: "whitespace",
39+
40+
messages: {
41+
missingSpace: "Missing space after hash(es) on ATX style heading.",
42+
},
43+
},
44+
45+
create(context) {
46+
return {
47+
paragraph(node) {
48+
if (node.children && node.children.length > 0) {
49+
const firstTextChild = node.children.find(
50+
child => child.type === "text",
51+
);
52+
if (!firstTextChild) {
53+
return;
54+
}
55+
56+
const text = context.sourceCode.getText(firstTextChild);
57+
const lines = text.split(newLinePattern);
58+
59+
lines.forEach((line, idx) => {
60+
const lineNum =
61+
firstTextChild.position.start.line + idx;
62+
63+
const match = headingPattern.exec(line);
64+
if (!match) {
65+
return;
66+
}
67+
68+
const hashes = match[1];
69+
70+
const startColumn =
71+
firstTextChild.position.start.column;
72+
73+
context.report({
74+
loc: {
75+
start: { line: lineNum, column: startColumn },
76+
end: {
77+
line: lineNum,
78+
column: startColumn + hashes.length + 1,
79+
},
80+
},
81+
messageId: "missingSpace",
82+
fix(fixer) {
83+
const offset =
84+
firstTextChild.position.start.offset +
85+
lines.slice(0, idx).join("\n").length +
86+
(idx > 0 ? 1 : 0);
87+
88+
return fixer.insertTextAfterRange(
89+
[
90+
offset + hashes.length - 1,
91+
offset + hashes.length,
92+
],
93+
" ",
94+
);
95+
},
96+
});
97+
});
98+
}
99+
},
100+
};
101+
},
102+
};

0 commit comments

Comments
 (0)