diff --git a/lib/rules/prefer-message-ids.js b/lib/rules/prefer-message-ids.js index 9fca98a5..789173ff 100644 --- a/lib/rules/prefer-message-ids.js +++ b/lib/rules/prefer-message-ids.js @@ -21,9 +21,11 @@ module.exports = { fixable: null, schema: [], messages: { + foundMessage: 'Use `messageId` instead of `message`.', messagesMissing: '`meta.messages` must contain at least one violation message.', - foundMessage: 'Use `messageId` instead of `message`.', + missingMessage: '`meta.messages` is missing this messageId.', + unusedMessage: 'This messageId was never used.', }, }, @@ -31,6 +33,7 @@ module.exports = { const sourceCode = context.getSourceCode(); const info = utils.getRuleInfo(sourceCode); + const messageIdsUsed = new Set(); let contextIdentifiers; // ---------------------------------------------------------------------- @@ -83,6 +86,18 @@ module.exports = { }); } }, + + 'Program:exit'() { + for (const messageIdNode of utils + .getMessageIdNodes(info) + .filter((node) => !messageIdsUsed.has(node.key.name))) { + context.report({ + node: messageIdNode, + messageId: 'unusedMessage', + }); + } + }, + CallExpression(node) { if ( node.callee.type === 'MemberExpression' && @@ -95,15 +110,28 @@ module.exports = { return; } - const reportMessagesAndDataArray = utils - .collectReportViolationAndSuggestionData(reportInfo) - .filter((obj) => obj.message); - for (const { message } of reportMessagesAndDataArray) { + const reportMessagesAndDataArray = + utils.collectReportViolationAndSuggestionData(reportInfo); + + for (const { message } of reportMessagesAndDataArray.filter( + (obj) => obj.message + )) { context.report({ node: message.parent, messageId: 'foundMessage', }); } + + for (const { messageId } of reportMessagesAndDataArray.filter( + (obj) => obj.messageId && obj.messageId.type === 'Literal' + )) { + messageIdsUsed.add(messageId.value); + if (!utils.getMessageById(info, messageId.value)) + context.report({ + node: messageId, + messageId: 'missingMessage', + }); + } } }, }; diff --git a/lib/utils.js b/lib/utils.js index 26138755..5d38a2f3 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -762,4 +762,41 @@ module.exports = { return [property]; }); }, + + getMessageIdNodes(ruleInfo) { + if (!ruleInfo) { + return []; + } + const metaNode = ruleInfo.meta; + const messagesNode = + metaNode && + metaNode.properties && + metaNode.properties.find( + (p) => p.type === 'Property' && this.getKeyName(p) === 'messages' + ); + + return messagesNode && messagesNode.value.type === 'ObjectExpression' + ? messagesNode.value.properties + : []; + }, + + getMessageById(ruleInfo, messageId) { + const metaNode = ruleInfo.meta; + const messagesNode = + metaNode && + metaNode.properties && + metaNode.properties.find( + (p) => p.type === 'Property' && this.getKeyName(p) === 'messages' + ); + + const correspondingMessage = + messagesNode && + messagesNode.value.properties && + messagesNode.value.properties.find( + (p) => p.type === 'Property' && this.getKeyName(p) === messageId + ); + if (correspondingMessage) { + return correspondingMessage; + } + }, }; diff --git a/tests/lib/rules/prefer-message-ids.js b/tests/lib/rules/prefer-message-ids.js index 6372f00e..563e3dba 100644 --- a/tests/lib/rules/prefer-message-ids.js +++ b/tests/lib/rules/prefer-message-ids.js @@ -130,6 +130,7 @@ ruleTester.run('prefer-message-ids', rule, { module.exports = { meta: { messages: { foo: 'hello world' } }, create(context) { + context.report({ node, messageId: 'foo' }); context.report({ node, message: 'foo' }); } }; @@ -142,6 +143,7 @@ ruleTester.run('prefer-message-ids', rule, { module.exports = { meta: { messages: { foo: 'hello world' } }, create(context) { + context.report({ node, messageId: 'foo' }); context.report({ node, suggest: [{desc:'foo'}] }); } }; @@ -154,6 +156,7 @@ ruleTester.run('prefer-message-ids', rule, { export default { meta: { messages: { foo: 'hello world' } }, create(context) { + context.report({ node, messageId: 'foo' }); context.report({ node, message: 'foo' }); } }; @@ -168,6 +171,7 @@ ruleTester.run('prefer-message-ids', rule, { module.exports = { meta: { messages: { foo: 'hello world' } }, create(context) { + context.report({ node, messageId: 'foo' }); context.report({ node, message: MESSAGE @@ -183,6 +187,7 @@ ruleTester.run('prefer-message-ids', rule, { module.exports = { meta: { messages: { foo: 'hello world' } }, create(context) { + context.report({ node, messageId: 'foo' }); context.report({ node, message: foo + ' is bad.' @@ -270,5 +275,30 @@ ruleTester.run('prefer-message-ids', rule, { { messageId: 'foundMessage', type: 'Property' }, ], }, + { + // messageId missing + code: ` + module.exports = { + meta: { messages: { foo: 'hello world' } }, + create(context) { + context.report({ node, messageId: 'foo' }); + context.report({ node, messageId: 'bar' }); + } + }; + `, + errors: [{ messageId: 'missingMessage', type: 'Literal' }], + }, + { + // messageId unused + code: ` + module.exports = { + meta: { messages: { foo: 'hello world', bar: 'hello world 2' } }, + create(context) { + context.report({ node, messageId: 'foo' }); + } + }; + `, + errors: [{ messageId: 'unusedMessage', type: 'Property' }], + }, ], });