Skip to content

Commit 95021dd

Browse files
authored
New: Add new rule prefer-message-ids (#170)
1 parent fcbb65c commit 95021dd

24 files changed

+298
-56
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Name | ✔️ | 🛠 | 💡 | Description
5757
[no-only-tests](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-only-tests.md) | | | 💡 | disallow the test case property `only`
5858
[no-unused-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/no-unused-placeholders.md) | ✔️ | | | disallow unused placeholders in rule report messages
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()`
60+
[prefer-message-ids](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-message-ids.md) | | | | require using `messageId` instead of `message` to report rule violations
6061
[prefer-object-rule](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-object-rule.md) | | 🛠 | | disallow rule exports where the export is a function
6162
[prefer-output-null](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-output-null.md) | | 🛠 | | disallow invalid RuleTester test cases where the `output` matches the `code`
6263
[prefer-placeholders](https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-placeholders.md) | | | | require using placeholders for dynamic report messages

docs/rules/prefer-message-ids.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Require using `messageId` instead of `message` to report rule violations (prefer-message-ids)
2+
3+
When reporting a rule violation, it's preferred to provide the violation message with the `messageId` property instead of the `message` property. Message IDs provide the following benefits:
4+
5+
* Rule violation messages can be stored in a central `meta.messages` object for convenient management
6+
* Rule violation messages do not need to be repeated in both the rule file and rule test file
7+
8+
## Rule Details
9+
10+
This rule catches usages of the `message` property when reporting a rule violation.
11+
12+
Examples of **incorrect** code for this rule:
13+
14+
```js
15+
/* eslint eslint-plugin/prefer-message-ids: error */
16+
17+
module.exports = {
18+
create (context) {
19+
return {
20+
CallExpression (node) {
21+
context.report({
22+
node,
23+
message: 'Some error message.',
24+
});
25+
},
26+
};
27+
},
28+
};
29+
```
30+
31+
Examples of **correct** code for this rule:
32+
33+
```js
34+
/* eslint eslint-plugin/prefer-message-ids: error */
35+
36+
module.exports = {
37+
meta: {
38+
messages: {
39+
someMessageId: 'Some error message',
40+
},
41+
},
42+
create (context) {
43+
return {
44+
CallExpression (node) {
45+
context.report({
46+
node,
47+
messageId: 'someMessageId',
48+
});
49+
},
50+
};
51+
},
52+
};
53+
```
54+
55+
## Further Reading
56+
57+
* [messageIds API](https://eslint.org/docs/developer-guide/working-with-rules#messageids)

lib/rules/consistent-output.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ module.exports = {
2626
enum: ['always', 'consistent'],
2727
},
2828
],
29+
messages: {
30+
missingOutput: 'This test case should have an output assertion.',
31+
},
2932
},
3033

3134
create (context) {
@@ -48,7 +51,7 @@ module.exports = {
4851
casesWithoutOutput.forEach(testCase => {
4952
context.report({
5053
node: testCase,
51-
message: 'This test case should have an output assertion.',
54+
messageId: 'missingOutput',
5255
});
5356
});
5457
}

lib/rules/meta-property-ordering.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ module.exports = {
2323
type: 'array',
2424
elements: { type: 'string' },
2525
}],
26+
messages: {
27+
inconsistentOrder: 'The meta properties should be placed in a consistent order: [{{order}}].',
28+
},
2629
},
2730

2831
create (context) {
2932
const sourceCode = context.getSourceCode();
3033
const info = getRuleInfo(sourceCode);
3134

32-
const message = 'The meta properties should be placed in a consistent order: [{{order}}].';
3335
const order = context.options[0] || ['type', 'docs', 'fixable', 'schema', 'messages'];
3436

3537
const orderMap = new Map(order.map((name, i) => [name, i]));
@@ -67,7 +69,7 @@ module.exports = {
6769
for (const violatingProp of violatingProps) {
6870
context.report({
6971
node: violatingProp,
70-
message,
72+
messageId: 'inconsistentOrder',
7173
data: {
7274
order: knownProps.map(getKeyName).join(', '),
7375
},

lib/rules/no-deprecated-context-methods.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ module.exports = {
4444
},
4545
fixable: 'code',
4646
schema: [],
47+
messages: {
48+
newFormat: 'Use `{{contextName}}.getSourceCode().{{replacement}}` instead of `{{contextName}}.{{original}}`.',
49+
},
4750
},
4851

4952
create (context) {
@@ -66,7 +69,7 @@ module.exports = {
6669
contextId =>
6770
context.report({
6871
node: contextId.parent,
69-
message: 'Use `{{contextName}}.getSourceCode().{{replacement}}` instead of `{{contextName}}.{{original}}`.',
72+
messageId: 'newFormat',
7073
data: {
7174
contextName: contextId.name,
7275
original: contextId.parent.property.name,

lib/rules/no-deprecated-report-api.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ module.exports = {
2121
},
2222
fixable: 'code', // or "code" or "whitespace"
2323
schema: [],
24+
messages: {
25+
useNewAPI: 'Use the new-style context.report() API.',
26+
},
2427
},
2528

2629
create (context) {
@@ -44,7 +47,7 @@ module.exports = {
4447
) {
4548
context.report({
4649
node: node.callee.property,
47-
message: 'Use the new-style context.report() API.',
50+
messageId: 'useNewAPI',
4851
fix (fixer) {
4952
const openingParen = sourceCode.getTokenBefore(node.arguments[0]);
5053
const closingParen = sourceCode.getLastToken(node);

lib/rules/no-missing-placeholders.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ module.exports = {
2222
},
2323
fixable: null,
2424
schema: [],
25+
messages: {
26+
placeholderDoesNotExist: 'The placeholder {{{{missingKey}}}} does not exist.',
27+
},
2528
},
2629

2730
create (context) {
@@ -66,7 +69,7 @@ module.exports = {
6669
if (!matchingProperty) {
6770
context.report({
6871
node: reportInfo.message,
69-
message: 'The placeholder {{{{missingKey}}}} does not exist.',
72+
messageId: 'placeholderDoesNotExist',
7073
data: { missingKey: match[1] },
7174
});
7275
}

lib/rules/no-unused-placeholders.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ module.exports = {
2222
},
2323
fixable: null,
2424
schema: [],
25+
messages: {
26+
placeholderUnused: 'The placeholder {{{{unusedKey}}}} is unused.',
27+
},
2528
},
2629

2730
create (context) {
@@ -69,7 +72,7 @@ module.exports = {
6972
if (!placeholdersInMessage.has(key)) {
7073
context.report({
7174
node: reportInfo.message,
72-
message: 'The placeholder {{{{unusedKey}}}} is unused.',
75+
messageId: 'placeholderUnused',
7376
data: { unusedKey: key },
7477
});
7578
}

lib/rules/no-useless-token-range.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ module.exports = {
2121
},
2222
fixable: 'code',
2323
schema: [],
24+
messages: {
25+
useReplacement: "Use '{{replacementText}}' instead.",
26+
},
2427
},
2528

2629
create (context) {
@@ -125,7 +128,7 @@ module.exports = {
125128
sourceCode.text.slice(identifier.parent.parent.range[1], fullRangeAccess.range[1]);
126129
context.report({
127130
node: identifier.parent.parent,
128-
message: "Use '{{replacementText}}' instead.",
131+
messageId: 'useReplacement',
129132
data: { replacementText },
130133
fix (fixer) {
131134
return fixer.replaceText(identifier.parent.parent, sourceCode.getText(identifier.parent.parent.arguments[0]));

lib/rules/prefer-message-ids.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use strict';
2+
3+
const utils = require('../utils');
4+
5+
// ------------------------------------------------------------------------------
6+
// Rule Definition
7+
// ------------------------------------------------------------------------------
8+
9+
module.exports = {
10+
meta: {
11+
type: 'problem',
12+
docs: {
13+
description: 'require using `messageId` instead of `message` to report rule violations',
14+
category: 'Rules',
15+
recommended: false,
16+
},
17+
fixable: null,
18+
schema: [],
19+
messages: {
20+
foundMessage: 'Use `messageId` instead of `message`.',
21+
},
22+
},
23+
24+
create (context) {
25+
let contextIdentifiers;
26+
27+
// ----------------------------------------------------------------------
28+
// Public
29+
// ----------------------------------------------------------------------
30+
31+
return {
32+
Program (ast) {
33+
contextIdentifiers = utils.getContextIdentifiers(context, ast);
34+
},
35+
CallExpression (node) {
36+
if (
37+
node.callee.type === 'MemberExpression' &&
38+
contextIdentifiers.has(node.callee.object) &&
39+
node.callee.property.type === 'Identifier' && node.callee.property.name === 'report'
40+
) {
41+
const reportInfo = utils.getReportInfo(node.arguments, context);
42+
if (!reportInfo || !reportInfo.message) {
43+
return;
44+
}
45+
46+
context.report({
47+
node: reportInfo.message.parent,
48+
messageId: 'foundMessage',
49+
});
50+
}
51+
},
52+
};
53+
},
54+
};

0 commit comments

Comments
 (0)