diff --git a/src/rules/valid-key.spec.ts b/src/rules/valid-key.spec.ts index e8c9e73..b5fb7d5 100644 --- a/src/rules/valid-key.spec.ts +++ b/src/rules/valid-key.spec.ts @@ -39,6 +39,14 @@ ruleTester.run("valid-key", rule, { code: ``, settings: NESTED_SETTINGS, }, + { + code: `t("valid")`, + settings: FLAT_SETTINGS, + }, + { + code: `t("nested.valid")`, + settings: NESTED_SETTINGS, + }, ], invalid: [ { @@ -110,5 +118,64 @@ ruleTester.run("valid-key", rule, { }, ], }, + { + code: `t(dynamicKey)`, + settings: FLAT_SETTINGS, + errors: [ + { + messageId: "dynamic-key", + data: { filePath: "test/fixtures/flat/en-US.json" }, + }, + ], + }, + { + code: `t(42)`, + settings: FLAT_SETTINGS, + errors: [ + { + messageId: "wrong-key-type", + data: { filePath: "test/fixtures/flat/en-US.json" }, + }, + ], + }, + { + code: `t("invalid")`, + settings: FLAT_SETTINGS, + errors: [ + { + messageId: "non-existing-key", + data: { + key: "invalid", + closestKey: "valid", + }, + }, + ], + }, + { + code: `t("nested.invalid")`, + settings: NESTED_SETTINGS, + errors: [ + { + messageId: "non-existing-key", + data: { + key: "nested.invalid", + closestKey: "nested.valid", + }, + }, + ], + }, + { + code: `t("onlyInEn")`, + settings: FLAT_SETTINGS, + errors: [ + { + messageId: "missing-key-in-file", + data: { + key: "onlyInEn", + filePath: "test/fixtures/flat/es-ES.json", + }, + }, + ], + }, ], }); diff --git a/src/rules/valid-key.ts b/src/rules/valid-key.ts index 040a126..2d0293a 100644 --- a/src/rules/valid-key.ts +++ b/src/rules/valid-key.ts @@ -54,6 +54,62 @@ export default createRule({ ); const keysArray = Array.from(keys); + const validateStaticValue = ( + node: TSESTree.Node, + staticValue: { + value: unknown; + } | null + ) => { + if (!staticValue) { + context.report({ + messageId: "dynamic-key", + node, + }); + return; + } + + if (typeof staticValue.value !== "string") { + context.report({ + messageId: "wrong-key-type", + node, + }); + return; + } + + const key = staticValue.value; + + if (!keys.has(key)) { + context.report({ + messageId: "non-existing-key", + node, + data: { + key, + closestKey: closest(key, keysArray), + }, + }); + return; + } + + for (const [filePath, translation] of Object.entries(translations)) { + if ( + !hasKeyInTranslation( + settings.translationFiles.format, + translation, + key + ) + ) { + context.report({ + messageId: "missing-key-in-file", + node, + data: { + key, + filePath, + }, + }); + } + } + }; + return { JSXElement: (element) => { if (isTransElement(element)) { @@ -79,54 +135,23 @@ export default createRule({ context.getScope() ); - if (!staticValue) { - context.report({ - messageId: "dynamic-key", - node: attribute, - }); - return; - } - - if (typeof staticValue.value !== "string") { - context.report({ - messageId: "wrong-key-type", - node: attribute, - }); - return; - } - - const key = staticValue.value; - - if (!keys.has(key)) { - context.report({ - messageId: "non-existing-key", - node: attribute, - data: { - key, - closestKey: closest(key, keysArray), - }, - }); + validateStaticValue(attribute, staticValue); + } + }, + CallExpression: (expression) => { + if ( + ASTUtils.isIdentifier(expression.callee) && + expression.callee.name === "t" + ) { + if (!expression.arguments.length) { return; } - - for (const [filePath, translation] of Object.entries(translations)) { - if ( - !hasKeyInTranslation( - settings.translationFiles.format, - translation, - key - ) - ) { - context.report({ - messageId: "missing-key-in-file", - node: attribute, - data: { - key, - filePath, - }, - }); - } - } + const [firstArgument] = expression.arguments; + const staticValue = ASTUtils.getStaticValue( + firstArgument, + context.getScope() + ); + validateStaticValue(firstArgument, staticValue); } }, };