#!/usr/bin/env node const fs = require('fs') const path = require('path') const { camelCase } = require('lodash') const ts = require('typescript') // Import the NAMESPACES array from i18next-config.ts function getNamespacesFromConfig() { const configPath = path.join(__dirname, 'i18next-config.ts') const configContent = fs.readFileSync(configPath, 'utf8') const sourceFile = ts.createSourceFile(configPath, configContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS) const namespaces = [] const visit = (node) => { if ( ts.isVariableDeclaration(node) && node.name.getText() === 'NAMESPACES' && node.initializer && ts.isArrayLiteralExpression(node.initializer) ) { node.initializer.elements.forEach((el) => { if (ts.isStringLiteral(el)) namespaces.push(el.text) }) } ts.forEachChild(node, visit) } visit(sourceFile) if (!namespaces.length) throw new Error('Could not find NAMESPACES array in i18next-config.ts') return namespaces } function getNamespacesFromTypes() { const typesPath = path.join(__dirname, '../types/i18n.d.ts') if (!fs.existsSync(typesPath)) { return null } const typesContent = fs.readFileSync(typesPath, 'utf8') // Extract namespaces from Messages type const messagesMatch = typesContent.match(/export type Messages = \{([\s\S]*?)\}/) if (!messagesMatch) { return null } // Parse the properties const propertiesStr = messagesMatch[1] const properties = propertiesStr .split('\n') .map(line => line.trim()) .filter(line => line.includes(':')) .map(line => line.split(':')[0].trim()) .filter(prop => prop.length > 0) return properties } function main() { try { console.log('šŸ” Checking i18n types synchronization...') // Get namespaces from config const configNamespaces = getNamespacesFromConfig() console.log(`šŸ“¦ Found ${configNamespaces.length} namespaces in config`) // Convert to camelCase for comparison const configCamelCase = configNamespaces.map(ns => camelCase(ns)).sort() // Get namespaces from type definitions const typeNamespaces = getNamespacesFromTypes() if (!typeNamespaces) { console.error('āŒ Type definitions file not found or invalid') console.error(' Run: pnpm run gen:i18n-types') process.exit(1) } console.log(`šŸ”§ Found ${typeNamespaces.length} namespaces in types`) const typeCamelCase = typeNamespaces.sort() // Compare arrays const configSet = new Set(configCamelCase) const typeSet = new Set(typeCamelCase) // Find missing in types const missingInTypes = configCamelCase.filter(ns => !typeSet.has(ns)) // Find extra in types const extraInTypes = typeCamelCase.filter(ns => !configSet.has(ns)) let hasErrors = false if (missingInTypes.length > 0) { hasErrors = true console.error('āŒ Missing in type definitions:') missingInTypes.forEach(ns => console.error(` - ${ns}`)) } if (extraInTypes.length > 0) { hasErrors = true console.error('āŒ Extra in type definitions:') extraInTypes.forEach(ns => console.error(` - ${ns}`)) } if (hasErrors) { console.error('\nšŸ’” To fix synchronization issues:') console.error(' Run: pnpm run gen:i18n-types') process.exit(1) } console.log('āœ… i18n types are synchronized') } catch (error) { console.error('āŒ Error:', error.message) process.exit(1) } } if (require.main === module) { main() }