Skip to content

Commit e437f23

Browse files
authored
expiring-todo-comments: Add YAML support (#3625)
1 parent 823bb1f commit e437f23

8 files changed

Lines changed: 111 additions & 19 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ Available identifiers:
100100
- `json/json`, `json/jsonc`, `json/json5`[`@eslint/json`](https://github.com/eslint/json)
101101
- `markdown/commonmark`, `markdown/gfm`[`@eslint/markdown`](https://github.com/eslint/markdown)
102102
- `html/html`[`@html-eslint/eslint-plugin`](https://github.com/yeonjuan/html-eslint)
103+
- `yml/yaml`[`eslint-plugin-yml`](https://github.com/ota-meshi/eslint-plugin-yml)
103104

104-
Most rules visit JavaScript AST nodes, so `js/js` is all they can support. But when a rule's logic is language-agnostic (filename, raw text, comments, or disable directives), support as many languages as is feasible. Root node types differ per language (`Program` for JS/TS and HTML, `StyleSheet` for CSS, `Document` for JSON, `root` for Markdown), so use `onRoot(context, listener)` to run on every root and `getComments(context)` for cross-language comments (both from `rules/utils/`). For reference, see `prefer-https` (`['*']`, raw-text scan) and `no-empty-file` (per-language root handlers).
105+
Most rules visit JavaScript AST nodes, so `js/js` is all they can support. But when a rule's logic is language-agnostic (filename, raw text, comments, or disable directives), support as many languages as is feasible. Root node types differ per language (`Program` for JS/TS, HTML, and YAML, `StyleSheet` for CSS, `Document` for JSON, `root` for Markdown), so use `onRoot(context, listener)` to run on every root and `getComments(context)` for cross-language comments (both from `rules/utils/`). For reference, see `prefer-https` (`['*']`, raw-text scan) and `no-empty-file` (per-language root handlers).
105106

106107
## Reusable utilities
107108

docs/rules/expiring-todo-comments.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ With this rule, a TODO can have a condition right from the beginning to define i
1919

2020
This rule will ignore all TODOs without conditions. For more information, read the below [`eslint/no-warning-comments`](#disallow-warning-comments-no-warning-comments) section.
2121

22-
This rule also supports CSS comments when linting CSS files with [`@eslint/css`](https://github.com/eslint/css), HTML comments when linting HTML files with [`@html-eslint/eslint-plugin`](https://github.com/yeonjuan/html-eslint), line comments in JSONC/JSON5 files when linting with [`@eslint/json`](https://github.com/eslint/json), and HTML comments in Markdown files when linting with [`@eslint/markdown`](https://github.com/eslint/markdown).
22+
This rule also supports CSS comments when linting CSS files with [`@eslint/css`](https://github.com/eslint/css), HTML comments when linting HTML files with [`@html-eslint/eslint-plugin`](https://github.com/yeonjuan/html-eslint), line comments in JSONC/JSON5 files when linting with [`@eslint/json`](https://github.com/eslint/json), HTML comments in Markdown files when linting with [`@eslint/markdown`](https://github.com/eslint/markdown), and comments in YAML files when linting with [`eslint-plugin-yml`](https://github.com/ota-meshi/eslint-plugin-yml).
2323

2424
Quick overview of conditions:
2525

@@ -208,6 +208,15 @@ When used with [`@eslint/markdown`](https://github.com/eslint/markdown), this ru
208208
# Heading
209209
```
210210

211+
### YAML
212+
213+
When used with [`eslint-plugin-yml`](https://github.com/ota-meshi/eslint-plugin-yml), this rule can check comments in YAML files too.
214+
215+
```yaml
216+
# TODO [2019-11-15]: Update this configuration.
217+
key: value
218+
```
219+
211220
## Disallow Warning Comments (no-warning-comments)
212221
213222
This rule implements [`eslint/no-warning-comments`](https://eslint.org/docs/rules/no-warning-comments).
@@ -249,7 +258,7 @@ Imagine you maintain a `main` branch at a version such as 10 and always keep wor
249258
- TODOs may or may not have a colon before the message such as
250259
`TODO [...]: message` or `TODO [...] message`.
251260
- If no proper argument is found, you'll be notified that the TODO is useless (See [`eslint/no-warning-comments`](#disallow-warning-comments-no-warning-comments)).
252-
- CSS, HTML, JSONC, JSON5, and Markdown comments are supported when using [`@eslint/css`](https://github.com/eslint/css), [`@html-eslint/eslint-plugin`](https://github.com/yeonjuan/html-eslint), [`@eslint/json`](https://github.com/eslint/json), and [`@eslint/markdown`](https://github.com/eslint/markdown).
261+
- CSS, HTML, JSONC, JSON5, Markdown, and YAML comments are supported when using [`@eslint/css`](https://github.com/eslint/css), [`@html-eslint/eslint-plugin`](https://github.com/yeonjuan/html-eslint), [`@eslint/json`](https://github.com/eslint/json), [`@eslint/markdown`](https://github.com/eslint/markdown), and [`eslint-plugin-yml`](https://github.com/ota-meshi/eslint-plugin-yml).
253262

254263
## Examples
255264

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
"eslint-plugin-eslint-plugin": "^7.4.1",
105105
"eslint-plugin-jsdoc": "^63.0.11",
106106
"eslint-plugin-unicorn": "^70.0.0",
107+
"eslint-plugin-yml": "^3.8.1",
107108
"eslint-remote-tester": "^4.0.4",
108109
"eslint-remote-tester-repositories": "^2.0.2",
109110
"eslint-scope": "^9.1.2",

readme.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
> More than 300 powerful ESLint rules
77
8-
Most rules target JavaScript and TypeScript, but [some also lint CSS, HTML, JSON, and Markdown](#non-javascript-files) when used with the matching ESLint language plugin.
8+
Most rules target JavaScript and TypeScript, but [some also lint CSS, HTML, JSON, Markdown, and YAML](#non-javascript-files) when used with the matching ESLint language plugin.
99

1010
[**Propose a new rule ➡**](.github/contributing.md)
1111

@@ -434,7 +434,7 @@ export default defineConfig([
434434

435435
### Non-JavaScript files
436436

437-
While most rules target JavaScript and TypeScript, some also lint other file types when used with the corresponding [ESLint language plugin](https://eslint.org/docs/latest/use/configure/plugins#specifying-a-language) such as [`@eslint/css`](https://github.com/eslint/css), [`@eslint/json`](https://github.com/eslint/json), [`@eslint/markdown`](https://github.com/eslint/markdown), or [`@html-eslint/eslint-plugin`](https://github.com/yeonjuan/html-eslint). Each such rule declares this with the `meta.languages` field.
437+
While most rules target JavaScript and TypeScript, some also lint other file types when used with the corresponding [ESLint language plugin](https://eslint.org/docs/latest/use/configure/plugins#specifying-a-language) such as [`@eslint/css`](https://github.com/eslint/css), [`@eslint/json`](https://github.com/eslint/json), [`@eslint/markdown`](https://github.com/eslint/markdown), [`@html-eslint/eslint-plugin`](https://github.com/yeonjuan/html-eslint), or [`eslint-plugin-yml`](https://github.com/ota-meshi/eslint-plugin-yml). Each such rule declares this with the `meta.languages` field.
438438

439439
When linting JSON, CSS, Markdown, HTML, or other non-JavaScript languages in the same ESLint config, scope Unicorn's JavaScript rule config objects with `files`. Include TypeScript/JSX extensions there only if your config already provides the matching parser/language setup for those files.
440440

@@ -522,18 +522,18 @@ These rules work on **any** file type:
522522

523523
These rules also work on specific non-JavaScript languages:
524524

525-
| Name | CSS | HTML | JSON | Markdown |
526-
| :-- | :-: | :-: | :-: | :-: |
527-
| [`expiring-todo-comments`](docs/rules/expiring-todo-comments.md) |||||
528-
| [`no-empty-file`](docs/rules/no-empty-file.md) |||||
529-
| [`no-invalid-file-input-accept`](docs/rules/no-invalid-file-input-accept.md) | || | |
530-
| [`no-manually-wrapped-comments`](docs/rules/no-manually-wrapped-comments.md) | | || |
531-
| [`no-missing-local-resource`](docs/rules/no-missing-local-resource.md) ||| ||
532-
| [`no-shorthand-property-overrides`](docs/rules/no-shorthand-property-overrides.md) || | | |
533-
| [`no-transition-all`](docs/rules/no-transition-all.md) || | | |
534-
| [`prefer-explicit-viewport-units`](docs/rules/prefer-explicit-viewport-units.md) || | | |
535-
| [`require-frontmatter-fields`](docs/rules/require-frontmatter-fields.md) | | | ||
536-
| [`text-encoding-identifier-case`](docs/rules/text-encoding-identifier-case.md) ||| | |
525+
| Name | CSS | HTML | JSON | Markdown | YAML |
526+
| :-- | :-: | :-: | :-: | :-: | :-: |
527+
| [`expiring-todo-comments`](docs/rules/expiring-todo-comments.md) ||||||
528+
| [`no-empty-file`](docs/rules/no-empty-file.md) ||||| |
529+
| [`no-invalid-file-input-accept`](docs/rules/no-invalid-file-input-accept.md) | || | | |
530+
| [`no-manually-wrapped-comments`](docs/rules/no-manually-wrapped-comments.md) | | || | |
531+
| [`no-missing-local-resource`](docs/rules/no-missing-local-resource.md) ||| || |
532+
| [`no-shorthand-property-overrides`](docs/rules/no-shorthand-property-overrides.md) || | | | |
533+
| [`no-transition-all`](docs/rules/no-transition-all.md) || | | | |
534+
| [`prefer-explicit-viewport-units`](docs/rules/prefer-explicit-viewport-units.md) || | | | |
535+
| [`require-frontmatter-fields`](docs/rules/require-frontmatter-fields.md) | | | || |
536+
| [`text-encoding-identifier-case`](docs/rules/text-encoding-identifier-case.md) ||| | | |
537537

538538
<!-- end auto-generated non-js languages list -->
539539

rules/expiring-todo-comments.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ const config = {
657657
'json/json5',
658658
'markdown/commonmark',
659659
'markdown/gfm',
660+
'yml/yaml',
660661
],
661662
},
662663
};

rules/utils/on-root.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
The root node type produced by each ESLint language:
33
4-
- `Program` — JavaScript/TypeScript, and HTML via `@html-eslint`
4+
- `Program` — JavaScript/TypeScript, HTML via `@html-eslint`, and YAML via `eslint-plugin-yml`
55
- `StyleSheet` — CSS via `@eslint/css`
66
- `Document` — JSON via `@eslint/json`
77
- `root` — Markdown via `@eslint/markdown`

scripts/create-non-js-languages-list.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ const languageLabels = {
77
html: 'HTML',
88
json: 'JSON',
99
markdown: 'Markdown',
10+
yml: 'YAML',
1011
};
11-
const columnOrder = ['css', 'html', 'json', 'markdown'];
12+
const columnOrder = ['css', 'html', 'json', 'markdown', 'yml'];
1213

1314
const beginMarker = '<!-- begin auto-generated non-js languages list -->';
1415
const endMarker = '<!-- end auto-generated non-js languages list -->';

test/expiring-todo-comments.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import css from '@eslint/css';
66
import json from '@eslint/json';
77
import markdown from '@eslint/markdown';
88
import htmlEslintPlugin from '@html-eslint/eslint-plugin';
9+
import yml from 'eslint-plugin-yml';
910
import unicorn from '../index.js';
1011
import {getTester} from './utils/test.js';
1112

@@ -852,3 +853,81 @@ test('supports CSS comments with @eslint/css', t => {
852853
],
853854
);
854855
});
856+
857+
test('supports YAML comments with eslint-plugin-yml', t => {
858+
const linter = new Linter({configType: 'flat'});
859+
const messages = linter.verify('key: value # TODO [2000-01-01]: Drop\n# TODO: Update config', {
860+
files: ['**/*.yml'],
861+
language: 'yml/yaml',
862+
plugins: {
863+
yml,
864+
unicorn,
865+
},
866+
rules: {
867+
'unicorn/expiring-todo-comments': [
868+
'error',
869+
{
870+
date: '2026-05-29',
871+
checkDates: true,
872+
checkDatesOnPullRequests: true,
873+
allowWarningComments: false,
874+
},
875+
],
876+
},
877+
}, {
878+
filename: 'fixture.yml',
879+
});
880+
881+
t.deepEqual(
882+
messages.map(({message, ruleId, line, column, endLine, endColumn}) => ({
883+
message,
884+
ruleId,
885+
line,
886+
column,
887+
endLine,
888+
endColumn,
889+
})),
890+
[
891+
{
892+
message: 'Past due date: 2000-01-01. Drop',
893+
ruleId: 'unicorn/expiring-todo-comments',
894+
line: 1,
895+
column: 12,
896+
endLine: 1,
897+
endColumn: 37,
898+
},
899+
{
900+
message: 'Unexpected \'todo\': \'TODO: Update config\'.',
901+
ruleId: 'unicorn/expiring-todo-comments',
902+
line: 2,
903+
column: 1,
904+
endLine: 2,
905+
endColumn: 22,
906+
},
907+
],
908+
);
909+
});
910+
911+
test('supports ESLint disable directives in YAML', t => {
912+
const linter = new Linter({configType: 'flat'});
913+
const messages = linter.verify('# eslint-disable-next-line unicorn/expiring-todo-comments -- TODO reason\n# TODO: Update config\nkey: value', {
914+
files: ['**/*.yaml'],
915+
language: 'yml/yaml',
916+
plugins: {
917+
yml,
918+
unicorn,
919+
},
920+
rules: {
921+
'unicorn/expiring-todo-comments': [
922+
'error',
923+
{
924+
allowWarningComments: false,
925+
},
926+
],
927+
},
928+
}, {
929+
filename: 'fixture.yaml',
930+
});
931+
932+
t.deepEqual(messages, []);
933+
});

0 commit comments

Comments
 (0)