@intlayer/engine 9.3.1 → 9.3.3

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.
Files changed (39) hide show
  1. package/dist/cjs/build.cjs +1 -0
  2. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +3 -3
  3. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
  4. package/dist/cjs/cli.cjs +2 -0
  5. package/dist/cjs/createType/createModuleAugmentation.cjs +20 -1
  6. package/dist/cjs/createType/createModuleAugmentation.cjs.map +1 -1
  7. package/dist/cjs/createType/index.cjs +1 -0
  8. package/dist/cjs/init/index.cjs +60 -9
  9. package/dist/cjs/init/index.cjs.map +1 -1
  10. package/dist/cjs/init/utils/index.cjs +3 -0
  11. package/dist/cjs/init/utils/packageManager.cjs +101 -5
  12. package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
  13. package/dist/esm/build.mjs +2 -2
  14. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +4 -4
  15. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
  16. package/dist/esm/cli.mjs +2 -2
  17. package/dist/esm/createType/createModuleAugmentation.mjs +20 -2
  18. package/dist/esm/createType/createModuleAugmentation.mjs.map +1 -1
  19. package/dist/esm/createType/index.mjs +2 -2
  20. package/dist/esm/init/index.mjs +61 -10
  21. package/dist/esm/init/index.mjs.map +1 -1
  22. package/dist/esm/init/utils/index.mjs +2 -2
  23. package/dist/esm/init/utils/packageManager.mjs +99 -6
  24. package/dist/esm/init/utils/packageManager.mjs.map +1 -1
  25. package/dist/types/build.d.ts +2 -2
  26. package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts.map +1 -1
  27. package/dist/types/cli.d.ts +2 -2
  28. package/dist/types/createType/createModuleAugmentation.d.ts +12 -2
  29. package/dist/types/createType/createModuleAugmentation.d.ts.map +1 -1
  30. package/dist/types/createType/index.d.ts +2 -2
  31. package/dist/types/formatDictionary.d.ts +3 -2
  32. package/dist/types/formatDictionary.d.ts.map +1 -1
  33. package/dist/types/init/index.d.ts +2 -7
  34. package/dist/types/init/index.d.ts.map +1 -1
  35. package/dist/types/init/utils/index.d.ts +2 -2
  36. package/dist/types/init/utils/packageManager.d.ts +39 -6
  37. package/dist/types/init/utils/packageManager.d.ts.map +1 -1
  38. package/dist/types/intlayer/dist/types/index.d.ts +14 -0
  39. package/package.json +10 -10
@@ -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 = "json";
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 content = {\n${localeEntries}\n};\n\nexport default content;\n`;
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 content = {\n${marker},\n${localeEntries}\n};\n\nexport default content;\n`;
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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"}
package/dist/cjs/cli.cjs CHANGED
@@ -18,11 +18,13 @@ const require_installMCP_installMCP = require('./installMCP/installMCP.cjs');
18
18
  const require_listGitFiles = require('./listGitFiles.cjs');
19
19
  const require_logConfigDetails = require('./logConfigDetails.cjs');
20
20
 
21
+ exports.COMPAT_I18N_LIBRARIES = require_init_utils_packageManager.COMPAT_I18N_LIBRARIES;
21
22
  exports.NEXT_INTLAYER_BABEL_CONFIG_CONTENT = require_init_utils_nextCompilerBabel.NEXT_INTLAYER_BABEL_CONFIG_CONTENT;
22
23
  exports.PLATFORMS = require_installSkills_index.PLATFORMS;
23
24
  exports.PLATFORMS_METADATA = require_installSkills_index.PLATFORMS_METADATA;
24
25
  exports.SKILLS = require_installSkills_index.SKILLS;
25
26
  exports.SKILLS_METADATA = require_installSkills_index.SKILLS_METADATA;
27
+ exports.detectCompatI18nLibraries = require_init_utils_packageManager.detectCompatI18nLibraries;
26
28
  exports.detectExportedComponentName = require_writeContentDeclaration_detectExportedComponentName.detectExportedComponentName;
27
29
  exports.detectFormatCommand = require_detectFormatCommand.detectFormatCommand;
28
30
  exports.detectPackageManager = require_init_utils_packageManager.detectPackageManager;
@@ -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
- fileContent += ` interface __RoutingRegistry { mode: '${routingMode}'; defaultLocale: '${defaultLocale}' }\n`;
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;
@@ -46,6 +46,7 @@ const DocumentationRouter = {
46
46
  Fastify: "https://intlayer.org/doc/environment/fastify.md",
47
47
  Default: "https://intlayer.org/doc/get-started",
48
48
  LSP: "https://intlayer.org/doc/lsp.md",
49
+ ESLint: "https://intlayer.org/doc/eslint.md",
49
50
  NextIntl: "https://intlayer.org/blog/intlayer-with-next-intl.md",
50
51
  ReactI18Next: "https://intlayer.org/blog/intlayer-with-react-i18next.md",
51
52
  ReactIntl: "https://intlayer.org/blog/intlayer-with-react-intl.md",
@@ -53,6 +54,26 @@ const DocumentationRouter = {
53
54
  VueI18n: "https://intlayer.org/blog/intlayer-with-vue-i18n.md"
54
55
  };
55
56
  /**
57
+ * The Intlayer lint setup, as `intlayer init` writes it into `.oxlintrc.json`.
58
+ *
59
+ * Declared locally on purpose: `@intlayer/engine` is installed by every Intlayer
60
+ * user, so it must not depend on a lint plugin to write a handful of strings
61
+ * into a JSON file. oxlint resolves a plugin's *rules* but not its shipped
62
+ * *configs*, so the preset cannot be referenced by name there either.
63
+ *
64
+ * Mirrors the `recommended` preset of `eslint-plugin-intlayer`; keep in sync
65
+ * when a rule is enabled by default there.
66
+ */
67
+ const LINT_PLUGIN_PACKAGE_NAME = "eslint-plugin-intlayer";
68
+ /** Intlayer's generated output — never source, never worth linting. */
69
+ const GENERATED_OUTPUT_GLOBS = ["**/.intlayer/**"];
70
+ /** Rules of the `recommended` preset, prefixed by the plugin namespace. */
71
+ const getRecommendedRules = () => ({
72
+ "intlayer/no-raw-text": "warn",
73
+ "intlayer/static-dictionary-key": "error",
74
+ "intlayer/no-dynamic-field-access": "error"
75
+ });
76
+ /**
56
77
  * Helper: Detects the environment and returns the doc URL
57
78
  */
58
79
  const getDocumentationUrl = (packageJson) => {
@@ -143,18 +164,13 @@ const initIntlayer = async (rootDir, options) => {
143
164
  ...packageJson.dependencies ?? {},
144
165
  ...packageJson.devDependencies ?? {}
145
166
  };
146
- const effectiveAllDeps = {
147
- ...allDeps,
148
- ...options?.hintDependencies ?? {}
149
- };
150
167
  const packageManager = require_init_utils_packageManager.detectPackageManager(rootDir);
151
- const linguiPresent = Boolean(effectiveAllDeps["@lingui/core"] || effectiveAllDeps["@lingui/react"] || effectiveAllDeps["@intlayer/lingui"]);
168
+ const linguiPresent = Boolean(allDeps["@lingui/core"] || allDeps["@lingui/react"] || allDeps["@intlayer/lingui"]);
152
169
  const linguiCatalog = linguiPresent ? await require_init_utils_fileSystem.detectLinguiCatalogPattern(rootDir) : null;
153
- const hintLinguiJson = Boolean(options?.hintDependencies?.["__lingui_json__"]);
154
- const linguiCatalogFormat = linguiCatalog?.format ?? (linguiPresent ? hintLinguiJson ? "json" : "po" : null);
155
- const { packagesToInstall, devPackagesToInstall, compatSyncConfig, compatVitePluginConfig } = require_init_utils_packageManager.detectMissingIntlayerPackages(effectiveAllDeps, { linguiCatalogFormat });
170
+ const linguiCatalogFormat = linguiCatalog?.format ?? (linguiPresent ? "po" : null);
171
+ const { packagesToInstall, devPackagesToInstall, compatSyncConfig, compatVitePluginConfig } = require_init_utils_packageManager.detectMissingIntlayerPackages(allDeps, { linguiCatalogFormat });
156
172
  const wantsJsonNamespaces = options?.contentStrategy === "json-namespaces" && !compatSyncConfig;
157
- if (wantsJsonNamespaces && !effectiveAllDeps["@intlayer/sync-json-plugin"] && !devPackagesToInstall.includes("@intlayer/sync-json-plugin")) devPackagesToInstall.push("@intlayer/sync-json-plugin");
173
+ if (wantsJsonNamespaces && !allDeps["@intlayer/sync-json-plugin"] && !devPackagesToInstall.includes("@intlayer/sync-json-plugin")) devPackagesToInstall.push("@intlayer/sync-json-plugin");
158
174
  if (!options?.noInstallPackages) {
159
175
  const withVersion = (packages) => options?.upgradeToVersion ? packages.map((pkg) => `${pkg}@${options.upgradeToVersion}`) : packages;
160
176
  if (packagesToInstall.length > 0) {
@@ -258,6 +274,41 @@ const initIntlayer = async (rootDir, options) => {
258
274
  } catch {
259
275
  (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.x} Could not update ${(0, _intlayer_config_logger.colorizePath)(settingsJsonPath)}. You may need to add the LSP settings manually.`, { level: "warn" });
260
276
  }
277
+ if (!options?.noEslint && require_init_utils_packageManager.hasLintTooling(allDeps)) {
278
+ const oxlintConfigPath = ".oxlintrc.json";
279
+ if (await require_init_utils_fileSystem.exists(rootDir, oxlintConfigPath)) try {
280
+ const content = await require_init_utils_fileSystem.readFileFromRoot(rootDir, oxlintConfigPath);
281
+ const oxlintConfig = require_init_utils_jsonParser.parseJSONWithComments(content);
282
+ oxlintConfig.jsPlugins ??= [];
283
+ oxlintConfig.rules ??= {};
284
+ oxlintConfig.ignorePatterns ??= [];
285
+ let oxlintUpdated = false;
286
+ for (const glob of GENERATED_OUTPUT_GLOBS) if (!oxlintConfig.ignorePatterns.includes(glob)) {
287
+ oxlintConfig.ignorePatterns.push(glob);
288
+ oxlintUpdated = true;
289
+ }
290
+ if (!oxlintConfig.jsPlugins.includes(LINT_PLUGIN_PACKAGE_NAME)) {
291
+ oxlintConfig.jsPlugins.push(LINT_PLUGIN_PACKAGE_NAME);
292
+ oxlintUpdated = true;
293
+ }
294
+ for (const [ruleName, severity] of Object.entries(getRecommendedRules())) if (!oxlintConfig.rules[ruleName]) {
295
+ oxlintConfig.rules[ruleName] = severity;
296
+ oxlintUpdated = true;
297
+ }
298
+ if (oxlintUpdated) {
299
+ await require_init_utils_fileSystem.writeFileToRoot(rootDir, oxlintConfigPath, JSON.stringify(oxlintConfig, null, 2));
300
+ (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} Added the Intlayer lint rules to ${(0, _intlayer_config_logger.colorizePath)(oxlintConfigPath)}`);
301
+ } else (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} ${(0, _intlayer_config_logger.colorizePath)(oxlintConfigPath)} already includes the Intlayer lint rules`);
302
+ } catch {
303
+ (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.x} Could not update ${(0, _intlayer_config_logger.colorizePath)(oxlintConfigPath)}. You may need to add ${(0, _intlayer_config_logger.colorize)(LINT_PLUGIN_PACKAGE_NAME, _intlayer_config_colors.MAGENTA)} manually.`, { level: "warn" });
304
+ }
305
+ else (0, _intlayer_config_logger.logger)([
306
+ (0, _intlayer_config_logger.colorize)("Intlayer lint rules →", _intlayer_config_colors.MAGENTA),
307
+ (0, _intlayer_config_logger.colorize)(`Add ${LINT_PLUGIN_PACKAGE_NAME} to your ESLint flat config to catch hardcoded text and dynamic calls the compiler cannot optimize:`, _intlayer_config_colors.GREY_LIGHT),
308
+ (0, _intlayer_config_logger.colorize)(` import intlayer from '${LINT_PLUGIN_PACKAGE_NAME}';\n export default [...intlayer.configs.recommended];`, _intlayer_config_colors.GREY_LIGHT),
309
+ (0, _intlayer_config_logger.colorizePath)(DocumentationRouter.ESLint)
310
+ ]);
311
+ }
261
312
  const tsConfigFiles = await require_init_utils_tsConfig.findTsConfigFiles(rootDir);
262
313
  let hasTsConfig = false;
263
314
  for (const fileName of tsConfigFiles) if (await require_init_utils_fileSystem.exists(rootDir, fileName)) {