Skip to content

Commit 39a6424

Browse files
authored
fix!: assert 'text' is a string across all RuleFixer methods (#20082)
* fix!: assert 'text' is a string across all RuleFixer methods * fix failing tests * correct JSDoc type for checkComputedProperty value * update migration guide
1 parent f28fbf8 commit 39a6424

4 files changed

Lines changed: 88 additions & 4 deletions

File tree

docs/src/use/migrate-to-10.0.0.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ The lists below are ordered roughly by the number of users each change is expect
2828

2929
- [Node.js < v20.19, v21, v23 are no longer supported](#drop-old-node)
3030
- [Removal of `type` property in errors of invalid `RuleTester` cases](#ruletester-type-removed)
31+
- [Fixer methods now require string `text` arguments](#fixer-text-must-be-string)
3132

3233
### Breaking changes for integration developers
3334

@@ -124,6 +125,23 @@ In ESLint v10, the deprecated `type` property in errors of invalid test cases fo
124125

125126
**Related issue(s):** [#19029](https://github.com/eslint/eslint/issues/19029)
126127

128+
## <a name="fixer-text-must-be-string"></a> Fixer methods now require string `text` arguments
129+
130+
In ESLint v10, all rule fixer methods that accept a `text` argument now require that it be a string. Providing a non-string value will throw a `TypeError`.
131+
132+
Affected methods:
133+
134+
- `insertTextBefore(nodeOrToken, text)`
135+
- `insertTextBeforeRange(range, text)`
136+
- `insertTextAfter(nodeOrToken, text)`
137+
- `insertTextAfterRange(range, text)`
138+
- `replaceText(nodeOrToken, text)`
139+
- `replaceTextRange(range, text)`
140+
141+
**To address:** Ensure the `text` value you pass to fixer methods is a string.
142+
143+
**Related issue(s):** [#18807](https://github.com/eslint/eslint/issues/18807)
144+
127145
## <a name="lintmessage-nodetype-removed"></a> Removal of `nodeType` property in `LintMessage` objects
128146

129147
In ESLint v10, the deprecated `nodeType` property on `LintMessage` objects has been removed. This affects consumers of the Node.js API (for example, custom formatters and editor/tool integrations) that previously relied on `message.nodeType`.

lib/linter/rule-fixer.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ function insertTextAt(index, text) {
3838
};
3939
}
4040

41+
/**
42+
* Asserts that the provided text is a string.
43+
* @param {unknown} text The text to validate.
44+
* @returns {void}
45+
* @throws {TypeError} If `text` is not a string.
46+
*/
47+
function assertIsString(text) {
48+
if (typeof text !== "string") {
49+
throw new TypeError("'text' must be a string");
50+
}
51+
}
52+
4153
//------------------------------------------------------------------------------
4254
// Public Interface
4355
//------------------------------------------------------------------------------
@@ -67,8 +79,11 @@ class RuleFixer {
6779
* @param {ASTNode|Token} nodeOrToken The node or token to insert after.
6880
* @param {string} text The text to insert.
6981
* @returns {Object} The fix command.
82+
* @throws {TypeError} If `text` is not a string.
7083
*/
7184
insertTextAfter(nodeOrToken, text) {
85+
assertIsString(text);
86+
7287
const range = this.#sourceCode.getRange(nodeOrToken);
7388

7489
return this.insertTextAfterRange(range, text);
@@ -81,8 +96,11 @@ class RuleFixer {
8196
* is end of range.
8297
* @param {string} text The text to insert.
8398
* @returns {Object} The fix command.
99+
* @throws {TypeError} If `text` is not a string.
84100
*/
85101
insertTextAfterRange(range, text) {
102+
assertIsString(text);
103+
86104
return insertTextAt(range[1], text);
87105
}
88106

@@ -92,8 +110,11 @@ class RuleFixer {
92110
* @param {ASTNode|Token} nodeOrToken The node or token to insert before.
93111
* @param {string} text The text to insert.
94112
* @returns {Object} The fix command.
113+
* @throws {TypeError} If `text` is not a string.
95114
*/
96115
insertTextBefore(nodeOrToken, text) {
116+
assertIsString(text);
117+
97118
const range = this.#sourceCode.getRange(nodeOrToken);
98119

99120
return this.insertTextBeforeRange(range, text);
@@ -106,8 +127,11 @@ class RuleFixer {
106127
* is end of range.
107128
* @param {string} text The text to insert.
108129
* @returns {Object} The fix command.
130+
* @throws {TypeError} If `text` is not a string.
109131
*/
110132
insertTextBeforeRange(range, text) {
133+
assertIsString(text);
134+
111135
return insertTextAt(range[0], text);
112136
}
113137

@@ -117,8 +141,11 @@ class RuleFixer {
117141
* @param {ASTNode|Token} nodeOrToken The node or token to remove.
118142
* @param {string} text The text to insert.
119143
* @returns {Object} The fix command.
144+
* @throws {TypeError} If `text` is not a string.
120145
*/
121146
replaceText(nodeOrToken, text) {
147+
assertIsString(text);
148+
122149
const range = this.#sourceCode.getRange(nodeOrToken);
123150

124151
return this.replaceTextRange(range, text);
@@ -131,8 +158,11 @@ class RuleFixer {
131158
* is end of range.
132159
* @param {string} text The text to insert.
133160
* @returns {Object} The fix command.
161+
* @throws {TypeError} If `text` is not a string.
134162
*/
135163
replaceTextRange(range, text) {
164+
assertIsString(text);
165+
136166
return {
137167
range,
138168
text,

lib/rules/dot-notation.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ module.exports = {
7676
/**
7777
* Check if the property is valid dot notation
7878
* @param {ASTNode} node The dot notation node
79-
* @param {string} value Value which is to be checked
79+
* @param {string|boolean|null} value Value which is to be checked
8080
* @returns {void}
8181
*/
8282
function checkComputedProperty(node, value) {
@@ -125,7 +125,7 @@ module.exports = {
125125
}
126126
yield fixer.replaceTextRange(
127127
[leftBracket.range[0], rightBracket.range[1]],
128-
value,
128+
String(value),
129129
);
130130

131131
// Insert a space after the property if it will be connected to the next token.

tests/lib/linter/rule-fixer.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ describe("RuleFixer", () => {
4949
text: "",
5050
});
5151
});
52+
53+
it("should throw when inserting non-string text", () => {
54+
assert.throws(() => {
55+
ruleFixer.insertTextBefore({ range: [10, 20] }, null);
56+
}, /'text' must be a string/u);
57+
});
5258
});
5359

5460
describe("insertTextBeforeRange", () => {
@@ -69,6 +75,12 @@ describe("RuleFixer", () => {
6975
text: "",
7076
});
7177
});
78+
79+
it("should throw when inserting non-string text", () => {
80+
assert.throws(() => {
81+
ruleFixer.insertTextBeforeRange([10, 20], null);
82+
}, /'text' must be a string/u);
83+
});
7284
});
7385

7486
describe("insertTextAfter", () => {
@@ -89,6 +101,12 @@ describe("RuleFixer", () => {
89101
text: "",
90102
});
91103
});
104+
105+
it("should throw when inserting non-string text", () => {
106+
assert.throws(() => {
107+
ruleFixer.insertTextAfter({ range: [10, 20] }, null);
108+
}, /'text' must be a string/u);
109+
});
92110
});
93111

94112
describe("insertTextAfterRange", () => {
@@ -109,9 +127,15 @@ describe("RuleFixer", () => {
109127
text: "",
110128
});
111129
});
130+
131+
it("should throw when inserting non-string text", () => {
132+
assert.throws(() => {
133+
ruleFixer.insertTextAfterRange([10, 20], null);
134+
}, /'text' must be a string/u);
135+
});
112136
});
113137

114-
describe("removeAfter", () => {
138+
describe("remove", () => {
115139
it("should return an object with the correct information when called", () => {
116140
const result = ruleFixer.remove({ range: [0, 1] });
117141

@@ -122,7 +146,7 @@ describe("RuleFixer", () => {
122146
});
123147
});
124148

125-
describe("removeAfterRange", () => {
149+
describe("removeRange", () => {
126150
it("should return an object with the correct information when called", () => {
127151
const result = ruleFixer.removeRange([0, 1]);
128152

@@ -142,6 +166,12 @@ describe("RuleFixer", () => {
142166
text: "Hi",
143167
});
144168
});
169+
170+
it("should throw when replacing with non-string text", () => {
171+
assert.throws(() => {
172+
ruleFixer.replaceText({ range: [0, 1] }, null);
173+
}, /'text' must be a string/u);
174+
});
145175
});
146176

147177
describe("replaceTextRange", () => {
@@ -153,5 +183,11 @@ describe("RuleFixer", () => {
153183
text: "Hi",
154184
});
155185
});
186+
187+
it("should throw when replacing with non-string text", () => {
188+
assert.throws(() => {
189+
ruleFixer.replaceTextRange([0, 1], null);
190+
}, /'text' must be a string/u);
191+
});
156192
});
157193
});

0 commit comments

Comments
 (0)