@intlayer/engine 9.3.2 → 9.4.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/build.cjs +1 -0
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +3 -3
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
- package/dist/cjs/createType/createModuleAugmentation.cjs +20 -1
- package/dist/cjs/createType/createModuleAugmentation.cjs.map +1 -1
- package/dist/cjs/createType/index.cjs +1 -0
- package/dist/esm/build.mjs +2 -2
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +4 -4
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
- package/dist/esm/createType/createModuleAugmentation.mjs +20 -2
- package/dist/esm/createType/createModuleAugmentation.mjs.map +1 -1
- package/dist/esm/createType/index.mjs +2 -2
- package/dist/types/build.d.ts +2 -2
- package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts.map +1 -1
- package/dist/types/createType/createModuleAugmentation.d.ts +12 -2
- package/dist/types/createType/createModuleAugmentation.d.ts.map +1 -1
- package/dist/types/createType/index.d.ts +2 -2
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -327,4 +327,4 @@ Contribute on [GitHub](https://github.com/aymericzip/intlayer), [GitLab](https:/
|
|
|
327
327
|
|
|
328
328
|
If you like Intlayer, give us a ⭐ on GitHub. It helps others discover the project! [See why GitHub Stars matter](https://github.com/aymericzip/intlayer/blob/main/CONTRIBUTING.md#why-github-stars-matter-).
|
|
329
329
|
|
|
330
|
-
[](https://star-history.dera.page/#aymericzip/intlayer&Date)
|
package/dist/cjs/build.cjs
CHANGED
|
@@ -37,6 +37,7 @@ exports.formatDictionary = require_formatDictionary.formatDictionary;
|
|
|
37
37
|
exports.formatDictionaryOutput = require_formatDictionary.formatDictionaryOutput;
|
|
38
38
|
exports.formatDistantDictionaries = require_loadDictionaries_loadRemoteDictionaries.formatDistantDictionaries;
|
|
39
39
|
exports.formatLocalDictionaries = require_loadDictionaries_loadContentDeclaration.formatLocalDictionaries;
|
|
40
|
+
exports.formatRewriteRules = require_createType_createModuleAugmentation.formatRewriteRules;
|
|
40
41
|
exports.generateDictionaryListContent = require_createDictionaryEntryPoint_generateDictionaryListContent.generateDictionaryListContent;
|
|
41
42
|
exports.generateTypeScriptType = require_createType_createType.generateTypeScriptType;
|
|
42
43
|
exports.getBuiltDictionariesPath = require_createDictionaryEntryPoint_getBuiltDictionariesPath.getBuiltDictionariesPath;
|
|
@@ -11,7 +11,7 @@ let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryMan
|
|
|
11
11
|
let _intlayer_core_plugins = require("@intlayer/core/plugins");
|
|
12
12
|
|
|
13
13
|
//#region src/buildIntlayerDictionary/writeDynamicDictionary.ts
|
|
14
|
-
const DICTIONARIES_SUBDIR =
|
|
14
|
+
const DICTIONARIES_SUBDIR = _intlayer_config_defaultValues.DYNAMIC_DICTIONARIES_JSON_SUBDIR;
|
|
15
15
|
/**
|
|
16
16
|
* Escapes a value interpolated into a single-quoted literal of a generated
|
|
17
17
|
* loader module, so keys or segments containing `'` or `\` cannot break the
|
|
@@ -48,7 +48,7 @@ const generateDictionaryEntryPoint = (key, locales, format = "esm", nestedKeys =
|
|
|
48
48
|
if (sortedNestedKeys.length === 0) return ` '${safeLocale}': () => ${ownLoader}`;
|
|
49
49
|
return ` '${safeLocale}': () => Promise.all([\n ${ownLoader},\n ${sortedNestedKeys.map((nestedKey) => buildPerLocaleLoader(escapeJsLiteral(nestedKey), safeLocale, format)).join(",\n ")}\n ]).then(([_dictionary, ..._nested]) => ({\n ..._dictionary,\n nestedDictionaries: { ${sortedNestedKeys.map((nestedKey, index) => `'${escapeJsLiteral(nestedKey)}': _nested[${index}]`).join(", ")} }\n }))`;
|
|
50
50
|
}).join(",\n");
|
|
51
|
-
if (format === "esm") return `const
|
|
51
|
+
if (format === "esm") return `const ${_intlayer_config_defaultValues.DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\n${localeEntries}\n};\n\nexport default ${_intlayer_config_defaultValues.DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\n`;
|
|
52
52
|
return `module.exports = {\n${localeEntries}\n};\n`;
|
|
53
53
|
};
|
|
54
54
|
const buildLoaderExpression = (key, chunkSegments, locale, format) => {
|
|
@@ -96,7 +96,7 @@ const generateQualifiedDictionaryEntryPoint = (key, qualifierTypes, entries, loc
|
|
|
96
96
|
return ` '${escapeJsLiteral(locale)}': ${serializeLoaderTree(tree, 1)}`;
|
|
97
97
|
}).join(",\n");
|
|
98
98
|
const marker = ` '${_intlayer_core_dictionaryManipulator.QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;
|
|
99
|
-
if (format === "esm") return `const
|
|
99
|
+
if (format === "esm") return `const ${_intlayer_config_defaultValues.DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\n${marker},\n${localeEntries}\n};\n\nexport default ${_intlayer_config_defaultValues.DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\n`;
|
|
100
100
|
return `module.exports = {\n${marker},\n${localeEntries}\n};\n`;
|
|
101
101
|
};
|
|
102
102
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeDynamicDictionary.cjs","names":["QUALIFIER_DYNAMIC_TYPES_KEY","OUTPUT_FORMAT","resolve","mkdir","parallelize","getPerLocaleDictionary","writeJsonIfChanged","writeFileIfChanged","colorizePath","COMPOSITE_ID_SEPARATOR","reconstructQualifiedEntry"],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n COMPOSITE_ID_SEPARATOR,\n QUALIFIER_DYNAMIC_TYPES_KEY,\n reconstructQualifiedEntry,\n} from '@intlayer/core/dictionaryManipulator';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { PlainMergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = 'json'; // Necessary to add a static first dir for Turbopack\n\n/**\n * Escapes a value interpolated into a single-quoted literal of a generated\n * loader module, so keys or segments containing `'` or `\\` cannot break the\n * emitted JavaScript.\n */\nconst escapeJsLiteral = (value: string): string =>\n value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\n/** Loader expression for one `(key, locale)` per-locale chunk. */\nconst buildPerLocaleLoader = (\n key: string,\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = `./${DICTIONARIES_SUBDIR}/${key}/${locale}.json`;\n\n return format === 'esm'\n ? `import('${path}').then(m => m.default)`\n : `Promise.resolve(require('${path}'))`;\n};\n\n/**\n * Generates the content of a dictionary entry point file.\n *\n * When the dictionary references other dictionaries through `nest()`, the\n * loader for a locale resolves the referenced dictionaries alongside it and\n * attaches them as `nestedDictionaries`. The nest targets then travel in the\n * same lazy chunk as their consumer, and `getNesting` resolves them from that\n * local reference instead of the global registry — which the build\n * optimization strips.\n *\n * @param key - The dictionary key.\n * @param locales - Locales to emit a loader for.\n * @param format - Output module format.\n * @param nestedKeys - Keys referenced through `nest()`, transitively.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm',\n nestedKeys: string[] = []\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n const sortedNestedKeys = [...nestedKeys].sort((a, b) => a.localeCompare(b));\n\n const safeKey = escapeJsLiteral(key);\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const safeLocale = escapeJsLiteral(locale);\n const ownLoader = buildPerLocaleLoader(safeKey, safeLocale, format);\n\n if (sortedNestedKeys.length === 0) {\n return ` '${safeLocale}': () => ${ownLoader}`;\n }\n\n const nestedLoaders = sortedNestedKeys\n .map((nestedKey) =>\n buildPerLocaleLoader(escapeJsLiteral(nestedKey), safeLocale, format)\n )\n .join(',\\n ');\n\n const attachedEntries = sortedNestedKeys\n .map(\n (nestedKey, index) =>\n `'${escapeJsLiteral(nestedKey)}': _nested[${index}]`\n )\n .join(', ');\n\n return (\n ` '${safeLocale}': () => Promise.all([\\n` +\n ` ${ownLoader},\\n` +\n ` ${nestedLoaders}\\n` +\n ` ]).then(([_dictionary, ..._nested]) => ({\\n` +\n ` ..._dictionary,\\n` +\n ` nestedDictionaries: { ${attachedEntries} }\\n` +\n ` }))`\n );\n })\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * A nested loader tree: one level per declared dimension, leaves are loader\n * expression strings.\n */\ntype LoaderTree = { [segment: string]: LoaderTree | string };\n\n/**\n * One entry of a qualified loader map.\n *\n * `treeSegments` is where the entry lives in the loader tree (its composite id\n * segments); `chunkSegments` is the chunk path it loads. They differ when the\n * entry aliases another entry with identical content (e.g. array-variant\n * fan-out): the alias keeps its own tree position but points at the canonical\n * entry's chunk, so equal content is emitted — and downloaded — only once.\n */\nexport type QualifiedEntrySegments = {\n treeSegments: string[];\n chunkSegments: string[];\n};\n\nconst buildLoaderExpression = (\n key: string,\n chunkSegments: string[],\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = escapeJsLiteral(\n `./${DICTIONARIES_SUBDIR}/${key}/${chunkSegments.join('/')}/${locale}.json`\n );\n\n return format === 'esm'\n ? `() => import('${path}').then(m => m.default)`\n : `() => Promise.resolve(require('${path}'))`;\n};\n\nconst buildLoaderTree = (\n key: string,\n entries: QualifiedEntrySegments[],\n locale: string,\n format: 'cjs' | 'esm'\n): LoaderTree => {\n const root: LoaderTree = {};\n\n for (const { treeSegments, chunkSegments } of entries) {\n let node = root;\n\n treeSegments.forEach((segment, index) => {\n if (index === treeSegments.length - 1) {\n node[segment] = buildLoaderExpression(\n key,\n chunkSegments,\n locale,\n format\n );\n return;\n }\n\n node[segment] = (node[segment] as LoaderTree | undefined) ?? {};\n node = node[segment] as LoaderTree;\n });\n }\n\n return root;\n};\n\nconst serializeLoaderTree = (tree: LoaderTree, indentLevel: number): string => {\n const pad = ' '.repeat(indentLevel);\n const innerPad = ' '.repeat(indentLevel + 1);\n\n const lines = Object.keys(tree)\n .sort((a, b) => a.localeCompare(b))\n .map((segment) => {\n const value = tree[segment]!;\n const serialized =\n typeof value === 'string'\n ? value\n : serializeLoaderTree(value, indentLevel + 1);\n\n return `${innerPad}'${escapeJsLiteral(segment)}': ${serialized}`;\n });\n\n return `{\\n${lines.join(',\\n')}\\n${pad}}`;\n};\n\n/**\n * Generates the entry point of a qualified dictionary (collection / variant,\n * possibly combined). Under each locale the loader map nests one\n * level per declared dimension (canonical order) and carries a marker listing\n * those dimensions so the runtime can walk the tree.\n *\n * One static `import()` is emitted per leaf `(locale, …segments)` chunk, which\n * keeps the output compatible with bundlers that reject template-literal\n * dynamic imports (Turbopack). Entries whose content is identical share one\n * chunk: their leaves point at the canonical entry's import path.\n */\nexport const generateQualifiedDictionaryEntryPoint = (\n key: string,\n qualifierTypes: DictionaryQualifierType[],\n entries: QualifiedEntrySegments[],\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const tree = buildLoaderTree(key, entries, locale, format);\n return ` '${escapeJsLiteral(locale)}': ${serializeLoaderTree(tree, 1)}`;\n })\n .join(',\\n');\n\n const marker = ` '${QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;\n\n if (format === 'esm') {\n return (\n `const content = {\\n${marker},\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${marker},\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: PlainMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT,\n nestedDictionaryGraph: Map<string, Set<string>> = new Map()\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format, [\n ...(nestedDictionaryGraph.get(key) ?? []),\n ]);\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n\n return resultDictionariesPaths;\n};\n\nexport type QualifiedMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: QualifiedDictionaryGroup;\n};\n\nexport type QualifiedMergedDictionaryOutput = Record<\n string,\n QualifiedMergedDictionaryResult\n>;\n\n/**\n * Writes the dynamic chunks and entry points of qualified dictionaries\n * (collections, variants — possibly combined) in\n * `importMode: 'dynamic'`.\n *\n * Each entry is reduced to one per-locale chunk written to a path nested by\n * dimension — `json/{key}/{seg1}/{seg2}/{locale}.json` — and a single\n * `{key}.{ext}` entry point exposes the matching nested loader tree, so the\n * entry point is discovered and aggregated exactly like a plain dynamic one.\n */\nexport const writeDynamicQualifiedDictionaries = async (\n qualifiedDictionaries: QualifiedMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<void> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n await parallelize(\n Object.entries(qualifiedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, { dictionary: group }]) => {\n if (key === 'undefined') return;\n\n const entryIds = Object.keys(group.content);\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n\n // Entries with identical content (e.g. array-variant fan-out) share one\n // chunk: only the first entry per content identity writes files; the\n // others become aliases whose loaders point at the canonical chunk path.\n const canonicalSegmentsByContent = new Map<string, string[]>();\n const entries: QualifiedEntrySegments[] = [];\n const canonicalEntryIds: string[] = [];\n\n for (const entryId of entryIds) {\n const treeSegments = entryId.split(COMPOSITE_ID_SEPARATOR);\n const contentIdentity = JSON.stringify(group.content[entryId]);\n const canonicalSegments =\n canonicalSegmentsByContent.get(contentIdentity);\n\n if (canonicalSegments) {\n entries.push({ treeSegments, chunkSegments: canonicalSegments });\n continue;\n }\n\n canonicalSegmentsByContent.set(contentIdentity, treeSegments);\n entries.push({ treeSegments, chunkSegments: treeSegments });\n canonicalEntryIds.push(entryId);\n }\n\n await parallelize(canonicalEntryIds, async (entryId) => {\n // Rebuild a resolvable dictionary from the content node + composite id\n // so per-locale extraction sees the same `{ key, content }` shape.\n const entry = reconstructQualifiedEntry(group, entryId);\n\n const segments = entryId.split(COMPOSITE_ID_SEPARATOR);\n\n const entryDir = resolve(keyDir, ...segments);\n assertPathWithin(entryDir, keyDir);\n await mkdir(entryDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n entry,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/<…segments>/locale.json\n const resultFilePath = resolve(entryDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${segments.join('/')}/${locale}.json:`,\n err\n );\n }\n );\n });\n });\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateQualifiedDictionaryEntryPoint(\n key,\n group.qualifierTypes,\n entries,\n locales,\n format\n );\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"writeDynamicDictionary.cjs","names":["DYNAMIC_DICTIONARIES_JSON_SUBDIR","DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER","QUALIFIER_DYNAMIC_TYPES_KEY","OUTPUT_FORMAT","resolve","mkdir","parallelize","getPerLocaleDictionary","writeJsonIfChanged","writeFileIfChanged","colorizePath","COMPOSITE_ID_SEPARATOR","reconstructQualifiedEntry"],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport {\n DYNAMIC_DICTIONARIES_JSON_SUBDIR,\n DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER,\n OUTPUT_FORMAT,\n} from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n COMPOSITE_ID_SEPARATOR,\n QUALIFIER_DYNAMIC_TYPES_KEY,\n reconstructQualifiedEntry,\n} from '@intlayer/core/dictionaryManipulator';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { PlainMergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = DYNAMIC_DICTIONARIES_JSON_SUBDIR;\n\n/**\n * Escapes a value interpolated into a single-quoted literal of a generated\n * loader module, so keys or segments containing `'` or `\\` cannot break the\n * emitted JavaScript.\n */\nconst escapeJsLiteral = (value: string): string =>\n value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\n/** Loader expression for one `(key, locale)` per-locale chunk. */\nconst buildPerLocaleLoader = (\n key: string,\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = `./${DICTIONARIES_SUBDIR}/${key}/${locale}.json`;\n\n return format === 'esm'\n ? `import('${path}').then(m => m.default)`\n : `Promise.resolve(require('${path}'))`;\n};\n\n/**\n * Generates the content of a dictionary entry point file.\n *\n * When the dictionary references other dictionaries through `nest()`, the\n * loader for a locale resolves the referenced dictionaries alongside it and\n * attaches them as `nestedDictionaries`. The nest targets then travel in the\n * same lazy chunk as their consumer, and `getNesting` resolves them from that\n * local reference instead of the global registry — which the build\n * optimization strips.\n *\n * @param key - The dictionary key.\n * @param locales - Locales to emit a loader for.\n * @param format - Output module format.\n * @param nestedKeys - Keys referenced through `nest()`, transitively.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm',\n nestedKeys: string[] = []\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n const sortedNestedKeys = [...nestedKeys].sort((a, b) => a.localeCompare(b));\n\n const safeKey = escapeJsLiteral(key);\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const safeLocale = escapeJsLiteral(locale);\n const ownLoader = buildPerLocaleLoader(safeKey, safeLocale, format);\n\n if (sortedNestedKeys.length === 0) {\n return ` '${safeLocale}': () => ${ownLoader}`;\n }\n\n const nestedLoaders = sortedNestedKeys\n .map((nestedKey) =>\n buildPerLocaleLoader(escapeJsLiteral(nestedKey), safeLocale, format)\n )\n .join(',\\n ');\n\n const attachedEntries = sortedNestedKeys\n .map(\n (nestedKey, index) =>\n `'${escapeJsLiteral(nestedKey)}': _nested[${index}]`\n )\n .join(', ');\n\n return (\n ` '${safeLocale}': () => Promise.all([\\n` +\n ` ${ownLoader},\\n` +\n ` ${nestedLoaders}\\n` +\n ` ]).then(([_dictionary, ..._nested]) => ({\\n` +\n ` ..._dictionary,\\n` +\n ` nestedDictionaries: { ${attachedEntries} }\\n` +\n ` }))`\n );\n })\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\\n${localeEntries}\\n};\\n\\n` +\n `export default ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * A nested loader tree: one level per declared dimension, leaves are loader\n * expression strings.\n */\ntype LoaderTree = { [segment: string]: LoaderTree | string };\n\n/**\n * One entry of a qualified loader map.\n *\n * `treeSegments` is where the entry lives in the loader tree (its composite id\n * segments); `chunkSegments` is the chunk path it loads. They differ when the\n * entry aliases another entry with identical content (e.g. array-variant\n * fan-out): the alias keeps its own tree position but points at the canonical\n * entry's chunk, so equal content is emitted — and downloaded — only once.\n */\nexport type QualifiedEntrySegments = {\n treeSegments: string[];\n chunkSegments: string[];\n};\n\nconst buildLoaderExpression = (\n key: string,\n chunkSegments: string[],\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = escapeJsLiteral(\n `./${DICTIONARIES_SUBDIR}/${key}/${chunkSegments.join('/')}/${locale}.json`\n );\n\n return format === 'esm'\n ? `() => import('${path}').then(m => m.default)`\n : `() => Promise.resolve(require('${path}'))`;\n};\n\nconst buildLoaderTree = (\n key: string,\n entries: QualifiedEntrySegments[],\n locale: string,\n format: 'cjs' | 'esm'\n): LoaderTree => {\n const root: LoaderTree = {};\n\n for (const { treeSegments, chunkSegments } of entries) {\n let node = root;\n\n treeSegments.forEach((segment, index) => {\n if (index === treeSegments.length - 1) {\n node[segment] = buildLoaderExpression(\n key,\n chunkSegments,\n locale,\n format\n );\n return;\n }\n\n node[segment] = (node[segment] as LoaderTree | undefined) ?? {};\n node = node[segment] as LoaderTree;\n });\n }\n\n return root;\n};\n\nconst serializeLoaderTree = (tree: LoaderTree, indentLevel: number): string => {\n const pad = ' '.repeat(indentLevel);\n const innerPad = ' '.repeat(indentLevel + 1);\n\n const lines = Object.keys(tree)\n .sort((a, b) => a.localeCompare(b))\n .map((segment) => {\n const value = tree[segment]!;\n const serialized =\n typeof value === 'string'\n ? value\n : serializeLoaderTree(value, indentLevel + 1);\n\n return `${innerPad}'${escapeJsLiteral(segment)}': ${serialized}`;\n });\n\n return `{\\n${lines.join(',\\n')}\\n${pad}}`;\n};\n\n/**\n * Generates the entry point of a qualified dictionary (collection / variant,\n * possibly combined). Under each locale the loader map nests one\n * level per declared dimension (canonical order) and carries a marker listing\n * those dimensions so the runtime can walk the tree.\n *\n * One static `import()` is emitted per leaf `(locale, …segments)` chunk, which\n * keeps the output compatible with bundlers that reject template-literal\n * dynamic imports (Turbopack). Entries whose content is identical share one\n * chunk: their leaves point at the canonical entry's import path.\n */\nexport const generateQualifiedDictionaryEntryPoint = (\n key: string,\n qualifierTypes: DictionaryQualifierType[],\n entries: QualifiedEntrySegments[],\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const tree = buildLoaderTree(key, entries, locale, format);\n return ` '${escapeJsLiteral(locale)}': ${serializeLoaderTree(tree, 1)}`;\n })\n .join(',\\n');\n\n const marker = ` '${QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;\n\n if (format === 'esm') {\n return (\n `const ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\\n${marker},\\n${localeEntries}\\n};\\n\\n` +\n `export default ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\\n`\n );\n }\n return `module.exports = {\\n${marker},\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: PlainMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT,\n nestedDictionaryGraph: Map<string, Set<string>> = new Map()\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format, [\n ...(nestedDictionaryGraph.get(key) ?? []),\n ]);\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n\n return resultDictionariesPaths;\n};\n\nexport type QualifiedMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: QualifiedDictionaryGroup;\n};\n\nexport type QualifiedMergedDictionaryOutput = Record<\n string,\n QualifiedMergedDictionaryResult\n>;\n\n/**\n * Writes the dynamic chunks and entry points of qualified dictionaries\n * (collections, variants — possibly combined) in\n * `importMode: 'dynamic'`.\n *\n * Each entry is reduced to one per-locale chunk written to a path nested by\n * dimension — `json/{key}/{seg1}/{seg2}/{locale}.json` — and a single\n * `{key}.{ext}` entry point exposes the matching nested loader tree, so the\n * entry point is discovered and aggregated exactly like a plain dynamic one.\n */\nexport const writeDynamicQualifiedDictionaries = async (\n qualifiedDictionaries: QualifiedMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<void> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n await parallelize(\n Object.entries(qualifiedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, { dictionary: group }]) => {\n if (key === 'undefined') return;\n\n const entryIds = Object.keys(group.content);\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n\n // Entries with identical content (e.g. array-variant fan-out) share one\n // chunk: only the first entry per content identity writes files; the\n // others become aliases whose loaders point at the canonical chunk path.\n const canonicalSegmentsByContent = new Map<string, string[]>();\n const entries: QualifiedEntrySegments[] = [];\n const canonicalEntryIds: string[] = [];\n\n for (const entryId of entryIds) {\n const treeSegments = entryId.split(COMPOSITE_ID_SEPARATOR);\n const contentIdentity = JSON.stringify(group.content[entryId]);\n const canonicalSegments =\n canonicalSegmentsByContent.get(contentIdentity);\n\n if (canonicalSegments) {\n entries.push({ treeSegments, chunkSegments: canonicalSegments });\n continue;\n }\n\n canonicalSegmentsByContent.set(contentIdentity, treeSegments);\n entries.push({ treeSegments, chunkSegments: treeSegments });\n canonicalEntryIds.push(entryId);\n }\n\n await parallelize(canonicalEntryIds, async (entryId) => {\n // Rebuild a resolvable dictionary from the content node + composite id\n // so per-locale extraction sees the same `{ key, content }` shape.\n const entry = reconstructQualifiedEntry(group, entryId);\n\n const segments = entryId.split(COMPOSITE_ID_SEPARATOR);\n\n const entryDir = resolve(keyDir, ...segments);\n assertPathWithin(entryDir, keyDir);\n await mkdir(entryDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n entry,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/<…segments>/locale.json\n const resultFilePath = resolve(entryDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${segments.join('/')}/${locale}.json:`,\n err\n );\n }\n );\n });\n });\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateQualifiedDictionaryEntryPoint(\n key,\n group.qualifierTypes,\n entries,\n locales,\n format\n );\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n};\n"],"mappings":"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"}
|
|
@@ -7,11 +7,28 @@ let node_path = require("node:path");
|
|
|
7
7
|
let fast_glob = require("fast-glob");
|
|
8
8
|
fast_glob = require_runtime.__toESM(fast_glob);
|
|
9
9
|
let _intlayer_config_utils = require("@intlayer/config/utils");
|
|
10
|
+
let _intlayer_core_localization = require("@intlayer/core/localization");
|
|
10
11
|
|
|
11
12
|
//#region src/createType/createModuleAugmentation.ts
|
|
12
13
|
const getTypeName = (key) => `${(0, _intlayer_config_utils.kebabCaseToCamelCase)(key)}Content`;
|
|
13
14
|
/** Returns lines like: [Locales.FRENCH]: 1; */
|
|
14
15
|
const formatLocales = (locales) => locales.map((locale) => ` "${locale}": 1;`).join("\n");
|
|
16
|
+
/**
|
|
17
|
+
* Serializes the `url` rewrite rules as a type literal, so `getLocalizedPath`
|
|
18
|
+
* and `getLocalizedUrl` can resolve a canonical path to its localized literal at
|
|
19
|
+
* the type level. Returns an empty string when the project declares no rewrite —
|
|
20
|
+
* the consuming types then localize every path to itself.
|
|
21
|
+
*
|
|
22
|
+
* Rules are emitted in their normalized form (`:param`, no locale prefix), which
|
|
23
|
+
* is exactly what the runtime matches against.
|
|
24
|
+
*/
|
|
25
|
+
const formatRewriteRules = (rewrite) => {
|
|
26
|
+
const rewriteRules = (0, _intlayer_core_localization.getRewriteRules)(rewrite, "url");
|
|
27
|
+
if (!rewriteRules?.rules?.length) return "";
|
|
28
|
+
return `; rewrite: { ${rewriteRules.rules.map(({ canonical, localized }) => {
|
|
29
|
+
return `'${canonical}': { ${Object.entries(localized).filter(([, pattern]) => typeof pattern === "string").map(([locale, pattern]) => `'${locale}': '${pattern}'`).join("; ")} }`;
|
|
30
|
+
}).join("; ")} }`;
|
|
31
|
+
};
|
|
15
32
|
const zodToTsString = (schema) => {
|
|
16
33
|
if (!schema) return "any";
|
|
17
34
|
const def = schema._def ?? schema.def ?? schema;
|
|
@@ -98,7 +115,8 @@ const generateTypeIndexContent = (typeFiles, configuration, zodToTsFns) => {
|
|
|
98
115
|
fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\n\n`;
|
|
99
116
|
fileContent += ` interface __EditorRegistry { enabled : ${enabled} }\n\n`;
|
|
100
117
|
const routingMode = routing?.mode ?? "prefix-no-default";
|
|
101
|
-
|
|
118
|
+
const formattedRewriteRules = formatRewriteRules(routing?.rewrite);
|
|
119
|
+
fileContent += ` interface __RoutingRegistry { mode: '${routingMode}'; defaultLocale: '${defaultLocale}'${formattedRewriteRules} }\n`;
|
|
102
120
|
fileContent += `}\n`;
|
|
103
121
|
return fileContent;
|
|
104
122
|
};
|
|
@@ -123,5 +141,6 @@ const createModuleAugmentation = async (configuration) => {
|
|
|
123
141
|
|
|
124
142
|
//#endregion
|
|
125
143
|
exports.createModuleAugmentation = createModuleAugmentation;
|
|
144
|
+
exports.formatRewriteRules = formatRewriteRules;
|
|
126
145
|
exports.getTypeName = getTypeName;
|
|
127
146
|
//# sourceMappingURL=createModuleAugmentation.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createModuleAugmentation.cjs","names":["kebabCaseToCamelCase","normalizePath","relative","basename","extname","getPathHash","mkdir","fg","join","writeFileIfChanged"],"sources":["../../../src/createType/createModuleAugmentation.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { basename, extname, join, relative } from 'node:path';\nimport { kebabCaseToCamelCase, normalizePath } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { getPathHash } from '../utils';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\n\nexport const getTypeName = (key: string): string =>\n `${kebabCaseToCamelCase(key)}Content`;\n\n/** Returns lines like: [Locales.FRENCH]: 1; */\nconst formatLocales = (locales: Locale[]) =>\n locales.map((locale) => ` \"${locale}\": 1;`).join('\\n');\n\nconst zodToTsString = (schema: any): string => {\n if (!schema) return 'any';\n\n // Support both real Zod objects (_def) and serialized versions (def or nested)\n const def = schema._def ?? schema.def ?? schema;\n\n // Handle serialized type names (sometimes 'type' instead of 'typeName')\n const typeName = def.typeName ?? def.type;\n\n switch (typeName) {\n case 'ZodString':\n case 'string':\n return 'string';\n case 'ZodNumber':\n case 'number':\n return 'number';\n case 'ZodBoolean':\n case 'boolean':\n return 'boolean';\n case 'ZodNull':\n case 'null':\n return 'null';\n case 'ZodUndefined':\n case 'undefined':\n return 'undefined';\n case 'ZodArray':\n case 'array':\n return `${zodToTsString(def.type ?? def.element)}[]`;\n case 'ZodObject':\n case 'object': {\n const shape = typeof def.shape === 'function' ? def.shape() : def.shape;\n if (!shape) return 'Record<string, any>';\n\n const entries = Object.entries(shape)\n .map(([k, v]) => ` \"${k}\": ${zodToTsString(v)};`)\n .join('\\n');\n return `{\\n${entries}\\n }`;\n }\n case 'ZodOptional':\n case 'optional':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | undefined`;\n case 'ZodNullable':\n case 'nullable':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | null`;\n case 'ZodUnion':\n case 'union': {\n const options = def.options ?? [];\n return options.map(zodToTsString).join(' | ');\n }\n case 'ZodIntersection':\n case 'intersection':\n return `${zodToTsString(def.left)} & ${zodToTsString(def.right)}`;\n case 'ZodEnum':\n case 'enum': {\n const values = def.values ?? [];\n return values.map((v: string) => `\"${v}\"`).join(' | ');\n }\n case 'ZodLiteral':\n case 'literal': {\n const value = def.value;\n return typeof value === 'string' ? `\"${value}\"` : String(value);\n }\n default:\n return 'any';\n }\n};\n\ntype ZodToTsFns = {\n zodToTs: (schema: any, opts?: any) => { node: any };\n printNode: (node: any) => string;\n createAuxiliaryTypeStore: () => any;\n};\n\n/** Generate the content of the module augmentation file */\nconst generateTypeIndexContent = (\n typeFiles: string[],\n configuration: IntlayerConfig,\n zodToTsFns: ZodToTsFns | null\n): string => {\n const { internationalization, system, editor, routing } = configuration;\n const { moduleAugmentationDir } = system;\n const { enabled } = editor;\n const { locales, requiredLocales, strictMode, defaultLocale } =\n internationalization;\n\n let fileContent = 'import \"intlayer\";\\n';\n\n // Build dictionary refs\n const dictionariesRef = typeFiles.map((dictionaryPath) => ({\n relativePath: `./${normalizePath(relative(moduleAugmentationDir, dictionaryPath))}`,\n id: basename(dictionaryPath, extname(dictionaryPath)),\n hash: `_${getPathHash(dictionaryPath)}`,\n }));\n\n // Import all dictionaries\n for (const dictionary of dictionariesRef) {\n fileContent += `import ${dictionary.hash} from '${dictionary.relativePath}';\\n`;\n }\n fileContent += '\\n';\n\n // Dictionary map entries (id: typeof <hash>)\n const formattedDictionaryMap: string = dictionariesRef\n .map((dictionary) => ` \"${dictionary.id}\": typeof ${dictionary.hash};`)\n .join('\\n');\n\n // Ensure required ⊆ declared; if empty, default required = declared\n const declared = locales;\n const requiredSanitized = requiredLocales?.length\n ? requiredLocales.filter((requiredLocales) =>\n declared.includes(requiredLocales)\n )\n : declared;\n\n const formattedDeclaredLocales = formatLocales(declared);\n const formattedRequiredLocales = formatLocales(requiredSanitized);\n\n // Build schema registry\n const schemas = configuration.schemas ?? {};\n const formattedSchemas = Object.entries(schemas)\n .map(([key, schema]) => {\n let typeStr = 'any';\n\n if (schema) {\n try {\n if (zodToTsFns) {\n const { node } = zodToTsFns.zodToTs(schema, {\n auxiliaryTypeStore: zodToTsFns.createAuxiliaryTypeStore(),\n });\n // 133 is the kind for AnyKeyword in TypeScript\n if ((node as any).kind !== 133) {\n typeStr = zodToTsFns.printNode(node);\n } else {\n typeStr = zodToTsString(schema);\n }\n } else {\n typeStr = zodToTsString(schema);\n }\n } catch (_e) {\n // Fallback to custom string generator\n typeStr = zodToTsString(schema);\n }\n }\n return ` \"${key}\": ${typeStr};`;\n })\n .join('\\n');\n\n // Choose strict mode registry key\n const strictKey =\n strictMode === 'strict'\n ? 'strict'\n : strictMode === 'inclusive'\n ? 'inclusive'\n : 'loose';\n\n /**\n * Module augmentation that ONLY adds keys to registries.\n * No types/aliases redefined here—avoids merge conflicts.\n */\n fileContent += `declare module 'intlayer' {\\n`;\n // Dictionaries registry\n fileContent += ` interface __DictionaryRegistry {\\n${formattedDictionaryMap}\\n }\\n\\n`;\n // Locales registries\n fileContent += ` interface __DeclaredLocalesRegistry {\\n${formattedDeclaredLocales}\\n }\\n\\n`;\n fileContent += ` interface __RequiredLocalesRegistry {\\n${formattedRequiredLocales}\\n }\\n\\n`;\n // Schema registry\n fileContent += ` interface __SchemaRegistry {\\n${formattedSchemas}\\n }\\n\\n`;\n // Resolved strict mode (narrow the literal at build time)\n fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\\n\\n`;\n // Editor registry\n fileContent += ` interface __EditorRegistry { enabled : ${enabled} }\\n\\n`;\n // Routing registry (mode + defaultLocale narrowed to literals)\n const routingMode = routing?.mode ?? 'prefix-no-default';\n fileContent += ` interface __RoutingRegistry { mode: '${routingMode}'; defaultLocale: '${defaultLocale}' }\\n`;\n fileContent += `}\\n`;\n\n return fileContent;\n};\n\n/** Generate the index file merging all the types */\nexport const createModuleAugmentation = async (\n configuration: IntlayerConfig\n) => {\n const { moduleAugmentationDir, typesDir } = configuration.system;\n\n await mkdir(moduleAugmentationDir, { recursive: true });\n\n const dictionariesTypesDefinitions: string[] = await fg(\n normalizePath(`${typesDir}/*.ts`),\n { ignore: ['**/*.d.ts'] }\n );\n\n let zodToTsFns: ZodToTsFns | null = null;\n\n const hasSchemas = Object.keys(configuration.schemas ?? {}).length > 0;\n\n // zod-to-ts drags in the whole `typescript` package (~300ms import), so\n // only load it when there are schemas to convert.\n if (hasSchemas) {\n try {\n const mod = await import('zod-to-ts');\n zodToTsFns = {\n zodToTs: mod.zodToTs,\n printNode: mod.printNode,\n createAuxiliaryTypeStore: mod.createAuxiliaryTypeStore,\n };\n } catch {\n // typescript peer dep not installed (plain JS project), use fallback\n }\n }\n\n const tsContent = generateTypeIndexContent(\n dictionariesTypesDefinitions,\n configuration,\n zodToTsFns\n );\n\n const tsFilePath = join(moduleAugmentationDir, 'intlayer.d.ts');\n await writeFileIfChanged(tsFilePath, tsContent);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createModuleAugmentation.cjs","names":["kebabCaseToCamelCase","getRewriteRules","normalizePath","relative","basename","extname","getPathHash","mkdir","fg","join","writeFileIfChanged"],"sources":["../../../src/createType/createModuleAugmentation.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { basename, extname, join, relative } from 'node:path';\nimport { kebabCaseToCamelCase, normalizePath } from '@intlayer/config/utils';\nimport { getRewriteRules } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig, RoutingConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { getPathHash } from '../utils';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\n\nexport const getTypeName = (key: string): string =>\n `${kebabCaseToCamelCase(key)}Content`;\n\n/** Returns lines like: [Locales.FRENCH]: 1; */\nconst formatLocales = (locales: Locale[]) =>\n locales.map((locale) => ` \"${locale}\": 1;`).join('\\n');\n\n/**\n * Serializes the `url` rewrite rules as a type literal, so `getLocalizedPath`\n * and `getLocalizedUrl` can resolve a canonical path to its localized literal at\n * the type level. Returns an empty string when the project declares no rewrite —\n * the consuming types then localize every path to itself.\n *\n * Rules are emitted in their normalized form (`:param`, no locale prefix), which\n * is exactly what the runtime matches against.\n */\nexport const formatRewriteRules = (\n rewrite: RoutingConfig['rewrite']\n): string => {\n const rewriteRules = getRewriteRules(rewrite, 'url');\n\n if (!rewriteRules?.rules?.length) return '';\n\n const formattedRules = rewriteRules.rules\n .map(({ canonical, localized }) => {\n const formattedLocalized = Object.entries(localized)\n .filter(([, pattern]) => typeof pattern === 'string')\n .map(([locale, pattern]) => `'${locale}': '${pattern}'`)\n .join('; ');\n\n return `'${canonical}': { ${formattedLocalized} }`;\n })\n .join('; ');\n\n return `; rewrite: { ${formattedRules} }`;\n};\n\nconst zodToTsString = (schema: any): string => {\n if (!schema) return 'any';\n\n // Support both real Zod objects (_def) and serialized versions (def or nested)\n const def = schema._def ?? schema.def ?? schema;\n\n // Handle serialized type names (sometimes 'type' instead of 'typeName')\n const typeName = def.typeName ?? def.type;\n\n switch (typeName) {\n case 'ZodString':\n case 'string':\n return 'string';\n case 'ZodNumber':\n case 'number':\n return 'number';\n case 'ZodBoolean':\n case 'boolean':\n return 'boolean';\n case 'ZodNull':\n case 'null':\n return 'null';\n case 'ZodUndefined':\n case 'undefined':\n return 'undefined';\n case 'ZodArray':\n case 'array':\n return `${zodToTsString(def.type ?? def.element)}[]`;\n case 'ZodObject':\n case 'object': {\n const shape = typeof def.shape === 'function' ? def.shape() : def.shape;\n if (!shape) return 'Record<string, any>';\n\n const entries = Object.entries(shape)\n .map(([k, v]) => ` \"${k}\": ${zodToTsString(v)};`)\n .join('\\n');\n return `{\\n${entries}\\n }`;\n }\n case 'ZodOptional':\n case 'optional':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | undefined`;\n case 'ZodNullable':\n case 'nullable':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | null`;\n case 'ZodUnion':\n case 'union': {\n const options = def.options ?? [];\n return options.map(zodToTsString).join(' | ');\n }\n case 'ZodIntersection':\n case 'intersection':\n return `${zodToTsString(def.left)} & ${zodToTsString(def.right)}`;\n case 'ZodEnum':\n case 'enum': {\n const values = def.values ?? [];\n return values.map((v: string) => `\"${v}\"`).join(' | ');\n }\n case 'ZodLiteral':\n case 'literal': {\n const value = def.value;\n return typeof value === 'string' ? `\"${value}\"` : String(value);\n }\n default:\n return 'any';\n }\n};\n\ntype ZodToTsFns = {\n zodToTs: (schema: any, opts?: any) => { node: any };\n printNode: (node: any) => string;\n createAuxiliaryTypeStore: () => any;\n};\n\n/** Generate the content of the module augmentation file */\nconst generateTypeIndexContent = (\n typeFiles: string[],\n configuration: IntlayerConfig,\n zodToTsFns: ZodToTsFns | null\n): string => {\n const { internationalization, system, editor, routing } = configuration;\n const { moduleAugmentationDir } = system;\n const { enabled } = editor;\n const { locales, requiredLocales, strictMode, defaultLocale } =\n internationalization;\n\n let fileContent = 'import \"intlayer\";\\n';\n\n // Build dictionary refs\n const dictionariesRef = typeFiles.map((dictionaryPath) => ({\n relativePath: `./${normalizePath(relative(moduleAugmentationDir, dictionaryPath))}`,\n id: basename(dictionaryPath, extname(dictionaryPath)),\n hash: `_${getPathHash(dictionaryPath)}`,\n }));\n\n // Import all dictionaries\n for (const dictionary of dictionariesRef) {\n fileContent += `import ${dictionary.hash} from '${dictionary.relativePath}';\\n`;\n }\n fileContent += '\\n';\n\n // Dictionary map entries (id: typeof <hash>)\n const formattedDictionaryMap: string = dictionariesRef\n .map((dictionary) => ` \"${dictionary.id}\": typeof ${dictionary.hash};`)\n .join('\\n');\n\n // Ensure required ⊆ declared; if empty, default required = declared\n const declared = locales;\n const requiredSanitized = requiredLocales?.length\n ? requiredLocales.filter((requiredLocales) =>\n declared.includes(requiredLocales)\n )\n : declared;\n\n const formattedDeclaredLocales = formatLocales(declared);\n const formattedRequiredLocales = formatLocales(requiredSanitized);\n\n // Build schema registry\n const schemas = configuration.schemas ?? {};\n const formattedSchemas = Object.entries(schemas)\n .map(([key, schema]) => {\n let typeStr = 'any';\n\n if (schema) {\n try {\n if (zodToTsFns) {\n const { node } = zodToTsFns.zodToTs(schema, {\n auxiliaryTypeStore: zodToTsFns.createAuxiliaryTypeStore(),\n });\n // 133 is the kind for AnyKeyword in TypeScript\n if ((node as any).kind !== 133) {\n typeStr = zodToTsFns.printNode(node);\n } else {\n typeStr = zodToTsString(schema);\n }\n } else {\n typeStr = zodToTsString(schema);\n }\n } catch (_e) {\n // Fallback to custom string generator\n typeStr = zodToTsString(schema);\n }\n }\n return ` \"${key}\": ${typeStr};`;\n })\n .join('\\n');\n\n // Choose strict mode registry key\n const strictKey =\n strictMode === 'strict'\n ? 'strict'\n : strictMode === 'inclusive'\n ? 'inclusive'\n : 'loose';\n\n /**\n * Module augmentation that ONLY adds keys to registries.\n * No types/aliases redefined here—avoids merge conflicts.\n */\n fileContent += `declare module 'intlayer' {\\n`;\n // Dictionaries registry\n fileContent += ` interface __DictionaryRegistry {\\n${formattedDictionaryMap}\\n }\\n\\n`;\n // Locales registries\n fileContent += ` interface __DeclaredLocalesRegistry {\\n${formattedDeclaredLocales}\\n }\\n\\n`;\n fileContent += ` interface __RequiredLocalesRegistry {\\n${formattedRequiredLocales}\\n }\\n\\n`;\n // Schema registry\n fileContent += ` interface __SchemaRegistry {\\n${formattedSchemas}\\n }\\n\\n`;\n // Resolved strict mode (narrow the literal at build time)\n fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\\n\\n`;\n // Editor registry\n fileContent += ` interface __EditorRegistry { enabled : ${enabled} }\\n\\n`;\n // Routing registry (mode + defaultLocale narrowed to literals)\n const routingMode = routing?.mode ?? 'prefix-no-default';\n const formattedRewriteRules = formatRewriteRules(routing?.rewrite);\n fileContent += ` interface __RoutingRegistry { mode: '${routingMode}'; defaultLocale: '${defaultLocale}'${formattedRewriteRules} }\\n`;\n fileContent += `}\\n`;\n\n return fileContent;\n};\n\n/** Generate the index file merging all the types */\nexport const createModuleAugmentation = async (\n configuration: IntlayerConfig\n) => {\n const { moduleAugmentationDir, typesDir } = configuration.system;\n\n await mkdir(moduleAugmentationDir, { recursive: true });\n\n const dictionariesTypesDefinitions: string[] = await fg(\n normalizePath(`${typesDir}/*.ts`),\n { ignore: ['**/*.d.ts'] }\n );\n\n let zodToTsFns: ZodToTsFns | null = null;\n\n const hasSchemas = Object.keys(configuration.schemas ?? {}).length > 0;\n\n // zod-to-ts drags in the whole `typescript` package (~300ms import), so\n // only load it when there are schemas to convert.\n if (hasSchemas) {\n try {\n const mod = await import('zod-to-ts');\n zodToTsFns = {\n zodToTs: mod.zodToTs,\n printNode: mod.printNode,\n createAuxiliaryTypeStore: mod.createAuxiliaryTypeStore,\n };\n } catch {\n // typescript peer dep not installed (plain JS project), use fallback\n }\n }\n\n const tsContent = generateTypeIndexContent(\n dictionariesTypesDefinitions,\n configuration,\n zodToTsFns\n );\n\n const tsFilePath = join(moduleAugmentationDir, 'intlayer.d.ts');\n await writeFileIfChanged(tsFilePath, tsContent);\n};\n"],"mappings":"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"}
|
|
@@ -4,5 +4,6 @@ const require_createType_createType = require('./createType.cjs');
|
|
|
4
4
|
|
|
5
5
|
exports.createModuleAugmentation = require_createType_createModuleAugmentation.createModuleAugmentation;
|
|
6
6
|
exports.createTypes = require_createType_createType.createTypes;
|
|
7
|
+
exports.formatRewriteRules = require_createType_createModuleAugmentation.formatRewriteRules;
|
|
7
8
|
exports.generateTypeScriptType = require_createType_createType.generateTypeScriptType;
|
|
8
9
|
exports.getTypeName = require_createType_createModuleAugmentation.getTypeName;
|
package/dist/esm/build.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { getBuiltUnmergedDictionariesPath } from "./createDictionaryEntryPoint/g
|
|
|
7
7
|
import { createDictionaryEntryPoint } from "./createDictionaryEntryPoint/createDictionaryEntryPoint.mjs";
|
|
8
8
|
import { readDictionariesFromDisk } from "./utils/readDictionariesFromDisk.mjs";
|
|
9
9
|
import { buildDictionary } from "./buildIntlayerDictionary/buildIntlayerDictionary.mjs";
|
|
10
|
-
import { createModuleAugmentation, getTypeName } from "./createType/createModuleAugmentation.mjs";
|
|
10
|
+
import { createModuleAugmentation, formatRewriteRules, getTypeName } from "./createType/createModuleAugmentation.mjs";
|
|
11
11
|
import { createTypes, generateTypeScriptType } from "./createType/createType.mjs";
|
|
12
12
|
import { processContentDeclaration } from "./buildIntlayerDictionary/processContentDeclaration.mjs";
|
|
13
13
|
import { ensureIntlayerBundle, formatLocalDictionaries, loadContentDeclaration, loadContentDeclarations } from "./loadDictionaries/loadContentDeclaration.mjs";
|
|
@@ -23,4 +23,4 @@ import { transformJSFile } from "./writeContentDeclaration/transformJSFile.mjs";
|
|
|
23
23
|
import { writeJSFile } from "./writeContentDeclaration/writeJSFile.mjs";
|
|
24
24
|
import { writeContentDeclaration } from "./writeContentDeclaration/writeContentDeclaration.mjs";
|
|
25
25
|
|
|
26
|
-
export { buildDictionary, cleanOutputDir, createDictionaryEntryPoint, createModuleAugmentation, createTypes, detectExportedComponentName, ensureIntlayerBundle, formatDictionaries, formatDictionariesOutput, formatDictionary, formatDictionaryOutput, formatDistantDictionaries, formatLocalDictionaries, generateDictionaryListContent, generateTypeScriptType, getBuiltDictionariesPath, getBuiltDynamicDictionariesPath, getBuiltFetchDictionariesPath, getBuiltRemoteDictionariesPath, getBuiltUnmergedDictionariesPath, getTypeName, isCachedConfigurationUpToDate, loadContentDeclaration, loadContentDeclarations, loadDictionaries, loadLocalDictionaries, loadRemoteDictionaries, prepareIntlayer, processContentDeclaration, readDictionariesFromDisk, transformJSFile, writeConfiguration, writeContentDeclaration, writeJSFile };
|
|
26
|
+
export { buildDictionary, cleanOutputDir, createDictionaryEntryPoint, createModuleAugmentation, createTypes, detectExportedComponentName, ensureIntlayerBundle, formatDictionaries, formatDictionariesOutput, formatDictionary, formatDictionaryOutput, formatDistantDictionaries, formatLocalDictionaries, formatRewriteRules, generateDictionaryListContent, generateTypeScriptType, getBuiltDictionariesPath, getBuiltDynamicDictionariesPath, getBuiltFetchDictionariesPath, getBuiltRemoteDictionariesPath, getBuiltUnmergedDictionariesPath, getTypeName, isCachedConfigurationUpToDate, loadContentDeclaration, loadContentDeclarations, loadDictionaries, loadLocalDictionaries, loadRemoteDictionaries, prepareIntlayer, processContentDeclaration, readDictionariesFromDisk, transformJSFile, writeConfiguration, writeContentDeclaration, writeJSFile };
|
|
@@ -5,12 +5,12 @@ import { mkdir } from "node:fs/promises";
|
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { colorizePath } from "@intlayer/config/logger";
|
|
7
7
|
import { assertPathWithin } from "@intlayer/config/utils";
|
|
8
|
-
import { OUTPUT_FORMAT } from "@intlayer/config/defaultValues";
|
|
8
|
+
import { DYNAMIC_DICTIONARIES_JSON_SUBDIR, DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER, OUTPUT_FORMAT } from "@intlayer/config/defaultValues";
|
|
9
9
|
import { COMPOSITE_ID_SEPARATOR, QUALIFIER_DYNAMIC_TYPES_KEY, reconstructQualifiedEntry } from "@intlayer/core/dictionaryManipulator";
|
|
10
10
|
import { getPerLocaleDictionary } from "@intlayer/core/plugins";
|
|
11
11
|
|
|
12
12
|
//#region src/buildIntlayerDictionary/writeDynamicDictionary.ts
|
|
13
|
-
const DICTIONARIES_SUBDIR =
|
|
13
|
+
const DICTIONARIES_SUBDIR = DYNAMIC_DICTIONARIES_JSON_SUBDIR;
|
|
14
14
|
/**
|
|
15
15
|
* Escapes a value interpolated into a single-quoted literal of a generated
|
|
16
16
|
* loader module, so keys or segments containing `'` or `\` cannot break the
|
|
@@ -47,7 +47,7 @@ const generateDictionaryEntryPoint = (key, locales, format = "esm", nestedKeys =
|
|
|
47
47
|
if (sortedNestedKeys.length === 0) return ` '${safeLocale}': () => ${ownLoader}`;
|
|
48
48
|
return ` '${safeLocale}': () => Promise.all([\n ${ownLoader},\n ${sortedNestedKeys.map((nestedKey) => buildPerLocaleLoader(escapeJsLiteral(nestedKey), safeLocale, format)).join(",\n ")}\n ]).then(([_dictionary, ..._nested]) => ({\n ..._dictionary,\n nestedDictionaries: { ${sortedNestedKeys.map((nestedKey, index) => `'${escapeJsLiteral(nestedKey)}': _nested[${index}]`).join(", ")} }\n }))`;
|
|
49
49
|
}).join(",\n");
|
|
50
|
-
if (format === "esm") return `const
|
|
50
|
+
if (format === "esm") return `const ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\n${localeEntries}\n};\n\nexport default ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\n`;
|
|
51
51
|
return `module.exports = {\n${localeEntries}\n};\n`;
|
|
52
52
|
};
|
|
53
53
|
const buildLoaderExpression = (key, chunkSegments, locale, format) => {
|
|
@@ -95,7 +95,7 @@ const generateQualifiedDictionaryEntryPoint = (key, qualifierTypes, entries, loc
|
|
|
95
95
|
return ` '${escapeJsLiteral(locale)}': ${serializeLoaderTree(tree, 1)}`;
|
|
96
96
|
}).join(",\n");
|
|
97
97
|
const marker = ` '${QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;
|
|
98
|
-
if (format === "esm") return `const
|
|
98
|
+
if (format === "esm") return `const ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\n${marker},\n${localeEntries}\n};\n\nexport default ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\n`;
|
|
99
99
|
return `module.exports = {\n${marker},\n${localeEntries}\n};\n`;
|
|
100
100
|
};
|
|
101
101
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeDynamicDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n COMPOSITE_ID_SEPARATOR,\n QUALIFIER_DYNAMIC_TYPES_KEY,\n reconstructQualifiedEntry,\n} from '@intlayer/core/dictionaryManipulator';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { PlainMergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = 'json'; // Necessary to add a static first dir for Turbopack\n\n/**\n * Escapes a value interpolated into a single-quoted literal of a generated\n * loader module, so keys or segments containing `'` or `\\` cannot break the\n * emitted JavaScript.\n */\nconst escapeJsLiteral = (value: string): string =>\n value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\n/** Loader expression for one `(key, locale)` per-locale chunk. */\nconst buildPerLocaleLoader = (\n key: string,\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = `./${DICTIONARIES_SUBDIR}/${key}/${locale}.json`;\n\n return format === 'esm'\n ? `import('${path}').then(m => m.default)`\n : `Promise.resolve(require('${path}'))`;\n};\n\n/**\n * Generates the content of a dictionary entry point file.\n *\n * When the dictionary references other dictionaries through `nest()`, the\n * loader for a locale resolves the referenced dictionaries alongside it and\n * attaches them as `nestedDictionaries`. The nest targets then travel in the\n * same lazy chunk as their consumer, and `getNesting` resolves them from that\n * local reference instead of the global registry — which the build\n * optimization strips.\n *\n * @param key - The dictionary key.\n * @param locales - Locales to emit a loader for.\n * @param format - Output module format.\n * @param nestedKeys - Keys referenced through `nest()`, transitively.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm',\n nestedKeys: string[] = []\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n const sortedNestedKeys = [...nestedKeys].sort((a, b) => a.localeCompare(b));\n\n const safeKey = escapeJsLiteral(key);\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const safeLocale = escapeJsLiteral(locale);\n const ownLoader = buildPerLocaleLoader(safeKey, safeLocale, format);\n\n if (sortedNestedKeys.length === 0) {\n return ` '${safeLocale}': () => ${ownLoader}`;\n }\n\n const nestedLoaders = sortedNestedKeys\n .map((nestedKey) =>\n buildPerLocaleLoader(escapeJsLiteral(nestedKey), safeLocale, format)\n )\n .join(',\\n ');\n\n const attachedEntries = sortedNestedKeys\n .map(\n (nestedKey, index) =>\n `'${escapeJsLiteral(nestedKey)}': _nested[${index}]`\n )\n .join(', ');\n\n return (\n ` '${safeLocale}': () => Promise.all([\\n` +\n ` ${ownLoader},\\n` +\n ` ${nestedLoaders}\\n` +\n ` ]).then(([_dictionary, ..._nested]) => ({\\n` +\n ` ..._dictionary,\\n` +\n ` nestedDictionaries: { ${attachedEntries} }\\n` +\n ` }))`\n );\n })\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * A nested loader tree: one level per declared dimension, leaves are loader\n * expression strings.\n */\ntype LoaderTree = { [segment: string]: LoaderTree | string };\n\n/**\n * One entry of a qualified loader map.\n *\n * `treeSegments` is where the entry lives in the loader tree (its composite id\n * segments); `chunkSegments` is the chunk path it loads. They differ when the\n * entry aliases another entry with identical content (e.g. array-variant\n * fan-out): the alias keeps its own tree position but points at the canonical\n * entry's chunk, so equal content is emitted — and downloaded — only once.\n */\nexport type QualifiedEntrySegments = {\n treeSegments: string[];\n chunkSegments: string[];\n};\n\nconst buildLoaderExpression = (\n key: string,\n chunkSegments: string[],\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = escapeJsLiteral(\n `./${DICTIONARIES_SUBDIR}/${key}/${chunkSegments.join('/')}/${locale}.json`\n );\n\n return format === 'esm'\n ? `() => import('${path}').then(m => m.default)`\n : `() => Promise.resolve(require('${path}'))`;\n};\n\nconst buildLoaderTree = (\n key: string,\n entries: QualifiedEntrySegments[],\n locale: string,\n format: 'cjs' | 'esm'\n): LoaderTree => {\n const root: LoaderTree = {};\n\n for (const { treeSegments, chunkSegments } of entries) {\n let node = root;\n\n treeSegments.forEach((segment, index) => {\n if (index === treeSegments.length - 1) {\n node[segment] = buildLoaderExpression(\n key,\n chunkSegments,\n locale,\n format\n );\n return;\n }\n\n node[segment] = (node[segment] as LoaderTree | undefined) ?? {};\n node = node[segment] as LoaderTree;\n });\n }\n\n return root;\n};\n\nconst serializeLoaderTree = (tree: LoaderTree, indentLevel: number): string => {\n const pad = ' '.repeat(indentLevel);\n const innerPad = ' '.repeat(indentLevel + 1);\n\n const lines = Object.keys(tree)\n .sort((a, b) => a.localeCompare(b))\n .map((segment) => {\n const value = tree[segment]!;\n const serialized =\n typeof value === 'string'\n ? value\n : serializeLoaderTree(value, indentLevel + 1);\n\n return `${innerPad}'${escapeJsLiteral(segment)}': ${serialized}`;\n });\n\n return `{\\n${lines.join(',\\n')}\\n${pad}}`;\n};\n\n/**\n * Generates the entry point of a qualified dictionary (collection / variant,\n * possibly combined). Under each locale the loader map nests one\n * level per declared dimension (canonical order) and carries a marker listing\n * those dimensions so the runtime can walk the tree.\n *\n * One static `import()` is emitted per leaf `(locale, …segments)` chunk, which\n * keeps the output compatible with bundlers that reject template-literal\n * dynamic imports (Turbopack). Entries whose content is identical share one\n * chunk: their leaves point at the canonical entry's import path.\n */\nexport const generateQualifiedDictionaryEntryPoint = (\n key: string,\n qualifierTypes: DictionaryQualifierType[],\n entries: QualifiedEntrySegments[],\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const tree = buildLoaderTree(key, entries, locale, format);\n return ` '${escapeJsLiteral(locale)}': ${serializeLoaderTree(tree, 1)}`;\n })\n .join(',\\n');\n\n const marker = ` '${QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;\n\n if (format === 'esm') {\n return (\n `const content = {\\n${marker},\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${marker},\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: PlainMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT,\n nestedDictionaryGraph: Map<string, Set<string>> = new Map()\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format, [\n ...(nestedDictionaryGraph.get(key) ?? []),\n ]);\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n\n return resultDictionariesPaths;\n};\n\nexport type QualifiedMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: QualifiedDictionaryGroup;\n};\n\nexport type QualifiedMergedDictionaryOutput = Record<\n string,\n QualifiedMergedDictionaryResult\n>;\n\n/**\n * Writes the dynamic chunks and entry points of qualified dictionaries\n * (collections, variants — possibly combined) in\n * `importMode: 'dynamic'`.\n *\n * Each entry is reduced to one per-locale chunk written to a path nested by\n * dimension — `json/{key}/{seg1}/{seg2}/{locale}.json` — and a single\n * `{key}.{ext}` entry point exposes the matching nested loader tree, so the\n * entry point is discovered and aggregated exactly like a plain dynamic one.\n */\nexport const writeDynamicQualifiedDictionaries = async (\n qualifiedDictionaries: QualifiedMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<void> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n await parallelize(\n Object.entries(qualifiedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, { dictionary: group }]) => {\n if (key === 'undefined') return;\n\n const entryIds = Object.keys(group.content);\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n\n // Entries with identical content (e.g. array-variant fan-out) share one\n // chunk: only the first entry per content identity writes files; the\n // others become aliases whose loaders point at the canonical chunk path.\n const canonicalSegmentsByContent = new Map<string, string[]>();\n const entries: QualifiedEntrySegments[] = [];\n const canonicalEntryIds: string[] = [];\n\n for (const entryId of entryIds) {\n const treeSegments = entryId.split(COMPOSITE_ID_SEPARATOR);\n const contentIdentity = JSON.stringify(group.content[entryId]);\n const canonicalSegments =\n canonicalSegmentsByContent.get(contentIdentity);\n\n if (canonicalSegments) {\n entries.push({ treeSegments, chunkSegments: canonicalSegments });\n continue;\n }\n\n canonicalSegmentsByContent.set(contentIdentity, treeSegments);\n entries.push({ treeSegments, chunkSegments: treeSegments });\n canonicalEntryIds.push(entryId);\n }\n\n await parallelize(canonicalEntryIds, async (entryId) => {\n // Rebuild a resolvable dictionary from the content node + composite id\n // so per-locale extraction sees the same `{ key, content }` shape.\n const entry = reconstructQualifiedEntry(group, entryId);\n\n const segments = entryId.split(COMPOSITE_ID_SEPARATOR);\n\n const entryDir = resolve(keyDir, ...segments);\n assertPathWithin(entryDir, keyDir);\n await mkdir(entryDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n entry,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/<…segments>/locale.json\n const resultFilePath = resolve(entryDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${segments.join('/')}/${locale}.json:`,\n err\n );\n }\n );\n });\n });\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateQualifiedDictionaryEntryPoint(\n key,\n group.qualifierTypes,\n entries,\n locales,\n format\n );\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"writeDynamicDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport {\n DYNAMIC_DICTIONARIES_JSON_SUBDIR,\n DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER,\n OUTPUT_FORMAT,\n} from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n COMPOSITE_ID_SEPARATOR,\n QUALIFIER_DYNAMIC_TYPES_KEY,\n reconstructQualifiedEntry,\n} from '@intlayer/core/dictionaryManipulator';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { PlainMergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = DYNAMIC_DICTIONARIES_JSON_SUBDIR;\n\n/**\n * Escapes a value interpolated into a single-quoted literal of a generated\n * loader module, so keys or segments containing `'` or `\\` cannot break the\n * emitted JavaScript.\n */\nconst escapeJsLiteral = (value: string): string =>\n value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\n/** Loader expression for one `(key, locale)` per-locale chunk. */\nconst buildPerLocaleLoader = (\n key: string,\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = `./${DICTIONARIES_SUBDIR}/${key}/${locale}.json`;\n\n return format === 'esm'\n ? `import('${path}').then(m => m.default)`\n : `Promise.resolve(require('${path}'))`;\n};\n\n/**\n * Generates the content of a dictionary entry point file.\n *\n * When the dictionary references other dictionaries through `nest()`, the\n * loader for a locale resolves the referenced dictionaries alongside it and\n * attaches them as `nestedDictionaries`. The nest targets then travel in the\n * same lazy chunk as their consumer, and `getNesting` resolves them from that\n * local reference instead of the global registry — which the build\n * optimization strips.\n *\n * @param key - The dictionary key.\n * @param locales - Locales to emit a loader for.\n * @param format - Output module format.\n * @param nestedKeys - Keys referenced through `nest()`, transitively.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm',\n nestedKeys: string[] = []\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n const sortedNestedKeys = [...nestedKeys].sort((a, b) => a.localeCompare(b));\n\n const safeKey = escapeJsLiteral(key);\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const safeLocale = escapeJsLiteral(locale);\n const ownLoader = buildPerLocaleLoader(safeKey, safeLocale, format);\n\n if (sortedNestedKeys.length === 0) {\n return ` '${safeLocale}': () => ${ownLoader}`;\n }\n\n const nestedLoaders = sortedNestedKeys\n .map((nestedKey) =>\n buildPerLocaleLoader(escapeJsLiteral(nestedKey), safeLocale, format)\n )\n .join(',\\n ');\n\n const attachedEntries = sortedNestedKeys\n .map(\n (nestedKey, index) =>\n `'${escapeJsLiteral(nestedKey)}': _nested[${index}]`\n )\n .join(', ');\n\n return (\n ` '${safeLocale}': () => Promise.all([\\n` +\n ` ${ownLoader},\\n` +\n ` ${nestedLoaders}\\n` +\n ` ]).then(([_dictionary, ..._nested]) => ({\\n` +\n ` ..._dictionary,\\n` +\n ` nestedDictionaries: { ${attachedEntries} }\\n` +\n ` }))`\n );\n })\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\\n${localeEntries}\\n};\\n\\n` +\n `export default ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * A nested loader tree: one level per declared dimension, leaves are loader\n * expression strings.\n */\ntype LoaderTree = { [segment: string]: LoaderTree | string };\n\n/**\n * One entry of a qualified loader map.\n *\n * `treeSegments` is where the entry lives in the loader tree (its composite id\n * segments); `chunkSegments` is the chunk path it loads. They differ when the\n * entry aliases another entry with identical content (e.g. array-variant\n * fan-out): the alias keeps its own tree position but points at the canonical\n * entry's chunk, so equal content is emitted — and downloaded — only once.\n */\nexport type QualifiedEntrySegments = {\n treeSegments: string[];\n chunkSegments: string[];\n};\n\nconst buildLoaderExpression = (\n key: string,\n chunkSegments: string[],\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = escapeJsLiteral(\n `./${DICTIONARIES_SUBDIR}/${key}/${chunkSegments.join('/')}/${locale}.json`\n );\n\n return format === 'esm'\n ? `() => import('${path}').then(m => m.default)`\n : `() => Promise.resolve(require('${path}'))`;\n};\n\nconst buildLoaderTree = (\n key: string,\n entries: QualifiedEntrySegments[],\n locale: string,\n format: 'cjs' | 'esm'\n): LoaderTree => {\n const root: LoaderTree = {};\n\n for (const { treeSegments, chunkSegments } of entries) {\n let node = root;\n\n treeSegments.forEach((segment, index) => {\n if (index === treeSegments.length - 1) {\n node[segment] = buildLoaderExpression(\n key,\n chunkSegments,\n locale,\n format\n );\n return;\n }\n\n node[segment] = (node[segment] as LoaderTree | undefined) ?? {};\n node = node[segment] as LoaderTree;\n });\n }\n\n return root;\n};\n\nconst serializeLoaderTree = (tree: LoaderTree, indentLevel: number): string => {\n const pad = ' '.repeat(indentLevel);\n const innerPad = ' '.repeat(indentLevel + 1);\n\n const lines = Object.keys(tree)\n .sort((a, b) => a.localeCompare(b))\n .map((segment) => {\n const value = tree[segment]!;\n const serialized =\n typeof value === 'string'\n ? value\n : serializeLoaderTree(value, indentLevel + 1);\n\n return `${innerPad}'${escapeJsLiteral(segment)}': ${serialized}`;\n });\n\n return `{\\n${lines.join(',\\n')}\\n${pad}}`;\n};\n\n/**\n * Generates the entry point of a qualified dictionary (collection / variant,\n * possibly combined). Under each locale the loader map nests one\n * level per declared dimension (canonical order) and carries a marker listing\n * those dimensions so the runtime can walk the tree.\n *\n * One static `import()` is emitted per leaf `(locale, …segments)` chunk, which\n * keeps the output compatible with bundlers that reject template-literal\n * dynamic imports (Turbopack). Entries whose content is identical share one\n * chunk: their leaves point at the canonical entry's import path.\n */\nexport const generateQualifiedDictionaryEntryPoint = (\n key: string,\n qualifierTypes: DictionaryQualifierType[],\n entries: QualifiedEntrySegments[],\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const tree = buildLoaderTree(key, entries, locale, format);\n return ` '${escapeJsLiteral(locale)}': ${serializeLoaderTree(tree, 1)}`;\n })\n .join(',\\n');\n\n const marker = ` '${QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;\n\n if (format === 'esm') {\n return (\n `const ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER} = {\\n${marker},\\n${localeEntries}\\n};\\n\\n` +\n `export default ${DYNAMIC_ENTRY_LOADER_MAP_IDENTIFIER};\\n`\n );\n }\n return `module.exports = {\\n${marker},\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: PlainMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT,\n nestedDictionaryGraph: Map<string, Set<string>> = new Map()\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format, [\n ...(nestedDictionaryGraph.get(key) ?? []),\n ]);\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n\n return resultDictionariesPaths;\n};\n\nexport type QualifiedMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: QualifiedDictionaryGroup;\n};\n\nexport type QualifiedMergedDictionaryOutput = Record<\n string,\n QualifiedMergedDictionaryResult\n>;\n\n/**\n * Writes the dynamic chunks and entry points of qualified dictionaries\n * (collections, variants — possibly combined) in\n * `importMode: 'dynamic'`.\n *\n * Each entry is reduced to one per-locale chunk written to a path nested by\n * dimension — `json/{key}/{seg1}/{seg2}/{locale}.json` — and a single\n * `{key}.{ext}` entry point exposes the matching nested loader tree, so the\n * entry point is discovered and aggregated exactly like a plain dynamic one.\n */\nexport const writeDynamicQualifiedDictionaries = async (\n qualifiedDictionaries: QualifiedMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<void> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n await parallelize(\n Object.entries(qualifiedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, { dictionary: group }]) => {\n if (key === 'undefined') return;\n\n const entryIds = Object.keys(group.content);\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n\n // Entries with identical content (e.g. array-variant fan-out) share one\n // chunk: only the first entry per content identity writes files; the\n // others become aliases whose loaders point at the canonical chunk path.\n const canonicalSegmentsByContent = new Map<string, string[]>();\n const entries: QualifiedEntrySegments[] = [];\n const canonicalEntryIds: string[] = [];\n\n for (const entryId of entryIds) {\n const treeSegments = entryId.split(COMPOSITE_ID_SEPARATOR);\n const contentIdentity = JSON.stringify(group.content[entryId]);\n const canonicalSegments =\n canonicalSegmentsByContent.get(contentIdentity);\n\n if (canonicalSegments) {\n entries.push({ treeSegments, chunkSegments: canonicalSegments });\n continue;\n }\n\n canonicalSegmentsByContent.set(contentIdentity, treeSegments);\n entries.push({ treeSegments, chunkSegments: treeSegments });\n canonicalEntryIds.push(entryId);\n }\n\n await parallelize(canonicalEntryIds, async (entryId) => {\n // Rebuild a resolvable dictionary from the content node + composite id\n // so per-locale extraction sees the same `{ key, content }` shape.\n const entry = reconstructQualifiedEntry(group, entryId);\n\n const segments = entryId.split(COMPOSITE_ID_SEPARATOR);\n\n const entryDir = resolve(keyDir, ...segments);\n assertPathWithin(entryDir, keyDir);\n await mkdir(entryDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n entry,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/<…segments>/locale.json\n const resultFilePath = resolve(entryDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${segments.join('/')}/${locale}.json:`,\n err\n );\n }\n );\n });\n });\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateQualifiedDictionaryEntryPoint(\n key,\n group.qualifierTypes,\n entries,\n locales,\n format\n );\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n};\n"],"mappings":"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"}
|
|
@@ -4,11 +4,28 @@ import { mkdir } from "node:fs/promises";
|
|
|
4
4
|
import { basename, extname, join, relative } from "node:path";
|
|
5
5
|
import fg from "fast-glob";
|
|
6
6
|
import { kebabCaseToCamelCase, normalizePath } from "@intlayer/config/utils";
|
|
7
|
+
import { getRewriteRules } from "@intlayer/core/localization";
|
|
7
8
|
|
|
8
9
|
//#region src/createType/createModuleAugmentation.ts
|
|
9
10
|
const getTypeName = (key) => `${kebabCaseToCamelCase(key)}Content`;
|
|
10
11
|
/** Returns lines like: [Locales.FRENCH]: 1; */
|
|
11
12
|
const formatLocales = (locales) => locales.map((locale) => ` "${locale}": 1;`).join("\n");
|
|
13
|
+
/**
|
|
14
|
+
* Serializes the `url` rewrite rules as a type literal, so `getLocalizedPath`
|
|
15
|
+
* and `getLocalizedUrl` can resolve a canonical path to its localized literal at
|
|
16
|
+
* the type level. Returns an empty string when the project declares no rewrite —
|
|
17
|
+
* the consuming types then localize every path to itself.
|
|
18
|
+
*
|
|
19
|
+
* Rules are emitted in their normalized form (`:param`, no locale prefix), which
|
|
20
|
+
* is exactly what the runtime matches against.
|
|
21
|
+
*/
|
|
22
|
+
const formatRewriteRules = (rewrite) => {
|
|
23
|
+
const rewriteRules = getRewriteRules(rewrite, "url");
|
|
24
|
+
if (!rewriteRules?.rules?.length) return "";
|
|
25
|
+
return `; rewrite: { ${rewriteRules.rules.map(({ canonical, localized }) => {
|
|
26
|
+
return `'${canonical}': { ${Object.entries(localized).filter(([, pattern]) => typeof pattern === "string").map(([locale, pattern]) => `'${locale}': '${pattern}'`).join("; ")} }`;
|
|
27
|
+
}).join("; ")} }`;
|
|
28
|
+
};
|
|
12
29
|
const zodToTsString = (schema) => {
|
|
13
30
|
if (!schema) return "any";
|
|
14
31
|
const def = schema._def ?? schema.def ?? schema;
|
|
@@ -95,7 +112,8 @@ const generateTypeIndexContent = (typeFiles, configuration, zodToTsFns) => {
|
|
|
95
112
|
fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\n\n`;
|
|
96
113
|
fileContent += ` interface __EditorRegistry { enabled : ${enabled} }\n\n`;
|
|
97
114
|
const routingMode = routing?.mode ?? "prefix-no-default";
|
|
98
|
-
|
|
115
|
+
const formattedRewriteRules = formatRewriteRules(routing?.rewrite);
|
|
116
|
+
fileContent += ` interface __RoutingRegistry { mode: '${routingMode}'; defaultLocale: '${defaultLocale}'${formattedRewriteRules} }\n`;
|
|
99
117
|
fileContent += `}\n`;
|
|
100
118
|
return fileContent;
|
|
101
119
|
};
|
|
@@ -119,5 +137,5 @@ const createModuleAugmentation = async (configuration) => {
|
|
|
119
137
|
};
|
|
120
138
|
|
|
121
139
|
//#endregion
|
|
122
|
-
export { createModuleAugmentation, getTypeName };
|
|
140
|
+
export { createModuleAugmentation, formatRewriteRules, getTypeName };
|
|
123
141
|
//# sourceMappingURL=createModuleAugmentation.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createModuleAugmentation.mjs","names":[],"sources":["../../../src/createType/createModuleAugmentation.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { basename, extname, join, relative } from 'node:path';\nimport { kebabCaseToCamelCase, normalizePath } from '@intlayer/config/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { getPathHash } from '../utils';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\n\nexport const getTypeName = (key: string): string =>\n `${kebabCaseToCamelCase(key)}Content`;\n\n/** Returns lines like: [Locales.FRENCH]: 1; */\nconst formatLocales = (locales: Locale[]) =>\n locales.map((locale) => ` \"${locale}\": 1;`).join('\\n');\n\nconst zodToTsString = (schema: any): string => {\n if (!schema) return 'any';\n\n // Support both real Zod objects (_def) and serialized versions (def or nested)\n const def = schema._def ?? schema.def ?? schema;\n\n // Handle serialized type names (sometimes 'type' instead of 'typeName')\n const typeName = def.typeName ?? def.type;\n\n switch (typeName) {\n case 'ZodString':\n case 'string':\n return 'string';\n case 'ZodNumber':\n case 'number':\n return 'number';\n case 'ZodBoolean':\n case 'boolean':\n return 'boolean';\n case 'ZodNull':\n case 'null':\n return 'null';\n case 'ZodUndefined':\n case 'undefined':\n return 'undefined';\n case 'ZodArray':\n case 'array':\n return `${zodToTsString(def.type ?? def.element)}[]`;\n case 'ZodObject':\n case 'object': {\n const shape = typeof def.shape === 'function' ? def.shape() : def.shape;\n if (!shape) return 'Record<string, any>';\n\n const entries = Object.entries(shape)\n .map(([k, v]) => ` \"${k}\": ${zodToTsString(v)};`)\n .join('\\n');\n return `{\\n${entries}\\n }`;\n }\n case 'ZodOptional':\n case 'optional':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | undefined`;\n case 'ZodNullable':\n case 'nullable':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | null`;\n case 'ZodUnion':\n case 'union': {\n const options = def.options ?? [];\n return options.map(zodToTsString).join(' | ');\n }\n case 'ZodIntersection':\n case 'intersection':\n return `${zodToTsString(def.left)} & ${zodToTsString(def.right)}`;\n case 'ZodEnum':\n case 'enum': {\n const values = def.values ?? [];\n return values.map((v: string) => `\"${v}\"`).join(' | ');\n }\n case 'ZodLiteral':\n case 'literal': {\n const value = def.value;\n return typeof value === 'string' ? `\"${value}\"` : String(value);\n }\n default:\n return 'any';\n }\n};\n\ntype ZodToTsFns = {\n zodToTs: (schema: any, opts?: any) => { node: any };\n printNode: (node: any) => string;\n createAuxiliaryTypeStore: () => any;\n};\n\n/** Generate the content of the module augmentation file */\nconst generateTypeIndexContent = (\n typeFiles: string[],\n configuration: IntlayerConfig,\n zodToTsFns: ZodToTsFns | null\n): string => {\n const { internationalization, system, editor, routing } = configuration;\n const { moduleAugmentationDir } = system;\n const { enabled } = editor;\n const { locales, requiredLocales, strictMode, defaultLocale } =\n internationalization;\n\n let fileContent = 'import \"intlayer\";\\n';\n\n // Build dictionary refs\n const dictionariesRef = typeFiles.map((dictionaryPath) => ({\n relativePath: `./${normalizePath(relative(moduleAugmentationDir, dictionaryPath))}`,\n id: basename(dictionaryPath, extname(dictionaryPath)),\n hash: `_${getPathHash(dictionaryPath)}`,\n }));\n\n // Import all dictionaries\n for (const dictionary of dictionariesRef) {\n fileContent += `import ${dictionary.hash} from '${dictionary.relativePath}';\\n`;\n }\n fileContent += '\\n';\n\n // Dictionary map entries (id: typeof <hash>)\n const formattedDictionaryMap: string = dictionariesRef\n .map((dictionary) => ` \"${dictionary.id}\": typeof ${dictionary.hash};`)\n .join('\\n');\n\n // Ensure required ⊆ declared; if empty, default required = declared\n const declared = locales;\n const requiredSanitized = requiredLocales?.length\n ? requiredLocales.filter((requiredLocales) =>\n declared.includes(requiredLocales)\n )\n : declared;\n\n const formattedDeclaredLocales = formatLocales(declared);\n const formattedRequiredLocales = formatLocales(requiredSanitized);\n\n // Build schema registry\n const schemas = configuration.schemas ?? {};\n const formattedSchemas = Object.entries(schemas)\n .map(([key, schema]) => {\n let typeStr = 'any';\n\n if (schema) {\n try {\n if (zodToTsFns) {\n const { node } = zodToTsFns.zodToTs(schema, {\n auxiliaryTypeStore: zodToTsFns.createAuxiliaryTypeStore(),\n });\n // 133 is the kind for AnyKeyword in TypeScript\n if ((node as any).kind !== 133) {\n typeStr = zodToTsFns.printNode(node);\n } else {\n typeStr = zodToTsString(schema);\n }\n } else {\n typeStr = zodToTsString(schema);\n }\n } catch (_e) {\n // Fallback to custom string generator\n typeStr = zodToTsString(schema);\n }\n }\n return ` \"${key}\": ${typeStr};`;\n })\n .join('\\n');\n\n // Choose strict mode registry key\n const strictKey =\n strictMode === 'strict'\n ? 'strict'\n : strictMode === 'inclusive'\n ? 'inclusive'\n : 'loose';\n\n /**\n * Module augmentation that ONLY adds keys to registries.\n * No types/aliases redefined here—avoids merge conflicts.\n */\n fileContent += `declare module 'intlayer' {\\n`;\n // Dictionaries registry\n fileContent += ` interface __DictionaryRegistry {\\n${formattedDictionaryMap}\\n }\\n\\n`;\n // Locales registries\n fileContent += ` interface __DeclaredLocalesRegistry {\\n${formattedDeclaredLocales}\\n }\\n\\n`;\n fileContent += ` interface __RequiredLocalesRegistry {\\n${formattedRequiredLocales}\\n }\\n\\n`;\n // Schema registry\n fileContent += ` interface __SchemaRegistry {\\n${formattedSchemas}\\n }\\n\\n`;\n // Resolved strict mode (narrow the literal at build time)\n fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\\n\\n`;\n // Editor registry\n fileContent += ` interface __EditorRegistry { enabled : ${enabled} }\\n\\n`;\n // Routing registry (mode + defaultLocale narrowed to literals)\n const routingMode = routing?.mode ?? 'prefix-no-default';\n fileContent += ` interface __RoutingRegistry { mode: '${routingMode}'; defaultLocale: '${defaultLocale}' }\\n`;\n fileContent += `}\\n`;\n\n return fileContent;\n};\n\n/** Generate the index file merging all the types */\nexport const createModuleAugmentation = async (\n configuration: IntlayerConfig\n) => {\n const { moduleAugmentationDir, typesDir } = configuration.system;\n\n await mkdir(moduleAugmentationDir, { recursive: true });\n\n const dictionariesTypesDefinitions: string[] = await fg(\n normalizePath(`${typesDir}/*.ts`),\n { ignore: ['**/*.d.ts'] }\n );\n\n let zodToTsFns: ZodToTsFns | null = null;\n\n const hasSchemas = Object.keys(configuration.schemas ?? {}).length > 0;\n\n // zod-to-ts drags in the whole `typescript` package (~300ms import), so\n // only load it when there are schemas to convert.\n if (hasSchemas) {\n try {\n const mod = await import('zod-to-ts');\n zodToTsFns = {\n zodToTs: mod.zodToTs,\n printNode: mod.printNode,\n createAuxiliaryTypeStore: mod.createAuxiliaryTypeStore,\n };\n } catch {\n // typescript peer dep not installed (plain JS project), use fallback\n }\n }\n\n const tsContent = generateTypeIndexContent(\n dictionariesTypesDefinitions,\n configuration,\n zodToTsFns\n );\n\n const tsFilePath = join(moduleAugmentationDir, 'intlayer.d.ts');\n await writeFileIfChanged(tsFilePath, tsContent);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createModuleAugmentation.mjs","names":[],"sources":["../../../src/createType/createModuleAugmentation.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { basename, extname, join, relative } from 'node:path';\nimport { kebabCaseToCamelCase, normalizePath } from '@intlayer/config/utils';\nimport { getRewriteRules } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig, RoutingConfig } from '@intlayer/types/config';\nimport fg from 'fast-glob';\nimport { getPathHash } from '../utils';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\n\nexport const getTypeName = (key: string): string =>\n `${kebabCaseToCamelCase(key)}Content`;\n\n/** Returns lines like: [Locales.FRENCH]: 1; */\nconst formatLocales = (locales: Locale[]) =>\n locales.map((locale) => ` \"${locale}\": 1;`).join('\\n');\n\n/**\n * Serializes the `url` rewrite rules as a type literal, so `getLocalizedPath`\n * and `getLocalizedUrl` can resolve a canonical path to its localized literal at\n * the type level. Returns an empty string when the project declares no rewrite —\n * the consuming types then localize every path to itself.\n *\n * Rules are emitted in their normalized form (`:param`, no locale prefix), which\n * is exactly what the runtime matches against.\n */\nexport const formatRewriteRules = (\n rewrite: RoutingConfig['rewrite']\n): string => {\n const rewriteRules = getRewriteRules(rewrite, 'url');\n\n if (!rewriteRules?.rules?.length) return '';\n\n const formattedRules = rewriteRules.rules\n .map(({ canonical, localized }) => {\n const formattedLocalized = Object.entries(localized)\n .filter(([, pattern]) => typeof pattern === 'string')\n .map(([locale, pattern]) => `'${locale}': '${pattern}'`)\n .join('; ');\n\n return `'${canonical}': { ${formattedLocalized} }`;\n })\n .join('; ');\n\n return `; rewrite: { ${formattedRules} }`;\n};\n\nconst zodToTsString = (schema: any): string => {\n if (!schema) return 'any';\n\n // Support both real Zod objects (_def) and serialized versions (def or nested)\n const def = schema._def ?? schema.def ?? schema;\n\n // Handle serialized type names (sometimes 'type' instead of 'typeName')\n const typeName = def.typeName ?? def.type;\n\n switch (typeName) {\n case 'ZodString':\n case 'string':\n return 'string';\n case 'ZodNumber':\n case 'number':\n return 'number';\n case 'ZodBoolean':\n case 'boolean':\n return 'boolean';\n case 'ZodNull':\n case 'null':\n return 'null';\n case 'ZodUndefined':\n case 'undefined':\n return 'undefined';\n case 'ZodArray':\n case 'array':\n return `${zodToTsString(def.type ?? def.element)}[]`;\n case 'ZodObject':\n case 'object': {\n const shape = typeof def.shape === 'function' ? def.shape() : def.shape;\n if (!shape) return 'Record<string, any>';\n\n const entries = Object.entries(shape)\n .map(([k, v]) => ` \"${k}\": ${zodToTsString(v)};`)\n .join('\\n');\n return `{\\n${entries}\\n }`;\n }\n case 'ZodOptional':\n case 'optional':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | undefined`;\n case 'ZodNullable':\n case 'nullable':\n return `${zodToTsString(def.innerType ?? def.wrapped)} | null`;\n case 'ZodUnion':\n case 'union': {\n const options = def.options ?? [];\n return options.map(zodToTsString).join(' | ');\n }\n case 'ZodIntersection':\n case 'intersection':\n return `${zodToTsString(def.left)} & ${zodToTsString(def.right)}`;\n case 'ZodEnum':\n case 'enum': {\n const values = def.values ?? [];\n return values.map((v: string) => `\"${v}\"`).join(' | ');\n }\n case 'ZodLiteral':\n case 'literal': {\n const value = def.value;\n return typeof value === 'string' ? `\"${value}\"` : String(value);\n }\n default:\n return 'any';\n }\n};\n\ntype ZodToTsFns = {\n zodToTs: (schema: any, opts?: any) => { node: any };\n printNode: (node: any) => string;\n createAuxiliaryTypeStore: () => any;\n};\n\n/** Generate the content of the module augmentation file */\nconst generateTypeIndexContent = (\n typeFiles: string[],\n configuration: IntlayerConfig,\n zodToTsFns: ZodToTsFns | null\n): string => {\n const { internationalization, system, editor, routing } = configuration;\n const { moduleAugmentationDir } = system;\n const { enabled } = editor;\n const { locales, requiredLocales, strictMode, defaultLocale } =\n internationalization;\n\n let fileContent = 'import \"intlayer\";\\n';\n\n // Build dictionary refs\n const dictionariesRef = typeFiles.map((dictionaryPath) => ({\n relativePath: `./${normalizePath(relative(moduleAugmentationDir, dictionaryPath))}`,\n id: basename(dictionaryPath, extname(dictionaryPath)),\n hash: `_${getPathHash(dictionaryPath)}`,\n }));\n\n // Import all dictionaries\n for (const dictionary of dictionariesRef) {\n fileContent += `import ${dictionary.hash} from '${dictionary.relativePath}';\\n`;\n }\n fileContent += '\\n';\n\n // Dictionary map entries (id: typeof <hash>)\n const formattedDictionaryMap: string = dictionariesRef\n .map((dictionary) => ` \"${dictionary.id}\": typeof ${dictionary.hash};`)\n .join('\\n');\n\n // Ensure required ⊆ declared; if empty, default required = declared\n const declared = locales;\n const requiredSanitized = requiredLocales?.length\n ? requiredLocales.filter((requiredLocales) =>\n declared.includes(requiredLocales)\n )\n : declared;\n\n const formattedDeclaredLocales = formatLocales(declared);\n const formattedRequiredLocales = formatLocales(requiredSanitized);\n\n // Build schema registry\n const schemas = configuration.schemas ?? {};\n const formattedSchemas = Object.entries(schemas)\n .map(([key, schema]) => {\n let typeStr = 'any';\n\n if (schema) {\n try {\n if (zodToTsFns) {\n const { node } = zodToTsFns.zodToTs(schema, {\n auxiliaryTypeStore: zodToTsFns.createAuxiliaryTypeStore(),\n });\n // 133 is the kind for AnyKeyword in TypeScript\n if ((node as any).kind !== 133) {\n typeStr = zodToTsFns.printNode(node);\n } else {\n typeStr = zodToTsString(schema);\n }\n } else {\n typeStr = zodToTsString(schema);\n }\n } catch (_e) {\n // Fallback to custom string generator\n typeStr = zodToTsString(schema);\n }\n }\n return ` \"${key}\": ${typeStr};`;\n })\n .join('\\n');\n\n // Choose strict mode registry key\n const strictKey =\n strictMode === 'strict'\n ? 'strict'\n : strictMode === 'inclusive'\n ? 'inclusive'\n : 'loose';\n\n /**\n * Module augmentation that ONLY adds keys to registries.\n * No types/aliases redefined here—avoids merge conflicts.\n */\n fileContent += `declare module 'intlayer' {\\n`;\n // Dictionaries registry\n fileContent += ` interface __DictionaryRegistry {\\n${formattedDictionaryMap}\\n }\\n\\n`;\n // Locales registries\n fileContent += ` interface __DeclaredLocalesRegistry {\\n${formattedDeclaredLocales}\\n }\\n\\n`;\n fileContent += ` interface __RequiredLocalesRegistry {\\n${formattedRequiredLocales}\\n }\\n\\n`;\n // Schema registry\n fileContent += ` interface __SchemaRegistry {\\n${formattedSchemas}\\n }\\n\\n`;\n // Resolved strict mode (narrow the literal at build time)\n fileContent += ` interface __StrictModeRegistry { mode: '${strictKey}' }\\n\\n`;\n // Editor registry\n fileContent += ` interface __EditorRegistry { enabled : ${enabled} }\\n\\n`;\n // Routing registry (mode + defaultLocale narrowed to literals)\n const routingMode = routing?.mode ?? 'prefix-no-default';\n const formattedRewriteRules = formatRewriteRules(routing?.rewrite);\n fileContent += ` interface __RoutingRegistry { mode: '${routingMode}'; defaultLocale: '${defaultLocale}'${formattedRewriteRules} }\\n`;\n fileContent += `}\\n`;\n\n return fileContent;\n};\n\n/** Generate the index file merging all the types */\nexport const createModuleAugmentation = async (\n configuration: IntlayerConfig\n) => {\n const { moduleAugmentationDir, typesDir } = configuration.system;\n\n await mkdir(moduleAugmentationDir, { recursive: true });\n\n const dictionariesTypesDefinitions: string[] = await fg(\n normalizePath(`${typesDir}/*.ts`),\n { ignore: ['**/*.d.ts'] }\n );\n\n let zodToTsFns: ZodToTsFns | null = null;\n\n const hasSchemas = Object.keys(configuration.schemas ?? {}).length > 0;\n\n // zod-to-ts drags in the whole `typescript` package (~300ms import), so\n // only load it when there are schemas to convert.\n if (hasSchemas) {\n try {\n const mod = await import('zod-to-ts');\n zodToTsFns = {\n zodToTs: mod.zodToTs,\n printNode: mod.printNode,\n createAuxiliaryTypeStore: mod.createAuxiliaryTypeStore,\n };\n } catch {\n // typescript peer dep not installed (plain JS project), use fallback\n }\n }\n\n const tsContent = generateTypeIndexContent(\n dictionariesTypesDefinitions,\n configuration,\n zodToTsFns\n );\n\n const tsFilePath = join(moduleAugmentationDir, 'intlayer.d.ts');\n await writeFileIfChanged(tsFilePath, tsContent);\n};\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createModuleAugmentation, getTypeName } from "./createModuleAugmentation.mjs";
|
|
1
|
+
import { createModuleAugmentation, formatRewriteRules, getTypeName } from "./createModuleAugmentation.mjs";
|
|
2
2
|
import { createTypes, generateTypeScriptType } from "./createType.mjs";
|
|
3
3
|
|
|
4
|
-
export { createModuleAugmentation, createTypes, generateTypeScriptType, getTypeName };
|
|
4
|
+
export { createModuleAugmentation, createTypes, formatRewriteRules, generateTypeScriptType, getTypeName };
|
package/dist/types/build.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { getBuiltFetchDictionariesPath } from "./createDictionaryEntryPoint/getB
|
|
|
10
10
|
import { getBuiltRemoteDictionariesPath } from "./createDictionaryEntryPoint/getBuiltRemoteDictionariesPath.js";
|
|
11
11
|
import { getBuiltUnmergedDictionariesPath } from "./createDictionaryEntryPoint/getBuiltUnmergedDictionariesPath.js";
|
|
12
12
|
import "./createDictionaryEntryPoint/index.js";
|
|
13
|
-
import { createModuleAugmentation, getTypeName } from "./createType/createModuleAugmentation.js";
|
|
13
|
+
import { createModuleAugmentation, formatRewriteRules, getTypeName } from "./createType/createModuleAugmentation.js";
|
|
14
14
|
import { createTypes, generateTypeScriptType } from "./createType/createType.js";
|
|
15
15
|
import "./createType/index.js";
|
|
16
16
|
import { formatDictionaries, formatDictionariesOutput, formatDictionary, formatDictionaryOutput } from "./formatDictionary.js";
|
|
@@ -28,4 +28,4 @@ import { transformJSFile } from "./writeContentDeclaration/transformJSFile.js";
|
|
|
28
28
|
import { writeContentDeclaration } from "./writeContentDeclaration/writeContentDeclaration.js";
|
|
29
29
|
import { writeJSFile } from "./writeContentDeclaration/writeJSFile.js";
|
|
30
30
|
import "./writeContentDeclaration/index.js";
|
|
31
|
-
export { BuildDictionariesOptions, CreateDictionaryEntryPointOptions, DictionariesStatus, DictionaryStatus, buildDictionary, cleanOutputDir, createDictionaryEntryPoint, createModuleAugmentation, createTypes, detectExportedComponentName, ensureIntlayerBundle, formatDictionaries, formatDictionariesOutput, formatDictionary, formatDictionaryOutput, formatDistantDictionaries, formatLocalDictionaries, generateDictionaryListContent, generateTypeScriptType, getBuiltDictionariesPath, getBuiltDynamicDictionariesPath, getBuiltFetchDictionariesPath, getBuiltRemoteDictionariesPath, getBuiltUnmergedDictionariesPath, getTypeName, isCachedConfigurationUpToDate, loadContentDeclaration, loadContentDeclarations, loadDictionaries, loadLocalDictionaries, loadRemoteDictionaries, prepareIntlayer, processContentDeclaration, readDictionariesFromDisk, transformJSFile, writeConfiguration, writeContentDeclaration, writeJSFile };
|
|
31
|
+
export { BuildDictionariesOptions, CreateDictionaryEntryPointOptions, DictionariesStatus, DictionaryStatus, buildDictionary, cleanOutputDir, createDictionaryEntryPoint, createModuleAugmentation, createTypes, detectExportedComponentName, ensureIntlayerBundle, formatDictionaries, formatDictionariesOutput, formatDictionary, formatDictionaryOutput, formatDistantDictionaries, formatLocalDictionaries, formatRewriteRules, generateDictionaryListContent, generateTypeScriptType, getBuiltDictionariesPath, getBuiltDynamicDictionariesPath, getBuiltFetchDictionariesPath, getBuiltRemoteDictionariesPath, getBuiltUnmergedDictionariesPath, getTypeName, isCachedConfigurationUpToDate, loadContentDeclaration, loadContentDeclarations, loadDictionaries, loadLocalDictionaries, loadRemoteDictionaries, prepareIntlayer, processContentDeclaration, readDictionariesFromDisk, transformJSFile, writeConfiguration, writeContentDeclaration, writeJSFile };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeDynamicDictionary.d.ts","names":[],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"writeDynamicDictionary.d.ts","names":[],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"mappings":";;;;;KA2BY;EACV;EACA,YAAY;;KAGF,4BAA4B,QACtC,OAAO,QAAQ;KAGL,4BAA4B,eAEtC;;;;;;;;;;;;;;;;cAyCW,+BAA4B,aAC5B,mBACM,wBACI;;;;;;;;;;KAoEX;EACV;EACA;;;;;;;;;;;;;cA8EW,wCAAqC,aACrC,gBACK,2BAAyB,SAChC,0BAAwB,mBAChB;;;;;;;;;;;;;;;;;cAyCN,yBAAsB,oBACb,6BAA2B,eAChC,gBAAc,6BACH,wBACH,YAAY,iBAClC,QAAQ;KA2EC;EACV;EACA,YAAY;;KAGF,kCAAkC,eAE5C;;;;;;;;;;;cAaW,oCAAiC,uBACrB,iCAA+B,eACvC,gBAAc,gCAE5B"}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import { IntlayerConfig } from "@intlayer/types/config";
|
|
1
|
+
import { IntlayerConfig, RoutingConfig } from "@intlayer/types/config";
|
|
2
2
|
//#region src/createType/createModuleAugmentation.d.ts
|
|
3
3
|
declare const getTypeName: (key: string) => string;
|
|
4
|
+
/**
|
|
5
|
+
* Serializes the `url` rewrite rules as a type literal, so `getLocalizedPath`
|
|
6
|
+
* and `getLocalizedUrl` can resolve a canonical path to its localized literal at
|
|
7
|
+
* the type level. Returns an empty string when the project declares no rewrite —
|
|
8
|
+
* the consuming types then localize every path to itself.
|
|
9
|
+
*
|
|
10
|
+
* Rules are emitted in their normalized form (`:param`, no locale prefix), which
|
|
11
|
+
* is exactly what the runtime matches against.
|
|
12
|
+
*/
|
|
13
|
+
declare const formatRewriteRules: (rewrite: RoutingConfig['rewrite']) => string;
|
|
4
14
|
/** Generate the index file merging all the types */
|
|
5
15
|
declare const createModuleAugmentation: (configuration: IntlayerConfig) => Promise<void>;
|
|
6
16
|
//#endregion
|
|
7
|
-
export { createModuleAugmentation, getTypeName };
|
|
17
|
+
export { createModuleAugmentation, formatRewriteRules, getTypeName };
|
|
8
18
|
//# sourceMappingURL=createModuleAugmentation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createModuleAugmentation.d.ts","names":[],"sources":["../../../src/createType/createModuleAugmentation.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"createModuleAugmentation.d.ts","names":[],"sources":["../../../src/createType/createModuleAugmentation.ts"],"mappings":";;cAUa,cAAW;;;;;;;;;;cAgBX,qBAAkB,SACpB;;cAwME,2BAAwB,eACpB,mBAAc"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { createModuleAugmentation, getTypeName } from "./createModuleAugmentation.js";
|
|
1
|
+
import { createModuleAugmentation, formatRewriteRules, getTypeName } from "./createModuleAugmentation.js";
|
|
2
2
|
import { createTypes, generateTypeScriptType } from "./createType.js";
|
|
3
|
-
export { createModuleAugmentation, createTypes, generateTypeScriptType, getTypeName };
|
|
3
|
+
export { createModuleAugmentation, createTypes, formatRewriteRules, generateTypeScriptType, getTypeName };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/engine",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.0-canary.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Scans and builds Intlayer declaration files into dictionaries based on Intlayer configuration.",
|
|
6
6
|
"keywords": [
|
|
@@ -141,13 +141,13 @@
|
|
|
141
141
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
142
142
|
},
|
|
143
143
|
"dependencies": {
|
|
144
|
-
"@intlayer/api": "9.
|
|
145
|
-
"@intlayer/config": "9.
|
|
146
|
-
"@intlayer/core": "9.
|
|
147
|
-
"@intlayer/dictionaries-entry": "9.
|
|
148
|
-
"@intlayer/remote-dictionaries-entry": "9.
|
|
149
|
-
"@intlayer/types": "9.
|
|
150
|
-
"@intlayer/unmerged-dictionaries-entry": "9.
|
|
144
|
+
"@intlayer/api": "9.4.0-canary.0",
|
|
145
|
+
"@intlayer/config": "9.4.0-canary.0",
|
|
146
|
+
"@intlayer/core": "9.4.0-canary.0",
|
|
147
|
+
"@intlayer/dictionaries-entry": "9.4.0-canary.0",
|
|
148
|
+
"@intlayer/remote-dictionaries-entry": "9.4.0-canary.0",
|
|
149
|
+
"@intlayer/types": "9.4.0-canary.0",
|
|
150
|
+
"@intlayer/unmerged-dictionaries-entry": "9.4.0-canary.0",
|
|
151
151
|
"@parcel/watcher": "2.6.0",
|
|
152
152
|
"defu": "6.1.7",
|
|
153
153
|
"fast-glob": "3.3.3",
|
|
@@ -163,11 +163,11 @@
|
|
|
163
163
|
"rimraf": "6.1.3",
|
|
164
164
|
"tsdown": "0.22.14",
|
|
165
165
|
"typescript": "7.0.2",
|
|
166
|
-
"vitest": "4.1.
|
|
166
|
+
"vitest": "4.1.11",
|
|
167
167
|
"zod": "4.4.3"
|
|
168
168
|
},
|
|
169
169
|
"peerDependencies": {
|
|
170
|
-
"@intlayer/babel": "9.
|
|
170
|
+
"@intlayer/babel": "9.4.0-canary.0"
|
|
171
171
|
},
|
|
172
172
|
"peerDependenciesMeta": {
|
|
173
173
|
"@intlayer/babel": {
|