Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/max-len.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-markdown-preferences": minor
---

feat: add `markdown-preferences/max-len` rule
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,16 @@ The rules with the following 💄 are included in the `standard` config.

- Rules related to visual or stylistic decorations in Markdown.

<!-- eslint-disable markdown-links/no-dead-urls -- Auto generated -->

<!-- prettier-ignore-start -->

| Rule ID | Description | Fixable | Config |
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------------------------- | :-----: | :----: |
| [markdown-preferences/atx-heading-closing-sequence-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/atx-heading-closing-sequence-length.html) | enforce consistent length for the closing sequence (trailing #s) in ATX headings. | 🔧 | 💄 |
| [markdown-preferences/atx-heading-closing-sequence](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/atx-heading-closing-sequence.html) | enforce consistent use of closing sequence in ATX headings. | 🔧 | 💄 |
| [markdown-preferences/code-fence-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-length.html) | enforce consistent code fence length in fenced code blocks. | 🔧 | 💄 |
| [markdown-preferences/max-len](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/max-len.html) | enforce maximum length for various Markdown entities | | |
| [markdown-preferences/no-laziness-blockquotes](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-laziness-blockquotes.html) | disallow laziness in blockquotes | | ⭐💄 |
| [markdown-preferences/ordered-list-marker-sequence](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/ordered-list-marker-sequence.html) | enforce consistent ordered list marker numbering (sequential or flat) | 🔧 | 💄 |
| [markdown-preferences/setext-heading-underline-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/setext-heading-underline-length.html) | enforce setext heading underline length | 🔧 | 💄 |
Expand All @@ -234,6 +237,8 @@ The rules with the following 💄 are included in the `standard` config.

<!-- prettier-ignore-end -->

<!-- eslint-enable markdown-links/no-dead-urls -- Auto generated -->

<!--RULES_TABLE_END-->

<!--RULES_SECTION_END-->
Expand Down
9 changes: 5 additions & 4 deletions docs/appendix/comparison-with-markdownlint-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ Please note that each OSS is constantly evolving, so this list is not exhaustive

## Rules Related to Documents

| Description | [markdownlint] Rules | [@eslint/markdown] Rules | `eslint-plugin-markdown-preferences` Rules |
| ----------------------------- | ----------------------------------------------------------------------------- | ------------------------ | ------------------------------------------ |
| Enforce a maximum line length | [MD013] _line-length_<br>Line length | -- | -- |
| Proper names | [MD044] _proper-names_<br>Proper names should have the correct capitalization | -- | -- |
| Description | [markdownlint] Rules | [@eslint/markdown] Rules | `eslint-plugin-markdown-preferences` Rules |
| ----------------------------- | ----------------------------------------------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------- |
| Enforce a maximum line length | [MD013] _line-length_<br>Line length | -- | [markdown-preferences/max-len]<br>enforce maximum length for various Markdown entities |
| Proper names | [MD044] _proper-names_<br>Proper names should have the correct capitalization | -- | -- |

## Rules Related to Syntax

Expand Down Expand Up @@ -256,6 +256,7 @@ Please note that each OSS is constantly evolving, so this list is not exhaustive
[markdown-preferences/link-paren-spacing]: ./../rules/link-paren-spacing.md
[markdown-preferences/link-title-style]: ./../rules/link-title-style.md
[markdown-preferences/list-marker-alignment]: ../rules/list-marker-alignment.md
[markdown-preferences/max-len]: ./../rules/max-len.md
[markdown-preferences/no-heading-trailing-punctuation]: ./../rules/no-heading-trailing-punctuation.md
[markdown-preferences/no-implicit-block-closing]: ../rules/no-implicit-block-closing.md
[markdown-preferences/no-laziness-blockquotes]: ../rules/no-laziness-blockquotes.md
Expand Down
1 change: 1 addition & 0 deletions docs/rules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ The rules with the following 💄 are included in the `plugin.configs.standard`
| [markdown-preferences/atx-heading-closing-sequence-length](./atx-heading-closing-sequence-length.md) | enforce consistent length for the closing sequence (trailing #s) in ATX headings. | 🔧 | 💄 |
| [markdown-preferences/atx-heading-closing-sequence](./atx-heading-closing-sequence.md) | enforce consistent use of closing sequence in ATX headings. | 🔧 | 💄 |
| [markdown-preferences/code-fence-length](./code-fence-length.md) | enforce consistent code fence length in fenced code blocks. | 🔧 | 💄 |
| [markdown-preferences/max-len](./max-len.md) | enforce maximum length for various Markdown entities | | |
| [markdown-preferences/no-laziness-blockquotes](./no-laziness-blockquotes.md) | disallow laziness in blockquotes | | ⭐💄 |
| [markdown-preferences/ordered-list-marker-sequence](./ordered-list-marker-sequence.md) | enforce consistent ordered list marker numbering (sequential or flat) | 🔧 | 💄 |
| [markdown-preferences/setext-heading-underline-length](./setext-heading-underline-length.md) | enforce setext heading underline length | 🔧 | 💄 |
Expand Down
192 changes: 192 additions & 0 deletions docs/rules/max-len.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
pageClass: "rule-details"
sidebarDepth: 0
title: "markdown-preferences/max-len"
description: "enforce maximum length for various Markdown entities"
---

# markdown-preferences/max-len

> enforce maximum length for various Markdown entities

- ❗ <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>

## 📖 Rule Details

This rule enforces a configurable maximum length for different Markdown entities such as headings, paragraphs, lists, blockquotes, tables, code blocks, frontmatter, footnote definitions, HTML blocks, and math blocks.

Overly long Markdown elements reduce readability, make diffs harder to review, and complicate maintenance. By enforcing reasonable maximum lengths for various entities, documentation remains cleaner, easier to navigate, and more consistent across the project.

<!-- prettier-ignore-start -->

<!-- eslint-skip -->

```md
<!-- eslint markdown-preferences/max-len: ['error', { heading: 80, paragraph: 120 }] -->

<!-- ✓ GOOD -->

## This heading is within the limit

This is a paragraph that fits comfortably within the specified maximum length limit.

- A list item that is short enough

<!-- ✗ BAD -->

## This is a very long heading that exceeds the default eighty character maximum length

This is an extremely long paragraph that goes on and on and on well beyond the one hundred and twenty character maximum length limit and should be broken up.

- This is a very long list item that definitely exceeds the default one hundred and twenty character maximum length limit and should be reported
```

<!-- prettier-ignore-end -->

## 🔧 Options

This rule accepts an object with the following properties:

```json
{
"markdown-preferences/max-len": [
"error",
{
"heading": 80,
"paragraph": 120,
"list": 120,
"blockquote": 120,
"table": 120,
"footnoteDefinition": 120,
"html": 120,
"code": "ignore",
"frontmatter": "ignore",
"math": "ignore",
"ignoreUrls": true
}
]
}
```

### Entity Options

These options control the maximum line length for specific Markdown entity types:

- `heading` (default: `80`): Maximum line length for headings. Set to `"ignore"` to skip checking.
- `paragraph` (default: `120`): Maximum line length for paragraphs. Set to `"ignore"` to skip checking.
- `table` (default: `120`): Maximum line length for tables. Set to `"ignore"` to skip checking.
- `html` (default: `120`): Maximum line length for HTML blocks. Set to `"ignore"` to skip checking.
- `code` (default: `"ignore"`): Maximum line length for code blocks. Set to `"ignore"` to skip checking (recommended). See [notes on code blocks](#notes-on-code-blocks) below.
- `frontmatter` (default: `"ignore"`): Maximum line length for frontmatter. Set to `"ignore"` to skip checking (recommended).
- `math` (default: `"ignore"`): Maximum line length for math blocks. Set to `"ignore"` to skip checking.

### Container Options

These options control line length checking for entities within container elements. Containers can either have a simple numeric limit (applied to all nested content) or an object specifying different limits for different entity types within that container:

- `list`: Override limits for content within lists. When not specified, nested content inherits from entity-level defaults.
- `blockquote`: Override limits for content within blockquotes. When not specified, nested content inherits from entity-level defaults.
- `footnoteDefinition`: Override limits for content within footnote definitions. When not specified, nested content inherits from entity-level defaults.

#### Nested Configuration for Containers

For `list`, `blockquote`, and `footnoteDefinition`, you can specify different limits for any entity types within these containers (including `heading`, `paragraph`, `table`, `html`, `math`, `code`, and `frontmatter`):

```json
{
"markdown-preferences/max-len": [
"error",
{
"heading": 80,
"paragraph": 120,
"table": 100,
"blockquote": {
"heading": 70,
"paragraph": 100,
"table": 90
}
}
]
}
```

In this example:

- Standalone headings are limited to 80 characters, but headings inside blockquotes are limited to 70 characters
- Standalone tables are limited to 100 characters, but tables inside blockquotes are limited to 90 characters
- All entity types (`heading`, `paragraph`, `table`, `html`, `math`, `code`, `frontmatter`) can be configured within containers

### Language-specific Code Block Configuration

For `code` and `frontmatter`, you can specify different limits per language:

```json
{
"markdown-preferences/max-len": [
"error",
{
"code": {
"javascript": 100,
"python": 80,
"shell": "ignore"
},
"frontmatter": {
"yaml": 120,
"toml": "ignore"
}
}
]
}
```

### URL Handling

- `ignoreUrls` (default: `true`): When enabled, lines containing URLs are ignored.

This option works similarly to [`@stylistic/eslint-plugin`'s `max-len` rule](https://eslint.style/rules/max-len#ignoreurls).

### Notes on Code Blocks

**Note:** Code blocks are ignored by default (`code: "ignore"`) because:

1. Code inside code blocks should be linted using language-specific linters via ESLint's language plugins
2. The [`@eslint/markdown`](https://github.com/eslint/markdown) plugin supports linting code blocks as their respective languages
3. This rule cannot understand the syntax inside code blocks (e.g., comments, strings)

If you need to enforce line length for code blocks in Markdown, you can:

- Use the language-specific ESLint configuration for code blocks (recommended):

```js
{
files: ["**/*.md/*.js"],
rules: {
"@stylistic/max-len": ["error", { "ignoreComments": true }]
}
}
```

**However**, this feature is not currently available with this plugin. It may be possible in the future. The RFC is at: <https://github.com/eslint/rfcs/pull/105>

- Set the `code` option to enforce a simple line length limit (not syntax-aware)

For more information, see the [advanced configuration guide](https://github.com/eslint/markdown/blob/main/docs/processors/markdown.md#advanced-configuration).

## 📚 Further Reading

- [Markdownlint MD013](https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md013.md) - Similar rule in markdownlint
- [@stylistic/eslint-plugin max-len](https://eslint.style/rules/max-len) - ESLint stylistic rule for code

## 👫 Related Rules

- [no-multiple-empty-lines](./no-multiple-empty-lines.md) - Disallow multiple empty lines

## 🔍 Implementation

<!-- eslint-disable markdown-links/no-dead-urls -- Auto generated -->

- [Rule source](https://github.com/ota-meshi/eslint-plugin-markdown-preferences/blob/main/src/rules/max-len.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-markdown-preferences/blob/main/tests/src/rules/max-len.ts)
- [Test fixture sources](https://github.com/ota-meshi/eslint-plugin-markdown-preferences/tree/main/tests/fixtures/rules/max-len)

<!-- eslint-enable markdown-links/no-dead-urls -- Auto generated -->
5 changes: 5 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export default defineConfig([
...Object.fromEntries(
rules.map((rule) => [rule.meta.docs.ruleId, "error"]),
),
"markdown-preferences/max-len": "off", // 今は無効化します
"markdown-preferences/prefer-linked-words": [
"error",
{
Expand Down Expand Up @@ -197,8 +198,12 @@ export default defineConfig([
"/^https:\\/\\/www\\.npmtrends\\.com\\//u",
],
allowedAnchors: {
// https://eslint-online-playground.netlify.app/
"/^https:\\/\\/eslint-online-playground\\.netlify\\.app\\//u":
"/.*/u",
// https://github.com/eslint/markdown/blob/main/docs/processors/markdown.md
"/^https:\\/\\/github\\.com\\/eslint\\/markdown\\/blob\\/main\\/docs\\/processors\\/markdown\\.md/u":
"/.*/u",
},
},
],
Expand Down
59 changes: 59 additions & 0 deletions src/rule-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ export interface RuleOptions {
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/list-marker-alignment.html
*/
'markdown-preferences/list-marker-alignment'?: Linter.RuleEntry<MarkdownPreferencesListMarkerAlignment>
/**
* enforce maximum length for various Markdown entities
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/max-len.html
*/
'markdown-preferences/max-len'?: Linter.RuleEntry<MarkdownPreferencesMaxLen>
/**
* disallow trailing punctuation in headings.
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-heading-trailing-punctuation.html
Expand Down Expand Up @@ -433,6 +438,60 @@ type MarkdownPreferencesLinkTitleStyle = []|[{
type MarkdownPreferencesListMarkerAlignment = []|[{
align?: ("left" | "right")
}]
// ----- markdown-preferences/max-len -----
type MarkdownPreferencesMaxLen = []|[{
heading?: (number | "ignore")
paragraph?: (number | "ignore")
table?: (number | "ignore")
html?: (number | "ignore")
math?: (number | "ignore")
code?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
frontmatter?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
list?: ((number | "ignore") | {
heading?: (number | "ignore")
paragraph?: (number | "ignore")
table?: (number | "ignore")
html?: (number | "ignore")
math?: (number | "ignore")
code?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
frontmatter?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
})
blockquote?: ((number | "ignore") | {
heading?: (number | "ignore")
paragraph?: (number | "ignore")
table?: (number | "ignore")
html?: (number | "ignore")
math?: (number | "ignore")
code?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
frontmatter?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
})
footnoteDefinition?: ((number | "ignore") | {
heading?: (number | "ignore")
paragraph?: (number | "ignore")
table?: (number | "ignore")
html?: (number | "ignore")
math?: (number | "ignore")
code?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
frontmatter?: ((number | "ignore") | {
[k: string]: (number | "ignore")
})
})
ignoreUrls?: boolean
}]
// ----- markdown-preferences/no-heading-trailing-punctuation -----
type MarkdownPreferencesNoHeadingTrailingPunctuation = []|[{
punctuation?: (string | {
Expand Down
Loading
Loading