Skip to content

Commit d66730f

Browse files
committed
feat: make no-irregular-whitespace language-agnostic
1 parent 227c8cc commit d66730f

1 file changed

Lines changed: 113 additions & 0 deletions

File tree

  • designs/2026-language-agnostic-no-irregular-whitespace
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
- Repo: eslint/eslint
2+
- Start Date: 2026-07-14
3+
- RFC PR: https://github.com/eslint/rfcs/pull/150
4+
- Authors: xbinaryx
5+
6+
# Make no-irregular-whitespace Language-Agnostic
7+
8+
## Summary
9+
10+
Make the `no-irregular-whitespace` rule language-agnostic to prevent crashes on non-JavaScript files (e.g., CSS, JSON, Markdown), while accurately reporting irregular whitespace where applicable.
11+
12+
## Motivation
13+
14+
The introduction of Language Plugins enables ESLint to natively lint non-JavaScript languages such as CSS, JSON, and Markdown. However, running the core `no-irregular-whitespace` rule on files parsed by these plugins causes ESLint to crash with:
15+
`TypeError: Error while loading rule 'no-irregular-whitespace': sourceCode.getAllComments is not a function`.
16+
17+
This crash occurs because the rule assumes that the `SourceCode` object will always expose a `getAllComments()` method, which is JavaScript-specific and not implemented by the `@eslint/css`, `@eslint/json`, or `@eslint/markdown` language plugins. Furthermore, the rule assumes specific JS-specific AST node types (like `Literal`, `TemplateElement`, `JSXText`) to filter out whitespace inside strings and templates, and assumes the root AST node is always `Program`.
18+
19+
The goal is to make this rule language-agnostic in its basic operation, ensuring it checks for irregular whitespace characters across any language without crashing. This requires unifying how rules access comment nodes by standardizing on a `comments` property across all ESLint language plugins.
20+
21+
## Detailed Design
22+
23+
The proposed change is to standardize comment extraction and handle the varying implementations of ASTs across different language plugins.
24+
25+
Specifically:
26+
27+
1. **Standardize `comments` property on `SourceCode`:** - A `comments` property is to be added to the JavaScript `SourceCode` class. The `CSSSourceCode` and `JSONSourceCode` classes already expose this property. `MarkdownSourceCode` does not expose it, as Markdown does not have a direct equivalent to comments.
28+
29+
2. **Update `no-irregular-whitespace` Comment Extraction:** - The rule is to be updated to access the `sourceCode.comments` property, with a fallback to an empty array for languages like Markdown that lack comments.
30+
31+
```js
32+
const commentNodes = sourceCode.comments || [];
33+
```
34+
35+
Additionally, to make `skipComments` logic work reliably across all languages, the check will be updated to use `sourceCode.getText(node)` instead of `node.value`, as comment node shapes vary (e.g., JSON comment nodes from `@eslint/json` do not expose a `.value` property).
36+
37+
```js
38+
function removeInvalidNodeErrorsInComment(node) {
39+
if (ALL_IRREGULARS.test(sourceCode.getText(node))) {
40+
removeWhitespaceError(node);
41+
}
42+
}
43+
```
44+
45+
3. **Generic Syntax Exclusion via `skipNodes` Option:** - A new configuration option, `skipNodes`, will be added. This option will accept an array of ESQuery selectors. The rule will dynamically register traversal listeners for these selectors and strip any irregular whitespace errors found within their boundaries. For example, a user linting CSS or JSON could set `skipNodes: ["String"]`.
46+
47+
```js
48+
// In schema:
49+
// skipNodes: { type: "array", items: { type: "string" } }
50+
51+
const [{ skipNodes }] = context.options;
52+
53+
function removeInvalidNodeErrorsInSelector(node) {
54+
const rawText = sourceCode.getText(node);
55+
if (ALL_IRREGULARS.test(rawText)) {
56+
removeWhitespaceError(node);
57+
}
58+
}
59+
60+
skipNodes.forEach(selector => {
61+
nodes[selector] = removeInvalidNodeErrorsInSelector;
62+
});
63+
```
64+
65+
The existing JS-specific options (`skipStrings`, `skipRegExps`, etc.) will be retained and continue to function alongside `skipNodes` for backwards compatibility.
66+
67+
4. **Root Node Listener:** - The rule is to be updated to attach the main traversal to the root node type of the current AST, rather than the hardcoded `Program` node. This will accommodate different language plugins that use different root nodes (e.g., `StyleSheet` for CSS, `Document` for JSON, and `root` for Markdown).
68+
69+
```js
70+
const rootNodeType = sourceCode.ast.type;
71+
72+
nodes[rootNodeType] = function (node) {
73+
checkForIrregularWhitespace(node);
74+
checkForIrregularLineTerminators(node);
75+
};
76+
77+
nodes[`${rootNodeType}:exit`] = function () {
78+
// Handle comment stripping and reporting errors
79+
};
80+
```
81+
82+
## Documentation
83+
84+
- [Custom Rules documentation](https://eslint.org/docs/latest/extend/custom-rules) should be updated to document the new `comments` property on the `SourceCode` object.
85+
- [`no-irregular-whitespace` rule documentation](https://eslint.org/docs/latest/rules/no-irregular-whitespace) should be updated to document the new `skipNodes` option, explaining how to use it with ESQuery selectors, and to note that the rule can now safely be used on non-JavaScript files.
86+
87+
## Drawbacks
88+
89+
The existing JS-specific options (`skipStrings`, `skipTemplates`, `skipRegExps`, `skipJSXText`) will have no effect when the rule is used on non-JS files, since the corresponding AST node types (`Literal`, `TemplateElement`, `JSXText`) do not exist in CSS, JSON, or Markdown ASTs. The listeners are registered but never triggered. The new `skipNodes` option is the intended mechanism for non-JS exclusions.
90+
91+
## Backwards Compatibility Analysis
92+
93+
This change is fully backwards compatible. The JS `SourceCode` object will continue to expose the existing `getAllComments()` method alongside the new `comments` property, ensuring no disruption to ecosystem plugins that rely on the older API. The existing boolean options for skipping specific JS nodes (`skipStrings`, `skipTemplates`, etc.) will also continue to behave exactly as they do today.
94+
95+
## Alternatives
96+
97+
Separate rules, such as `css/no-irregular-whitespace` and `json/no-irregular-whitespace`, could be created. However, checking text for irregular whitespace is a text-based operation that applies to almost any language, making a unified language-agnostic core rule more maintainable.
98+
99+
## Open Questions
100+
101+
With the addition of the `comments` property to the JavaScript `SourceCode` class, its existing `getAllComments()` method becomes redundant. Should a formal deprecation (via JSDoc `@deprecated` tag and documentation updates) be included in the scope of this RFC, or should it be deferred to avoid immediate ecosystem churn?
102+
103+
## Help Needed
104+
105+
None.
106+
107+
## Frequently Asked Questions
108+
109+
None.
110+
111+
## Related Discussions
112+
113+
- eslint/eslint#19805

0 commit comments

Comments
 (0)