openpencil/scripts/check-locales.ts
Danila Poyarkov 1ed7de6056 chore(lint): reject broad unknown type assertions
- Add AST-based lint checks for broad unknown object assertions and local JsonObject aliases
- Centralize JsonObject in core and package-local MCP RPC JSON typing
- Replace baseline Record<string, unknown> assertions with named shared/domain types
2026-05-25 00:26:02 +03:00

51 lines
1.5 KiB
TypeScript

#!/usr/bin/env bun
import { readFileSync, readdirSync } from 'node:fs'
import { join } from 'node:path'
import type { JsonObject } from '@open-pencil/core/types'
const MESSAGES_PATH = 'packages/vue/src/i18n/messages.ts'
const LOCALES_DIR = 'packages/vue/src/locales'
const content = readFileSync(MESSAGES_PATH, 'utf-8')
const blocks = [...content.matchAll(/i18n\('(\w+)',\s*\{([\s\S]*?)\n\}\)/g)]
const enKeys = new Map<string, Set<string>>()
for (const [, ns, body] of blocks) {
const keys = [...body.matchAll(/^\s+(\w+):/gm)].map((m) => m[1])
enKeys.set(ns, new Set(keys))
}
const localeFiles = readdirSync(LOCALES_DIR).filter((f) => f.endsWith('.json'))
let hasErrors = false
for (const file of localeFiles) {
const data = JSON.parse(readFileSync(join(LOCALES_DIR, file), 'utf-8'))
const missing: string[] = []
const extra: string[] = []
for (const [ns, keys] of enKeys) {
const localeNs = (data[ns] ?? {}) as JsonObject
for (const key of keys) {
if (!(key in localeNs)) missing.push(`${ns}.${key}`)
}
for (const key of Object.keys(localeNs)) {
if (!keys.has(key)) extra.push(`${ns}.${key}`)
}
}
if (missing.length > 0 || extra.length > 0) {
hasErrors = true
console.error(`\n${file}:`)
for (const m of missing) console.error(` missing: ${m}`)
for (const e of extra) console.error(` extra: ${e}`)
}
}
if (hasErrors) {
console.error('\nLocale files are out of sync with messages.ts')
process.exit(1)
} else {
console.log('All locale files are in sync.')
}