Skip to content

Commit 2a29a8c

Browse files
committed
New: rule prefer-replace-text (fixes eslint-community#47).
1 parent efae7da commit 2a29a8c

File tree

3 files changed

+152
-0
lines changed

3 files changed

+152
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Name | ✔️ | 🛠 | Description
5959
[no-useless-token-range](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-useless-token-range.md) | ✔️ | 🛠 | Disallow unnecessary calls to sourceCode.getFirstToken and sourceCode.getLastToken
6060
[prefer-output-null](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-output-null.md) | | 🛠 | disallows invalid RuleTester test cases with the output the same as the code.
6161
[prefer-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-placeholders.md) | | | disallow template literals as report messages
62+
[prefer-replace-text](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-replace-text.md) | | 🛠 | prefer using replaceText instead of replaceTextRange.
6263
[report-message-format](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/report-message-format.md) | | | enforce a consistent format for rule report messages
6364
[require-meta-fixable](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/require-meta-fixable.md) | ✔️ | | require rules to implement a meta.fixable property
6465
[test-case-property-ordering](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/test-case-property-ordering.md) | | 🛠 | Requires the properties of a test case to be placed in a consistent order

lib/rules/prefer-replace-text.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* @fileoverview prefer using replaceText instead of replaceTextRange.
3+
* @author 薛定谔的猫<[email protected]>
4+
*/
5+
6+
'use strict';
7+
8+
const utils = require('../utils');
9+
10+
// ------------------------------------------------------------------------------
11+
// Rule Definition
12+
// ------------------------------------------------------------------------------
13+
14+
module.exports = {
15+
meta: {
16+
docs: {
17+
description: 'prefer using replaceText instead of replaceTextRange.',
18+
category: 'Rules',
19+
recommended: false,
20+
},
21+
fixable: 'code',
22+
schema: [],
23+
},
24+
25+
create (context) {
26+
const sourceCode = context.getSourceCode();
27+
const message = 'prefer using replaceText instead of replaceTextRange.';
28+
let funcInfo = {
29+
upper: null,
30+
codePath: null,
31+
shouldCheck: false,
32+
node: null,
33+
};
34+
let contextIdentifiers;
35+
36+
return {
37+
Program (node) {
38+
contextIdentifiers = utils.getContextIdentifiers(context, node);
39+
},
40+
41+
// Stacks this function's information.
42+
onCodePathStart (codePath, node) {
43+
const parent = node.parent;
44+
const shouldCheck = node.type === 'FunctionExpression' &&
45+
parent.parent.type === 'ObjectExpression' &&
46+
parent.parent.parent.type === 'CallExpression' &&
47+
contextIdentifiers.has(parent.parent.parent.callee.object) &&
48+
parent.parent.parent.callee.property.name === 'report' &&
49+
utils.getReportInfo(parent.parent.parent.arguments).fix === node;
50+
51+
funcInfo = {
52+
upper: funcInfo,
53+
codePath,
54+
shouldCheck,
55+
node,
56+
};
57+
},
58+
59+
// Pops this function's information.
60+
onCodePathEnd () {
61+
funcInfo = funcInfo.upper;
62+
},
63+
64+
// Checks the return statement is valid.
65+
'CallExpression[arguments.length>1]' (node) {
66+
if (funcInfo.shouldCheck &&
67+
node.callee.property.name === 'replaceTextRange') {
68+
const arg = node.arguments[0];
69+
if ((arg.type === 'MemberExpression' && arg.property.name === 'range')
70+
|| (
71+
arg.type === 'ArrayExpression' && arg.elements.length === 2 &&
72+
arg.elements[0].type === 'MemberExpression' && arg.elements[0].type === 'MemberExpression' && sourceCode.getText(arg.elements[0].object) === sourceCode.getText(arg.elements[1].object))
73+
) {
74+
context.report({
75+
node,
76+
message,
77+
});
78+
}
79+
}
80+
},
81+
};
82+
},
83+
};
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @fileoverview prefer using replaceText instead of replaceTextRange
3+
* @author 薛定谔的猫<[email protected]>
4+
*/
5+
6+
'use strict';
7+
8+
// ------------------------------------------------------------------------------
9+
// Requirements
10+
// ------------------------------------------------------------------------------
11+
12+
const rule = require('../../../lib/rules/prefer-replace-text');
13+
const RuleTester = require('eslint').RuleTester;
14+
15+
// ------------------------------------------------------------------------------
16+
// Tests
17+
// ------------------------------------------------------------------------------
18+
19+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
20+
const ERROR = { message: 'prefer using replaceText instead of replaceTextRange.' };
21+
22+
23+
ruleTester.run('prefer-placeholders', rule, {
24+
valid: [
25+
`
26+
module.exports = {
27+
create(context) {
28+
context.report({
29+
fix(fixer) {
30+
return fixer.replaceTextRange([start, end], '');
31+
}
32+
});
33+
}
34+
};
35+
`,
36+
],
37+
38+
invalid: [
39+
{
40+
code: `
41+
module.exports = {
42+
create(context) {
43+
context.report({
44+
fix(fixer) {
45+
return fixer.replaceTextRange(node.range, '');
46+
}
47+
});
48+
}
49+
};
50+
`,
51+
errors: [ERROR],
52+
},
53+
{
54+
code: `
55+
module.exports = {
56+
create(context) {
57+
context.report({
58+
fix(fixer) {
59+
return fixer.replaceTextRange([node.range[0], node.range[1]], '');
60+
}
61+
});
62+
}
63+
};
64+
`,
65+
errors: [ERROR],
66+
},
67+
],
68+
});

0 commit comments

Comments
 (0)