diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 51de8b5a..e9ef6f20 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -22,9 +22,9 @@ module.exports = { } }, { - files: ['src/i18n/**/translations.ts'], // Specify the path pattern for the files you want to apply the rule to + files: ['src/i18n/**/translations.ts'], rules: { - "internal-rules/check-i18n-keys": "off" + "internal-rules/check-i18n-keys": "warn" } }, ], diff --git a/tools/internal-rules/lib/rules/check-i18n-keys.js b/tools/internal-rules/lib/rules/check-i18n-keys.js index 75629b8a..5c5aec5f 100644 --- a/tools/internal-rules/lib/rules/check-i18n-keys.js +++ b/tools/internal-rules/lib/rules/check-i18n-keys.js @@ -18,89 +18,65 @@ module.exports = { schema: [] }, create: function (context) { - function extractKeysFromObjectExpression(node) { + function extractKeysFromObjectExpression(node, parentKey = "") { const keys = []; - function traverseProperties(properties) { - if (!properties) return; // Handle undefined or null properties - - for (const property of properties) { + function traverseObjectProperties(properties, parentKey) { + properties.forEach((property) => { if ( - property.type === "Property" && - property.value.type === "ObjectExpression" - ) { - traverseProperties(property.value.properties); - } else if ( - property.type === "Property" && - property.value.type === "ArrayExpression" - ) { - traverseArrayElements(property.value.elements); - } else if ( property.type === "Property" && property.key.type === "Identifier" ) { - keys.push(property.key.name); - } - } - } - - function traverseArrayElements(elements) { - if (!elements) return; // Handle undefined or null elements - - for (const element of elements) { - if (element.type === "ObjectExpression") { - traverseProperties(element.properties); + const currentKey = parentKey + ? `${parentKey}.${property.key.name}` + : property.key.name; + keys.push(currentKey); + if (property.value.type === "ObjectExpression") { + traverseObjectProperties( + property.value.properties, + currentKey + ); + } } - } + }); } - traverseProperties(node.properties); + traverseObjectProperties(node.properties, parentKey); return keys; } - function extractKeysFromFile(filePath) { + function extractKeysFromFile(filePath, parentKey = "") { const fileContent = fs.readFileSync(filePath, "utf8"); const ast = parse(fileContent, { - sourceType: "module", // or 'script' depending on your file + sourceType: "module", plugins: ["typescript", "jsx"] }); const keys = []; const properties = ast.program.body[0].declaration.properties; - function traverseProperties(properties) { - for (const property of properties) { + function traverseFileProperties(properties, parentKey) { + properties.forEach((property) => { if ( - property.type === "ObjectProperty" && - property.value.type === "ObjectExpression" - ) { - traverseProperties(property.value.properties); - } else if ( - property.type === "ObjectProperty" && - property.value.type === "ArrayExpression" - ) { - traverseArrayElements(property.value.elements); - } else if ( property.type === "ObjectProperty" && property.key.type === "Identifier" ) { - keys.push(property.key.name); - } - } - } - - function traverseArrayElements(elements) { - if (!elements) return; // Handle undefined or null elements - - for (const element of elements) { - if (element.type === "ObjectExpression") { - traverseProperties(element.properties); + const currentKey = parentKey + ? `${parentKey}.${property.key.name}` + : property.key.name; + keys.push(currentKey); + if (property.value.type === "ObjectExpression") { + traverseFileProperties( + property.value.properties, + currentKey + ); + } } - } + }); } - traverseProperties(properties); + traverseFileProperties(properties, parentKey); return keys; } @@ -108,7 +84,10 @@ module.exports = { return { Program(node) { for (const statement of node.body) { - const relativePath = path.relative(process.cwd(), context.getFilename()) + const relativePath = path.relative( + process.cwd(), + context.getFilename() + ); const fallbackFilePath = path .relative(process.cwd(), context.getFilename()) .replace( @@ -121,27 +100,23 @@ module.exports = { ); const enKeys = extractKeysFromFile(fallbackFilePath); - // Report missing keys and incorrect order - enKeys.forEach((enKey, index) => { + + // Report missing keys + enKeys.forEach((enKey) => { if (!keys.includes(enKey)) { context.report({ node: node, - message: `missing key '${enKey}'` - }); - } else if (keys.indexOf(enKey) !== index) { - context.report({ - node: node, - message: `incorrect key location '${enKey}'` + message: `missing key '${enKey}' ${relativePath}` }); } }); // Report extra keys - keys.forEach(key => { + keys.forEach((key) => { if (!enKeys.includes(key)) { context.report({ node: node, - message: `extra key '${key}'` + message: `extra key '${key}' ${relativePath}` }); } }); diff --git a/tools/internal-rules/package.json b/tools/internal-rules/package.json index c2dd173f..02929906 100644 --- a/tools/internal-rules/package.json +++ b/tools/internal-rules/package.json @@ -1,38 +1,38 @@ { - "name": "eslint-plugin-internal-rules", - "version": "0.0.0", - "description": "internal eslint rules", - "keywords": [ - "eslint", - "eslintplugin", - "eslint-plugin" - ], - "author": "", - "main": "./lib/index.js", - "exports": "./lib/index.js", - "scripts": { - "lint": "npm-run-all \"lint:*\"", - "lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"", - "lint:js": "eslint .", - "test": "mocha tests --recursive", - "update:eslint-docs": "eslint-doc-generator" - }, - "dependencies": { - "requireindex": "^1.2.0" - }, - "devDependencies": { - "eslint": "^8.19.0", - "eslint-doc-generator": "^1.0.0", - "eslint-plugin-eslint-plugin": "^5.0.0", - "eslint-plugin-node": "^11.1.0", - "mocha": "^10.0.0", - "npm-run-all": "^4.1.5" - }, - "engines": { - "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" - }, - "peerDependencies": { - "eslint": ">=7" - }, - "license": "ISC" + "name": "eslint-plugin-internal-rules", + "version": "0.0.0", + "description": "internal eslint rules", + "keywords": [ + "eslint", + "eslintplugin", + "eslint-plugin" + ], + "author": "", + "main": "./lib/index.js", + "exports": "./lib/index.js", + "scripts": { + "lint": "npm-run-all \"lint:*\"", + "lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"", + "lint:js": "eslint .", + "test": "mocha tests --recursive", + "update:eslint-docs": "eslint-doc-generator" + }, + "dependencies": { + "requireindex": "^1.2.0" + }, + "devDependencies": { + "eslint": "^8.19.0", + "eslint-doc-generator": "^1.0.0", + "eslint-plugin-eslint-plugin": "^5.0.0", + "eslint-plugin-node": "^11.1.0", + "mocha": "^10.0.0", + "npm-run-all": "^4.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + }, + "license": "ISC" }