Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Markdownlint: dashes-in-headings custom rule #28330

Closed
3 changes: 2 additions & 1 deletion .markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"./markdownlint/TOP006_fullFencedCodeLanguage/TOP006_fullFencedCodeLanguage.js",
"./markdownlint/TOP007_useMarkdownLinks/TOP007_useMarkdownLinks.js",
"./markdownlint/TOP008_useBackticksForFencedCodeBlocks/TOP008_useBackticksForFencedCodeBlocks.js",
"./markdownlint/TOP010_useLazyNumbering/TOP010_useLazyNumbering.js"
"./markdownlint/TOP010_useLazyNumbering/TOP010_useLazyNumbering.js",
"./markdownlint/TOP011_dashesInHeadings/TOP011_dashesInHeadings.js"
]
}
15 changes: 15 additions & 0 deletions LAYOUT_STYLE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,21 @@ Headings should never contain any code snippets.
### The id property
```

### Separators

Headings must use a colon to separate parts of the heading, not dashes. Hyphenated words are fine.

```markdown
<!-- Incorrect -->
### Headings - Don't separate with a dash

<!-- Incorrect -->
### Headings -- Don't use multiple dashes either

<!-- Correct -->
### Headings: Separate using a colon
```

### ATX-style headings

Use Heading 3 `###` for main section titles ("Lesson overview", "Assignment", custom sections, etc):
Expand Down
35 changes: 35 additions & 0 deletions markdownlint/TOP011_dashesInHeadings/TOP011_dashesInHeadings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = {
names: ["TOP011", "dashes-in-headings"],
description:
"Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed).",
information: new URL(
"https://github.com/TheOdinProject/curriculum/blob/main/markdownlint/docs/TOP011.md"
),
tags: ["headings"],
parser: "markdownit",
function: function TOP011(params, onError) {
const headings = params.parsers.markdownit.tokens.filter(
(token) => token.type === "heading_open"
);

// https://regexr.com/82ci6 to test this regex
const separatorDashRegex = /\s-+\s/;

headings.forEach((heading) => {
const separatorDash = heading.line.match(separatorDashRegex)?.[0];
if (separatorDash) {
onError({
lineNumber: heading.lineNumber,
detail: `\n Expected: ${heading.line.replace(separatorDash, ": ")}\n Actual: ${
heading.line
}\n`,
fixInfo: {
editColumn: heading.line.indexOf(separatorDash) + 1,
deleteCount: separatorDash.length,
insertText: ": ",
},
});
}
});
},
};
67 changes: 67 additions & 0 deletions markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
### Introduction

This file should flag with TOP011 errors, and no other linting errors.

### Lesson overview

This section contains a general overview of topics that you will learn in this lesson.

- A LESSON OVERVIEW ITEM.

### L3 Heading - Flags separator dash

Check failure on line 11 in markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md

View workflow job for this annotation

GitHub Actions / Lint lesson files

Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed).

markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md:11 TOP011/dashes-in-headings Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed). [ Expected: ### L3 Heading: Flags separator dash Actual: ### L3 Heading - Flags separator dash ] https://github.com/TheOdinProject/curriculum/blob/main/markdownlint/docs/TOP011.md

CUSTOM SECTION CONTENT.

### L3 Heading: Does not flag if no separator dash

CUSTOM SECTION CONTENT.

### L3 Heading: Does not flag for hyphenated-words

CUSTOM SECTION CONTENT.

#### L4 Heading - Flags separator dash

Check failure on line 23 in markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md

View workflow job for this annotation

GitHub Actions / Lint lesson files

Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed).

markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md:23 TOP011/dashes-in-headings Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed). [ Expected: #### L4 Heading: Flags separator dash Actual: #### L4 Heading - Flags separator dash ] https://github.com/TheOdinProject/curriculum/blob/main/markdownlint/docs/TOP011.md

CUSTOM SECTION CONTENT.

#### L4 Heading: Does not flag if no separator dash

CUSTOM SECTION CONTENT.

#### L4 Heading: Does not flag for hyphenated-words

CUSTOM SECTION CONTENT.

### Heading - Hyphenated-words are-left-alone when-fixing-separator-dashes

Check failure on line 35 in markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md

View workflow job for this annotation

GitHub Actions / Lint lesson files

Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed).

markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md:35 TOP011/dashes-in-headings Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed). [ Expected: ### Heading: Hyphenated-words are-left-alone when-fixing-separator-dashes Actual: ### Heading - Hyphenated-words are-left-alone when-fixing-separator-dashes ] https://github.com/TheOdinProject/curriculum/blob/main/markdownlint/docs/TOP011.md

CUSTOM SECTION CONTENT.

### Heading -- Also flags separators with double dashes

Check failure on line 39 in markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md

View workflow job for this annotation

GitHub Actions / Lint lesson files

Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed).

markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md:39 TOP011/dashes-in-headings Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed). [ Expected: ### Heading: Also flags separators with double dashes Actual: ### Heading -- Also flags separators with double dashes ] https://github.com/TheOdinProject/curriculum/blob/main/markdownlint/docs/TOP011.md

CUSTOM SECTION CONTENT.

### Heading: -f flag dashes -\-do-not count as separator dashes

CUSTOM SECTION CONTENT.

### Hyphenated-heading - Flags even if separator dash proceeds hyphen

Check failure on line 47 in markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md

View workflow job for this annotation

GitHub Actions / Lint lesson files

Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed).

markdownlint/TOP011_dashesInHeadings/tests/TOP011_test.md:47 TOP011/dashes-in-headings Headings must use a colon (:) instead of dashes (-) for the purpose of separating parts of the heading (hyphenated words are allowed). [ Expected: ### Hyphenated-heading: Flags even if separator dash proceeds hyphen Actual: ### Hyphenated-heading - Flags even if separator dash proceeds hyphen ] https://github.com/TheOdinProject/curriculum/blob/main/markdownlint/docs/TOP011.md

CUSTOM SECTION CONTENT.

### Assignment

<div class="lesson-content__panel" markdown="1">

</div>

### Knowledge check

The following questions are an opportunity to reflect on key topics in this lesson. If you can't answer a question, click on it to review the material, but keep in mind you are not expected to memorize or master this knowledge.

- [A KNOWLEDGE CHECK QUESTION](A-KNOWLEDGE-CHECK-URL)

### Additional resources

This section contains helpful links to related content. It isn't required, so consider it supplemental.

- It looks like this lesson doesn't have any additional resources yet. Help us expand this section by contributing to our curriculum.
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP001.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP001` - Descriptive link text
# `TOP001`: Descriptive link text

Tags: `accessibility`, `links`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP002.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP002` - No inline code in headings
# `TOP002`: No inline code in headings

Tags: `headings`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP003.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP003` - Default section content
# `TOP003`: Default section content

Tags: `content`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP004.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP004` - Lesson headings
# `TOP004`: Lesson headings

Tags: `headings`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP005.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP005` - Blanks around multiline HTML tags
# `TOP005`: Blanks around multiline HTML tags

Tags: `html`, `blank_lines`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP006.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP006` - Use full language name for fenced code blocks
# `TOP006`: Use full language name for fenced code blocks

Tags: `code`, `language`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP007.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP007` - Use markdown links
# `TOP007`: Use markdown links

Tags: `links`, `html`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP008.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP008` - Use backticks for fenced code blocks
# `TOP008`: Use backticks for fenced code blocks

Tags: `code`

Expand Down
2 changes: 1 addition & 1 deletion markdownlint/docs/TOP010.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `TOP010` - Use lazy numbering within ordered lists
# `TOP010`: Use lazy numbering within ordered lists

Tags: `ol`

Expand Down
46 changes: 46 additions & 0 deletions markdownlint/docs/TOP011.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# `TOP011`: Dashes in headings

Tags: `headings`

Aliases: `dashes-in-headings`

This rule is triggered when a heading contains dashes used for separating the heading's main title from a subtitle. For example:

```markdown
### Headings - Should dashes be used as separators?

### Headings -- Should double dashes be used as separators?
```

Instead of the dash(es), use a colon instead.

```markdown
### Headings: Colons must be used as separators
```

This rule only triggers for dashes used in this way. Dashes used as hyphens or CLI flags will not trigger this rule.

```markdown
### Hyphenated-words are perfectly acceptable

### As are headings with CLI -f --flags
```

Headings that violate this rule can be fixed via the appropriate `fix:*` script.

```markdown
### Headings - Hyphenated-words and --flags will be left alone

<!-- Will get fixed to -->
### Headings: Hyphenated-words and --flags will be left alone
```

## Rationale

Markdownlint and Visual Studio Code use the [GitHub heading algorithm](https://github.com/gjtorikian/html-pipeline/blob/f13a1534cb650ba17af400d1acd3a22c28004c09/lib/html/pipeline/toc_filter.rb) to generate the appropriate fragments from headings, which preserves *all* dashes in the heading. The TOP web app uses the [Ruby on Rails `#parameterize` method](https://apidock.com/rails/String/parameterize) to generate heading IDs and fragments, which removes separator dashes.

For example, the heading `### Heading - Hyphenated-subheading` will be converted to `heading---hyphenated-subheading` by Visual Studio Code and Markdownlint, but our web app will convert it to `heading-hypenated-subheading`.

This creates a discrepancy between the fragment any of our links would require for our website, and what Markdownlint considers "valid". Using the website-valid fragment would trigger Markdownlint's [MD051 rule](https://github.com/DavidAnson/markdownlint/blob/main/doc/md051.md). Using the Markdownlint-valid fragment would not match the right heading on our website.

Enforcing colons instead of dashes as separators, we can also avoid messy heading ID syntax such as `### Heading - Hyphenated-subheading {#heading-hypenated-subheading}`. Heading ID syntax would also introduce an additional point of failure, since we might change the heading and forget to change the custom ID.
Loading