forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1/* oxlint-disable no-console */
2import { readFile, writeFile } from 'node:fs/promises'
3import { join } from 'node:path'
4import { fileURLToPath } from 'node:url'
5import { colors } from './utils/colors.ts'
6
7const I18N_DIRECTORY = fileURLToPath(new URL('../i18n', import.meta.url))
8const LOCALES_DIRECTORY = join(I18N_DIRECTORY, 'locales')
9const REFERENCE_FILE_NAME = 'en.json'
10const SCHEMA_FILE_NAME = 'schema.json'
11
12type Json = Record<string, unknown>
13type LocaleJson = Json & { $schema: string }
14
15interface JsonSchema {
16 $schema?: string
17 title?: string
18 description?: string
19 type: string
20 properties?: Record<string, JsonSchema>
21 additionalProperties?: boolean
22}
23
24const generateSubSchema = (obj: Json): JsonSchema => {
25 const properties: Record<string, JsonSchema> = {}
26
27 for (const [key, value] of Object.entries(obj)) {
28 if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
29 properties[key] = generateSubSchema(value as Json)
30 } else {
31 properties[key] = { type: 'string' }
32 }
33 }
34
35 return {
36 type: 'object',
37 properties,
38 additionalProperties: false,
39 }
40}
41
42const generateSchema = (obj: LocaleJson): JsonSchema => {
43 const { $schema: _, ...rest } = obj // Exclude $schema from schema generation
44 const baseSchema = generateSubSchema(rest)
45 return {
46 $schema: 'http://json-schema.org/draft-07/schema#',
47 title: 'npmx i18n locale file',
48 description:
49 'Schema for npmx i18n translation files. Generated from en.json — do not edit manually.',
50 ...baseSchema,
51 // Allow $schema property at root level
52 properties: {
53 ...baseSchema.properties,
54 $schema: { type: 'string' },
55 },
56 }
57}
58
59/*
60 * Generates a JSON Schema from the reference locale file (en.json) and writes it to
61 * i18n/schema.json. All locale files include a $schema property that points to this file.
62 *
63 * This allows IDEs to provide validation, autocompletion, and other hints in translation files.
64 */
65const main = async (): Promise<void> => {
66 const referenceFilePath = join(LOCALES_DIRECTORY, REFERENCE_FILE_NAME)
67 const referenceContent = JSON.parse(await readFile(referenceFilePath, 'utf-8')) as LocaleJson
68
69 const schema = generateSchema(referenceContent)
70
71 const schemaFilePath = join(I18N_DIRECTORY, SCHEMA_FILE_NAME)
72 await writeFile(schemaFilePath, JSON.stringify(schema, null, 2) + '\n', 'utf-8')
73
74 console.log(colors.green(`✅ Generated schema at ${schemaFilePath}`))
75}
76
77await main()