Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export default defineConfig([
| [`no-bare-urls`](./docs/rules/no-bare-urls.md) | Disallow bare URLs | no |
| [`no-duplicate-definitions`](./docs/rules/no-duplicate-definitions.md) | Disallow duplicate definitions | yes |
| [`no-duplicate-headings`](./docs/rules/no-duplicate-headings.md) | Disallow duplicate headings in the same document | no |
| [`no-emphasis-as-heading`](./docs/rules/no-emphasis-as-heading.md) | Disallow using fully bolded paragraphs as headings | yes |
| [`no-empty-definitions`](./docs/rules/no-empty-definitions.md) | Disallow empty definitions | yes |
| [`no-empty-images`](./docs/rules/no-empty-images.md) | Disallow empty images | yes |
| [`no-empty-links`](./docs/rules/no-empty-links.md) | Disallow empty links | yes |
Expand Down
79 changes: 79 additions & 0 deletions docs/rules/no-emphasis-as-heading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# no-emphasis-as-heading

TODO

## Rule Details

This rule disallows using fully bolded paragraphs as headings. The use of fully bolded paragraphs as headings is a common anti-pattern that reduces document semantics and accessibility. Instead, proper heading elements (`#`, `##`, etc.) should be used.

### Why This Is Important

Using proper headings instead of bolded paragraphs:

- Improves document structure and semantics
- Enhances accessibility for screen readers
- Creates proper document outline
- Makes navigation of the document easier

### What This Rule Checks

This rule identifies paragraphs that:

- Consist entirely of a bold element (`**text**` or `__text__`)
- Are contained on a single line
- Are not within list items
- Have bold markup that spans the entire paragraph content

## Examples

### :x: Incorrect

Examples of **incorrect** code for this rule:

```md /**First Chapter**/ /__Second Chapter__/
<!-- eslint md/no-bold-paragraph: "error" -->

# Book

**First Chapter**

Content of the first chapter

__Second Chapter__

Content of the second chapter
```

### :white_check_mark: Correct

Examples of **correct** code for this rule:

```md
<!-- eslint md/no-bold-paragraph: "error" -->

# Book

## First Chapter

Content of the first chapter

## Second Chapter

Content of the second chapter

---

**Bold text** with normal text in the paragraph.

Text with **bold parts** is fine.

- **Bold text in a list item** is allowed.
```

## Options

No options are available for this rule.

## Prior Art

- [textlint-rule-no-bold-paragraph](https://github.com/aborazmeh/textlint-rule-no-bold-paragraph)
67 changes: 67 additions & 0 deletions src/rules/no-emphasis-as-heading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* @fileoverview Rule to disallow using fully bolded paragraphs as headings.
* @author lumir(lumirlumir)
*/

// @ts-nocheck -- TODO

//-----------------------------------------------------------------------------
// Type Definitions
//-----------------------------------------------------------------------------

/**
* @import { MarkdownRuleDefinition } from "../types.js";
* @typedef {"no-emphasis-as-heading"} NoEmphasisAsHeadingMessageIds
* @typedef {[]} NoEmphasisAsHeadingOptions
* @typedef {MarkdownRuleDefinition<{ RuleOptions: NoEmphasisAsHeadingOptions, MessageIds: NoEmphasisAsHeadingMessageIds }>} NoEmphasisAsHeadingRuleDefinition
*/

// --------------------------------------------------------------------------------
// Rule Definition
// --------------------------------------------------------------------------------

export default /** @satisfies {NoEmphasisAsHeadingRuleDefinition} */ ({
meta: {
type: "problem",

docs: {
recommended: true,
description: "Disallow using fully bolded paragraphs as headings",
url: "https://github.com/eslint/markdown/blob/main/docs/rules/no-emphasis-as-heading.md",
},

messages: {
noEmphasisAsHeading:
"Fully bolded paragraphs should not be used as headings. Please use a heading instead.",
},

// language: "markdown",

// dialects: ["commonmark", "gfm"],
},

create(context) {
return {
strong(node) {
const parentNode = context.sourceCode.getParent(node);
const ancestorNode = context.sourceCode.getParent(parentNode);

if (
parentNode.type === "paragraph" &&
ancestorNode.type !== "listItem" &&
parentNode.position.start.line ===
parentNode.position.end.line && // Should be a single line.
parentNode.position.start.offset ===
node.position.start.offset && // Should have the same start offset.
parentNode.position.end.offset === node.position.end.offset // Should have the same end offset.
) {
context.report({
node,

messageId: "noEmphasisAsHeading",
});
}
},
};
},
});
115 changes: 115 additions & 0 deletions tests/rules/no-emphasis-as-heading.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* @fileoverview Test for `no-emphasis-as-heading.ts`.
* @author lumir(lumirlumir)
*/

//------------------------------------------------------------------------------
// Imports
//------------------------------------------------------------------------------

import rule from "../../src/rules/no-emphasis-as-heading.js";
import markdown from "../../src/index.js";
import { RuleTester } from "eslint";

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester({
plugins: {
markdown,
},
language: "markdown/commonmark",
});

ruleTester.run("no-emphasis-as-heading", rule, {
valid: [
// Basic
{
name: "Empty",
code: "",
},
{
name: "Empty string",
code: " ",
},
{
name: "Paragraph without bolded text",
code: "#Book\n\nFirst Chapter\n\nContent\n\nSecond Chapter\n\nContent",
},
{
name: "Paragraph with bolded text and other text",
code: "**Hello** World.\n\nHello **World**.",
},
{
name: "Paragraph with bolded text and other multiline text",
code: "**Hello**\nWorld.\n\nHello\n**World**.",
},
{
name: "Bold with whitespace inside",
code: "** Not fully bolded paragraph **",
},
{
name: "Multiple bold elements comprising entire paragraph",
code: "**First part** **second part**",
},

// ListItem
{
name: "Bold text in list item",
code: "- **List item**\n- __List item__",
},
],

invalid: [
// Basic
{
name: "Paragraph with fully bolded text",
code: "#Book\n\n**First Chapter**\n\nContent\n\n__Second Chapter__\n\nContent",
errors: [
{
messageId: "noEmphasisAsHeading",
line: 3,
column: 1,
endLine: 3,
endColumn: 18,
},
{
messageId: "noEmphasisAsHeading",
line: 7,
column: 1,
endLine: 7,
endColumn: 19,
},
],
},
{
name: "Paragraph with fully bolded single character",
code: "**X**",
errors: [
{
messageId: "noEmphasisAsHeading",
line: 1,
column: 1,
endLine: 1,
endColumn: 6,
},
],
},

// Blockquote
{
name: "Bold text in blockquote",
code: "> **Blockquote**",
errors: [
{
messageId: "noEmphasisAsHeading",
line: 1,
column: 3,
endLine: 1,
endColumn: 17,
},
],
},
],
});