@intlayer/chokidar 9.0.0-canary.7 → 9.0.0-canary.9

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 (56) hide show
  1. package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
  2. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +3 -3
  3. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
  4. package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs.map +1 -1
  5. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs +6 -21
  6. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs.map +1 -1
  7. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +33 -20
  8. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
  9. package/dist/cjs/init/index.cjs +15 -5
  10. package/dist/cjs/init/index.cjs.map +1 -1
  11. package/dist/cjs/init/utils/astImports.cjs +56 -0
  12. package/dist/cjs/init/utils/astImports.cjs.map +1 -0
  13. package/dist/cjs/init/utils/configManipulation.cjs +28 -14
  14. package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
  15. package/dist/cjs/init/utils/fileSystem.cjs +50 -0
  16. package/dist/cjs/init/utils/fileSystem.cjs.map +1 -1
  17. package/dist/cjs/init/utils/index.cjs +5 -0
  18. package/dist/cjs/init/utils/packageManager.cjs +17 -8
  19. package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
  20. package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs.map +1 -1
  21. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +3 -3
  22. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
  23. package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs.map +1 -1
  24. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs +1 -16
  25. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs.map +1 -1
  26. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +29 -16
  27. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
  28. package/dist/esm/init/index.mjs +16 -6
  29. package/dist/esm/init/index.mjs.map +1 -1
  30. package/dist/esm/init/utils/astImports.mjs +51 -0
  31. package/dist/esm/init/utils/astImports.mjs.map +1 -0
  32. package/dist/esm/init/utils/configManipulation.mjs +28 -14
  33. package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
  34. package/dist/esm/init/utils/fileSystem.mjs +50 -1
  35. package/dist/esm/init/utils/fileSystem.mjs.map +1 -1
  36. package/dist/esm/init/utils/index.mjs +3 -2
  37. package/dist/esm/init/utils/packageManager.mjs +17 -8
  38. package/dist/esm/init/utils/packageManager.mjs.map +1 -1
  39. package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts +3 -3
  40. package/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts +1 -1
  41. package/dist/types/formatDictionary.d.ts +2 -4
  42. package/dist/types/formatDictionary.d.ts.map +1 -1
  43. package/dist/types/init/frameworkSetup/nextAppRouter/transforms.d.ts.map +1 -1
  44. package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts.map +1 -1
  45. package/dist/types/init/index.d.ts +7 -0
  46. package/dist/types/init/index.d.ts.map +1 -1
  47. package/dist/types/init/utils/astImports.d.ts +29 -0
  48. package/dist/types/init/utils/astImports.d.ts.map +1 -0
  49. package/dist/types/init/utils/configManipulation.d.ts +3 -3
  50. package/dist/types/init/utils/configManipulation.d.ts.map +1 -1
  51. package/dist/types/init/utils/fileSystem.d.ts +29 -1
  52. package/dist/types/init/utils/fileSystem.d.ts.map +1 -1
  53. package/dist/types/init/utils/index.d.ts +4 -3
  54. package/dist/types/init/utils/packageManager.d.ts +30 -3
  55. package/dist/types/init/utils/packageManager.d.ts.map +1 -1
  56. package/package.json +8 -8
@@ -1 +1 @@
1
- {"version":3,"file":"buildIntlayerDictionary.cjs","names":["OUTPUT_FORMAT","IMPORT_MODE","readDictionariesFromDisk","writeUnmergedDictionaries","writeMergedDictionaries","writeDynamicDictionary","writeDynamicQualifiedDictionaries","writeFetchDictionary"],"sources":["../../../src/buildIntlayerDictionary/buildIntlayerDictionary.ts"],"sourcesContent":["import { IMPORT_MODE, OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { isQualifiedDictionaryGroup } from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { readDictionariesFromDisk } from '../utils/readDictionariesFromDisk';\nimport {\n type LocalizedDictionaryOutput,\n type QualifiedMergedDictionaryOutput,\n writeDynamicDictionary,\n writeDynamicQualifiedDictionaries,\n} from './writeDynamicDictionary';\nimport { writeFetchDictionary } from './writeFetchDictionary';\nimport {\n type PlainMergedDictionaryOutput,\n writeMergedDictionaries,\n} from './writeMergedDictionary';\nimport { writeUnmergedDictionaries } from './writeUnmergedDictionary';\n\nexport type BuildDictionariesOptions = Partial<{\n formats: typeof OUTPUT_FORMAT;\n importOtherDictionaries: boolean;\n env: 'prod' | 'dev';\n}>;\n\nconst defaultOptions = {\n formats: OUTPUT_FORMAT,\n importOtherDictionaries: true,\n env: 'dev',\n} as const satisfies BuildDictionariesOptions;\n\n/**\n * This function transpile the bundled code to to make dictionaries as JSON files\n */\nexport const buildDictionary = async (\n localDictionariesEntries: Dictionary[],\n configuration: IntlayerConfig,\n options?: BuildDictionariesOptions\n) => {\n const importMode =\n configuration?.build?.importMode ??\n configuration?.dictionary?.importMode ??\n IMPORT_MODE;\n\n const { importOtherDictionaries, env, formats } = {\n ...defaultOptions,\n ...options,\n };\n\n const unmergedDictionariesToUpdate: Dictionary[] = [\n ...localDictionariesEntries,\n ];\n\n if (importOtherDictionaries) {\n const prevUnmergedDictionaries: Record<string, Dictionary[]> =\n readDictionariesFromDisk(configuration.system.unmergedDictionariesDir);\n\n // Reinsert other dictionaries with the same key to avoid merging errors\n for (const dictionaryToWrite of localDictionariesEntries) {\n const allPrebuiltUnmergedDictionaries =\n prevUnmergedDictionaries[dictionaryToWrite.key]!;\n\n if (allPrebuiltUnmergedDictionaries?.length > 0) {\n // Do not add the same dictionary again by filtering out the one with the same localId\n const otherUnmergedDictionaries =\n allPrebuiltUnmergedDictionaries?.filter(\n (unmergedDictionary) =>\n unmergedDictionary.localId !== dictionaryToWrite.localId\n );\n\n unmergedDictionariesToUpdate.push(...(otherUnmergedDictionaries ?? []));\n }\n }\n }\n\n const unmergedDictionaries = await writeUnmergedDictionaries(\n unmergedDictionariesToUpdate,\n configuration,\n env\n );\n\n const mergedDictionaries = await writeMergedDictionaries(\n unmergedDictionaries,\n configuration\n );\n\n const dictionariesToBuildDynamic: PlainMergedDictionaryOutput = {};\n const qualifiedDictionariesToBuildDynamic: QualifiedMergedDictionaryOutput =\n {};\n const keysToBuildFetch = new Set<string>();\n\n for (const [key, mergedResult] of Object.entries(mergedDictionaries)) {\n const dictionary = mergedResult.dictionary;\n\n if (isQualifiedDictionaryGroup(dictionary)) {\n // Collections / variants / meta records resolve their qualifier at\n // runtime from a selector. Static mode keeps every entry in one JSON;\n // dynamic mode splits them into one chunk per (locale, qualifierId).\n const entryMode = dictionary.importMode ?? importMode;\n\n if (entryMode === 'dynamic') {\n qualifiedDictionariesToBuildDynamic[key] = {\n dictionaryPath: mergedResult.dictionaryPath,\n dictionary,\n };\n } else if (entryMode === 'fetch') {\n const appLogger = getAppLogger(configuration);\n appLogger(\n `Dictionary ${colorizeKey(key)} uses 'fetch' import mode with (${dictionary.qualifierTypes.join(', ')}) entries — fetch mode is not qualifier-aware yet, falling back to static import.`,\n { level: 'warn' }\n );\n }\n\n continue;\n }\n\n const mode = dictionary.importMode ?? importMode;\n\n if (mode === 'dynamic' || mode === 'fetch') {\n dictionariesToBuildDynamic[key] = {\n dictionaryPath: mergedResult.dictionaryPath,\n dictionary,\n };\n }\n\n if (mode === 'fetch') {\n keysToBuildFetch.add(key);\n }\n }\n\n let dynamicDictionaries: LocalizedDictionaryOutput | null = null;\n\n if (Object.keys(dictionariesToBuildDynamic).length > 0) {\n dynamicDictionaries = await writeDynamicDictionary(\n dictionariesToBuildDynamic,\n configuration,\n formats\n );\n }\n\n if (Object.keys(qualifiedDictionariesToBuildDynamic).length > 0) {\n await writeDynamicQualifiedDictionaries(\n qualifiedDictionariesToBuildDynamic,\n configuration,\n formats\n );\n }\n\n let fetchDictionaries: LocalizedDictionaryOutput | null = null;\n\n if (dynamicDictionaries && keysToBuildFetch.size > 0) {\n const dictionariesToBuildFetch: LocalizedDictionaryOutput = {};\n\n for (const key of keysToBuildFetch) {\n if (dynamicDictionaries[key]) {\n dictionariesToBuildFetch[key] = dynamicDictionaries[key];\n }\n }\n\n if (Object.keys(dictionariesToBuildFetch).length > 0) {\n fetchDictionaries = await writeFetchDictionary(\n dictionariesToBuildFetch,\n configuration,\n formats\n );\n }\n }\n\n return {\n unmergedDictionaries,\n mergedDictionaries,\n dynamicDictionaries,\n fetchDictionaries,\n };\n};\n"],"mappings":";;;;;;;;;;;;AAyBA,MAAM,iBAAiB;CACrB,SAASA;CACT,yBAAyB;CACzB,KAAK;CACN;;;;AAKD,MAAa,kBAAkB,OAC7B,0BACA,eACA,YACG;CACH,MAAM,aACJ,eAAe,OAAO,cACtB,eAAe,YAAY,cAC3BC;CAEF,MAAM,EAAE,yBAAyB,KAAK,YAAY;EAChD,GAAG;EACH,GAAG;EACJ;CAED,MAAM,+BAA6C,CACjD,GAAG,yBACJ;AAED,KAAI,yBAAyB;EAC3B,MAAM,2BACJC,gEAAyB,cAAc,OAAO,wBAAwB;AAGxE,OAAK,MAAM,qBAAqB,0BAA0B;GACxD,MAAM,kCACJ,yBAAyB,kBAAkB;AAE7C,OAAI,iCAAiC,SAAS,GAAG;IAE/C,MAAM,4BACJ,iCAAiC,QAC9B,uBACC,mBAAmB,YAAY,kBAAkB,QACpD;AAEH,iCAA6B,KAAK,GAAI,6BAA6B,EAAE,CAAE;;;;CAK7E,MAAM,uBAAuB,MAAMC,kFACjC,8BACA,eACA,IACD;CAED,MAAM,qBAAqB,MAAMC,8EAC/B,sBACA,cACD;CAED,MAAM,6BAA0D,EAAE;CAClE,MAAM,sCACJ,EAAE;CACJ,MAAM,mCAAmB,IAAI,KAAa;AAE1C,MAAK,MAAM,CAAC,KAAK,iBAAiB,OAAO,QAAQ,mBAAmB,EAAE;EACpE,MAAM,aAAa,aAAa;AAEhC,2EAA+B,WAAW,EAAE;GAI1C,MAAM,YAAY,WAAW,cAAc;AAE3C,OAAI,cAAc,UAChB,qCAAoC,OAAO;IACzC,gBAAgB,aAAa;IAC7B;IACD;YACQ,cAAc,QAEvB,2CAD+B,cACtB,CACP,uDAA0B,IAAI,CAAC,kCAAkC,WAAW,eAAe,KAAK,KAAK,CAAC,oFACtG,EAAE,OAAO,QAAQ,CAClB;AAGH;;EAGF,MAAM,OAAO,WAAW,cAAc;AAEtC,MAAI,SAAS,aAAa,SAAS,QACjC,4BAA2B,OAAO;GAChC,gBAAgB,aAAa;GAC7B;GACD;AAGH,MAAI,SAAS,QACX,kBAAiB,IAAI,IAAI;;CAI7B,IAAI,sBAAwD;AAE5D,KAAI,OAAO,KAAK,2BAA2B,CAAC,SAAS,EACnD,uBAAsB,MAAMC,8EAC1B,4BACA,eACA,QACD;AAGH,KAAI,OAAO,KAAK,oCAAoC,CAAC,SAAS,EAC5D,OAAMC,yFACJ,qCACA,eACA,QACD;CAGH,IAAI,oBAAsD;AAE1D,KAAI,uBAAuB,iBAAiB,OAAO,GAAG;EACpD,MAAM,2BAAsD,EAAE;AAE9D,OAAK,MAAM,OAAO,iBAChB,KAAI,oBAAoB,KACtB,0BAAyB,OAAO,oBAAoB;AAIxD,MAAI,OAAO,KAAK,yBAAyB,CAAC,SAAS,EACjD,qBAAoB,MAAMC,0EACxB,0BACA,eACA,QACD;;AAIL,QAAO;EACL;EACA;EACA;EACA;EACD"}
1
+ {"version":3,"file":"buildIntlayerDictionary.cjs","names":["OUTPUT_FORMAT","IMPORT_MODE","readDictionariesFromDisk","writeUnmergedDictionaries","writeMergedDictionaries","writeDynamicDictionary","writeDynamicQualifiedDictionaries","writeFetchDictionary"],"sources":["../../../src/buildIntlayerDictionary/buildIntlayerDictionary.ts"],"sourcesContent":["import { IMPORT_MODE, OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { isQualifiedDictionaryGroup } from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { readDictionariesFromDisk } from '../utils/readDictionariesFromDisk';\nimport {\n type LocalizedDictionaryOutput,\n type QualifiedMergedDictionaryOutput,\n writeDynamicDictionary,\n writeDynamicQualifiedDictionaries,\n} from './writeDynamicDictionary';\nimport { writeFetchDictionary } from './writeFetchDictionary';\nimport {\n type PlainMergedDictionaryOutput,\n writeMergedDictionaries,\n} from './writeMergedDictionary';\nimport { writeUnmergedDictionaries } from './writeUnmergedDictionary';\n\nexport type BuildDictionariesOptions = Partial<{\n formats: typeof OUTPUT_FORMAT;\n importOtherDictionaries: boolean;\n env: 'prod' | 'dev';\n}>;\n\nconst defaultOptions = {\n formats: OUTPUT_FORMAT,\n importOtherDictionaries: true,\n env: 'dev',\n} as const satisfies BuildDictionariesOptions;\n\n/**\n * This function transpile the bundled code to to make dictionaries as JSON files\n */\nexport const buildDictionary = async (\n localDictionariesEntries: Dictionary[],\n configuration: IntlayerConfig,\n options?: BuildDictionariesOptions\n) => {\n const importMode =\n configuration?.build?.importMode ??\n configuration?.dictionary?.importMode ??\n IMPORT_MODE;\n\n const { importOtherDictionaries, env, formats } = {\n ...defaultOptions,\n ...options,\n };\n\n const unmergedDictionariesToUpdate: Dictionary[] = [\n ...localDictionariesEntries,\n ];\n\n if (importOtherDictionaries) {\n const prevUnmergedDictionaries: Record<string, Dictionary[]> =\n readDictionariesFromDisk(configuration.system.unmergedDictionariesDir);\n\n // Reinsert other dictionaries with the same key to avoid merging errors\n for (const dictionaryToWrite of localDictionariesEntries) {\n const allPrebuiltUnmergedDictionaries =\n prevUnmergedDictionaries[dictionaryToWrite.key]!;\n\n if (allPrebuiltUnmergedDictionaries?.length > 0) {\n // Do not add the same dictionary again by filtering out the one with the same localId\n const otherUnmergedDictionaries =\n allPrebuiltUnmergedDictionaries?.filter(\n (unmergedDictionary) =>\n unmergedDictionary.localId !== dictionaryToWrite.localId\n );\n\n unmergedDictionariesToUpdate.push(...(otherUnmergedDictionaries ?? []));\n }\n }\n }\n\n const unmergedDictionaries = await writeUnmergedDictionaries(\n unmergedDictionariesToUpdate,\n configuration,\n env\n );\n\n const mergedDictionaries = await writeMergedDictionaries(\n unmergedDictionaries,\n configuration\n );\n\n const dictionariesToBuildDynamic: PlainMergedDictionaryOutput = {};\n const qualifiedDictionariesToBuildDynamic: QualifiedMergedDictionaryOutput =\n {};\n const keysToBuildFetch = new Set<string>();\n\n for (const [key, mergedResult] of Object.entries(mergedDictionaries)) {\n const dictionary = mergedResult.dictionary;\n\n if (isQualifiedDictionaryGroup(dictionary)) {\n // Collections / variants resolve their qualifier at\n // runtime from a selector. Static mode keeps every entry in one JSON;\n // dynamic mode splits them into one chunk per (locale, qualifierId).\n const entryMode = dictionary.importMode ?? importMode;\n\n if (entryMode === 'dynamic') {\n qualifiedDictionariesToBuildDynamic[key] = {\n dictionaryPath: mergedResult.dictionaryPath,\n dictionary,\n };\n } else if (entryMode === 'fetch') {\n const appLogger = getAppLogger(configuration);\n appLogger(\n `Dictionary ${colorizeKey(key)} uses 'fetch' import mode with (${dictionary.qualifierTypes.join(', ')}) entries — fetch mode is not qualifier-aware yet, falling back to static import.`,\n { level: 'warn' }\n );\n }\n\n continue;\n }\n\n const mode = dictionary.importMode ?? importMode;\n\n if (mode === 'dynamic' || mode === 'fetch') {\n dictionariesToBuildDynamic[key] = {\n dictionaryPath: mergedResult.dictionaryPath,\n dictionary,\n };\n }\n\n if (mode === 'fetch') {\n keysToBuildFetch.add(key);\n }\n }\n\n let dynamicDictionaries: LocalizedDictionaryOutput | null = null;\n\n if (Object.keys(dictionariesToBuildDynamic).length > 0) {\n dynamicDictionaries = await writeDynamicDictionary(\n dictionariesToBuildDynamic,\n configuration,\n formats\n );\n }\n\n if (Object.keys(qualifiedDictionariesToBuildDynamic).length > 0) {\n await writeDynamicQualifiedDictionaries(\n qualifiedDictionariesToBuildDynamic,\n configuration,\n formats\n );\n }\n\n let fetchDictionaries: LocalizedDictionaryOutput | null = null;\n\n if (dynamicDictionaries && keysToBuildFetch.size > 0) {\n const dictionariesToBuildFetch: LocalizedDictionaryOutput = {};\n\n for (const key of keysToBuildFetch) {\n if (dynamicDictionaries[key]) {\n dictionariesToBuildFetch[key] = dynamicDictionaries[key];\n }\n }\n\n if (Object.keys(dictionariesToBuildFetch).length > 0) {\n fetchDictionaries = await writeFetchDictionary(\n dictionariesToBuildFetch,\n configuration,\n formats\n );\n }\n }\n\n return {\n unmergedDictionaries,\n mergedDictionaries,\n dynamicDictionaries,\n fetchDictionaries,\n };\n};\n"],"mappings":";;;;;;;;;;;;AAyBA,MAAM,iBAAiB;CACrB,SAASA;CACT,yBAAyB;CACzB,KAAK;CACN;;;;AAKD,MAAa,kBAAkB,OAC7B,0BACA,eACA,YACG;CACH,MAAM,aACJ,eAAe,OAAO,cACtB,eAAe,YAAY,cAC3BC;CAEF,MAAM,EAAE,yBAAyB,KAAK,YAAY;EAChD,GAAG;EACH,GAAG;EACJ;CAED,MAAM,+BAA6C,CACjD,GAAG,yBACJ;AAED,KAAI,yBAAyB;EAC3B,MAAM,2BACJC,gEAAyB,cAAc,OAAO,wBAAwB;AAGxE,OAAK,MAAM,qBAAqB,0BAA0B;GACxD,MAAM,kCACJ,yBAAyB,kBAAkB;AAE7C,OAAI,iCAAiC,SAAS,GAAG;IAE/C,MAAM,4BACJ,iCAAiC,QAC9B,uBACC,mBAAmB,YAAY,kBAAkB,QACpD;AAEH,iCAA6B,KAAK,GAAI,6BAA6B,EAAE,CAAE;;;;CAK7E,MAAM,uBAAuB,MAAMC,kFACjC,8BACA,eACA,IACD;CAED,MAAM,qBAAqB,MAAMC,8EAC/B,sBACA,cACD;CAED,MAAM,6BAA0D,EAAE;CAClE,MAAM,sCACJ,EAAE;CACJ,MAAM,mCAAmB,IAAI,KAAa;AAE1C,MAAK,MAAM,CAAC,KAAK,iBAAiB,OAAO,QAAQ,mBAAmB,EAAE;EACpE,MAAM,aAAa,aAAa;AAEhC,2EAA+B,WAAW,EAAE;GAI1C,MAAM,YAAY,WAAW,cAAc;AAE3C,OAAI,cAAc,UAChB,qCAAoC,OAAO;IACzC,gBAAgB,aAAa;IAC7B;IACD;YACQ,cAAc,QAEvB,2CAD+B,cACtB,CACP,uDAA0B,IAAI,CAAC,kCAAkC,WAAW,eAAe,KAAK,KAAK,CAAC,oFACtG,EAAE,OAAO,QAAQ,CAClB;AAGH;;EAGF,MAAM,OAAO,WAAW,cAAc;AAEtC,MAAI,SAAS,aAAa,SAAS,QACjC,4BAA2B,OAAO;GAChC,gBAAgB,aAAa;GAC7B;GACD;AAGH,MAAI,SAAS,QACX,kBAAiB,IAAI,IAAI;;CAI7B,IAAI,sBAAwD;AAE5D,KAAI,OAAO,KAAK,2BAA2B,CAAC,SAAS,EACnD,uBAAsB,MAAMC,8EAC1B,4BACA,eACA,QACD;AAGH,KAAI,OAAO,KAAK,oCAAoC,CAAC,SAAS,EAC5D,OAAMC,yFACJ,qCACA,eACA,QACD;CAGH,IAAI,oBAAsD;AAE1D,KAAI,uBAAuB,iBAAiB,OAAO,GAAG;EACpD,MAAM,2BAAsD,EAAE;AAE9D,OAAK,MAAM,OAAO,iBAChB,KAAI,oBAAoB,KACtB,0BAAyB,OAAO,oBAAoB;AAIxD,MAAI,OAAO,KAAK,yBAAyB,CAAC,SAAS,EACjD,qBAAoB,MAAMC,0EACxB,0BACA,eACA,QACD;;AAIL,QAAO;EACL;EACA;EACA;EACA;EACD"}
@@ -49,8 +49,8 @@ const serializeLoaderTree = (tree, indentLevel) => {
49
49
  }).join(",\n")}\n${pad}}`;
50
50
  };
51
51
  /**
52
- * Generates the entry point of a qualified dictionary (collection / variant /
53
- * meta record, possibly combined). Under each locale the loader map nests one
52
+ * Generates the entry point of a qualified dictionary (collection / variant,
53
+ * possibly combined). Under each locale the loader map nests one
54
54
  * level per declared dimension (canonical order) and carries a marker listing
55
55
  * those dimensions so the runtime can walk the tree.
56
56
  *
@@ -120,7 +120,7 @@ const writeDynamicDictionary = async (mergedDictionaries, configuration, formats
120
120
  };
121
121
  /**
122
122
  * Writes the dynamic chunks and entry points of qualified dictionaries
123
- * (collections, variants, meta records — possibly combined) in
123
+ * (collections, variants — possibly combined) in
124
124
  * `importMode: 'dynamic'`.
125
125
  *
126
126
  * Each entry is reduced to one per-locale chunk written to a path nested by
@@ -1 +1 @@
1
- {"version":3,"file":"writeDynamicDictionary.cjs","names":["QUALIFIER_DYNAMIC_TYPES_KEY","OUTPUT_FORMAT","parallelize","writeJsonIfChanged","writeFileIfChanged","COMPOSITE_ID_SEPARATOR"],"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 * Generates the content of a dictionary entry point file.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\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 format === 'esm'\n ? ` '${locale}': () => import('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json').then(m => m.default)`\n : ` '${locale}': () => Promise.resolve(require('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json'))`\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\nconst buildLoaderExpression = (\n key: string,\n segments: string[],\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = `./${DICTIONARIES_SUBDIR}/${key}/${segments.join('/')}/${locale}.json`;\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 entriesSegments: string[][],\n locale: string,\n format: 'cjs' | 'esm'\n): LoaderTree => {\n const root: LoaderTree = {};\n\n for (const segments of entriesSegments) {\n let node = root;\n\n segments.forEach((segment, index) => {\n if (index === segments.length - 1) {\n node[segment] = buildLoaderExpression(key, segments, locale, format);\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}'${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 * meta record, 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).\n */\nexport const generateQualifiedDictionaryEntryPoint = (\n key: string,\n qualifierTypes: DictionaryQualifierType[],\n entriesSegments: string[][],\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, entriesSegments, locale, format);\n return ` '${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): 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\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, meta records — 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 // Per-entry segment lists (one segment per declared dimension), reused for\n // both the chunk paths and the generated loader tree.\n const entriesSegments: string[][] = [];\n\n await parallelize(entryIds, async (entryId) => {\n // Rebuild a resolvable dictionary from the content node + composite id\n // so per-locale extraction sees the same `{ key, content, meta? }` shape.\n const entry = reconstructQualifiedEntry(group, entryId);\n\n const segments = entryId.split(COMPOSITE_ID_SEPARATOR);\n entriesSegments.push(segments);\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 entriesSegments,\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":["QUALIFIER_DYNAMIC_TYPES_KEY","OUTPUT_FORMAT","parallelize","writeJsonIfChanged","writeFileIfChanged","COMPOSITE_ID_SEPARATOR"],"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 * Generates the content of a dictionary entry point file.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\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 format === 'esm'\n ? ` '${locale}': () => import('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json').then(m => m.default)`\n : ` '${locale}': () => Promise.resolve(require('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json'))`\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\nconst buildLoaderExpression = (\n key: string,\n segments: string[],\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = `./${DICTIONARIES_SUBDIR}/${key}/${segments.join('/')}/${locale}.json`;\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 entriesSegments: string[][],\n locale: string,\n format: 'cjs' | 'esm'\n): LoaderTree => {\n const root: LoaderTree = {};\n\n for (const segments of entriesSegments) {\n let node = root;\n\n segments.forEach((segment, index) => {\n if (index === segments.length - 1) {\n node[segment] = buildLoaderExpression(key, segments, locale, format);\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}'${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).\n */\nexport const generateQualifiedDictionaryEntryPoint = (\n key: string,\n qualifierTypes: DictionaryQualifierType[],\n entriesSegments: string[][],\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, entriesSegments, locale, format);\n return ` '${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): 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\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 // Per-entry segment lists (one segment per declared dimension), reused for\n // both the chunk paths and the generated loader tree.\n const entriesSegments: string[][] = [];\n\n await parallelize(entryIds, async (entryId) => {\n // Rebuild a resolvable dictionary from the content node + composite id\n // so per-locale extraction sees the same `{ key, content, meta? }` shape.\n const entry = reconstructQualifiedEntry(group, entryId);\n\n const segments = entryId.split(COMPOSITE_ID_SEPARATOR);\n entriesSegments.push(segments);\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 entriesSegments,\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 +1 @@
1
- {"version":3,"file":"writeMergedDictionary.cjs","names":["parallelize","writeJsonIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeMergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n mergeQualifiedDictionaries,\n normalizeDictionaries,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { UnmergedDictionaryOutput } from './writeUnmergedDictionary';\n\nexport type MergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary | QualifiedDictionaryGroup;\n};\n\nexport type MergedDictionaryOutput = Record<string, MergedDictionaryResult>;\n\n/**\n * Merged output restricted to plain (unqualified) dictionaries — qualified\n * groups (collections, variants, meta records) are static-only and filtered\n * out before the dynamic/fetch build steps.\n */\nexport type PlainMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type PlainMergedDictionaryOutput = Record<\n string,\n PlainMergedDictionaryResult\n>;\n\n/**\n * Write the merged dictionaries to the dictionariesDir\n * @param groupedDictionaries - The grouped dictionaries\n * @param configuration - The configuration\n * @returns The merged 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/dictionary/home.json\n * // { key: 'home', content: { ... } },\n * ```\n */\nexport const writeMergedDictionaries = async (\n groupedDictionaries: UnmergedDictionaryOutput,\n configuration: IntlayerConfig\n): Promise<MergedDictionaryOutput> => {\n const { dictionariesDir } = configuration.system;\n\n // Create the dictionaries folder if it doesn't exist\n await mkdir(resolve(dictionariesDir), { recursive: true });\n\n const results = await parallelize(\n Object.entries(groupedDictionaries),\n async ([key, dictionariesEntry]) => {\n if (key === 'undefined') {\n return undefined as unknown as readonly [\n string,\n MergedDictionaryResult,\n ];\n }\n\n const normalizedDictionaries = normalizeDictionaries(\n dictionariesEntry.dictionaries,\n configuration\n );\n\n const mergedDictionary = mergeQualifiedDictionaries(\n normalizedDictionaries\n );\n\n const outputFileName = `${key}.json`;\n const resultFilePath = resolve(dictionariesDir, outputFileName);\n\n assertPathWithin(resultFilePath, dictionariesDir);\n\n // Write the merged dictionary\n await writeJsonIfChanged(resultFilePath, mergedDictionary).catch(\n (err) => {\n console.error(\n `Error creating merged ${colorizePath(resultFilePath)}:`,\n err\n );\n }\n );\n\n return [\n key,\n {\n dictionaryPath: resultFilePath,\n dictionary: mergedDictionary,\n } as MergedDictionaryResult,\n ] as const;\n }\n );\n\n return Object.fromEntries(\n results.filter(Boolean) as Array<readonly [string, MergedDictionaryResult]>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,MAAa,0BAA0B,OACrC,qBACA,kBACoC;CACpC,MAAM,EAAE,oBAAoB,cAAc;AAG1C,0DAAoB,gBAAgB,EAAE,EAAE,WAAW,MAAM,CAAC;CAE1D,MAAM,UAAU,MAAMA,sCACpB,OAAO,QAAQ,oBAAoB,EACnC,OAAO,CAAC,KAAK,uBAAuB;AAClC,MAAI,QAAQ,YACV;EAWF,MAAM,wJAJJ,kBAAkB,cAClB,cAIsB,CACvB;EAGD,MAAM,wCAAyB,iBAAiB,GADtB,IAAI,OACiC;AAE/D,+CAAiB,gBAAgB,gBAAgB;AAGjD,QAAMC,8CAAmB,gBAAgB,iBAAiB,CAAC,OACxD,QAAQ;AACP,WAAQ,MACN,mEAAsC,eAAe,CAAC,IACtD,IACD;IAEJ;AAED,SAAO,CACL,KACA;GACE,gBAAgB;GAChB,YAAY;GACb,CACF;GAEJ;AAED,QAAO,OAAO,YACZ,QAAQ,OAAO,QAAQ,CACxB"}
1
+ {"version":3,"file":"writeMergedDictionary.cjs","names":["parallelize","writeJsonIfChanged"],"sources":["../../../src/buildIntlayerDictionary/writeMergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n mergeQualifiedDictionaries,\n normalizeDictionaries,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { UnmergedDictionaryOutput } from './writeUnmergedDictionary';\n\nexport type MergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary | QualifiedDictionaryGroup;\n};\n\nexport type MergedDictionaryOutput = Record<string, MergedDictionaryResult>;\n\n/**\n * Merged output restricted to plain (unqualified) dictionaries — qualified\n * groups (collections, variants) are static-only and filtered\n * out before the dynamic/fetch build steps.\n */\nexport type PlainMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type PlainMergedDictionaryOutput = Record<\n string,\n PlainMergedDictionaryResult\n>;\n\n/**\n * Write the merged dictionaries to the dictionariesDir\n * @param groupedDictionaries - The grouped dictionaries\n * @param configuration - The configuration\n * @returns The merged 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/dictionary/home.json\n * // { key: 'home', content: { ... } },\n * ```\n */\nexport const writeMergedDictionaries = async (\n groupedDictionaries: UnmergedDictionaryOutput,\n configuration: IntlayerConfig\n): Promise<MergedDictionaryOutput> => {\n const { dictionariesDir } = configuration.system;\n\n // Create the dictionaries folder if it doesn't exist\n await mkdir(resolve(dictionariesDir), { recursive: true });\n\n const results = await parallelize(\n Object.entries(groupedDictionaries),\n async ([key, dictionariesEntry]) => {\n if (key === 'undefined') {\n return undefined as unknown as readonly [\n string,\n MergedDictionaryResult,\n ];\n }\n\n const normalizedDictionaries = normalizeDictionaries(\n dictionariesEntry.dictionaries,\n configuration\n );\n\n const mergedDictionary = mergeQualifiedDictionaries(\n normalizedDictionaries\n );\n\n const outputFileName = `${key}.json`;\n const resultFilePath = resolve(dictionariesDir, outputFileName);\n\n assertPathWithin(resultFilePath, dictionariesDir);\n\n // Write the merged dictionary\n await writeJsonIfChanged(resultFilePath, mergedDictionary).catch(\n (err) => {\n console.error(\n `Error creating merged ${colorizePath(resultFilePath)}:`,\n err\n );\n }\n );\n\n return [\n key,\n {\n dictionaryPath: resultFilePath,\n dictionary: mergedDictionary,\n } as MergedDictionaryResult,\n ] as const;\n }\n );\n\n return Object.fromEntries(\n results.filter(Boolean) as Array<readonly [string, MergedDictionaryResult]>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,MAAa,0BAA0B,OACrC,qBACA,kBACoC;CACpC,MAAM,EAAE,oBAAoB,cAAc;AAG1C,0DAAoB,gBAAgB,EAAE,EAAE,WAAW,MAAM,CAAC;CAE1D,MAAM,UAAU,MAAMA,sCACpB,OAAO,QAAQ,oBAAoB,EACnC,OAAO,CAAC,KAAK,uBAAuB;AAClC,MAAI,QAAQ,YACV;EAWF,MAAM,wJAJJ,kBAAkB,cAClB,cAIsB,CACvB;EAGD,MAAM,wCAAyB,iBAAiB,GADtB,IAAI,OACiC;AAE/D,+CAAiB,gBAAgB,gBAAgB;AAGjD,QAAMC,8CAAmB,gBAAgB,iBAAiB,CAAC,OACxD,QAAQ;AACP,WAAQ,MACN,mEAAsC,eAAe,CAAC,IACtD,IACD;IAEJ;AAED,SAAO,CACL,KACA;GACE,gBAAgB;GAChB,YAAY;GACb,CACF;GAEJ;AAED,QAAO,OAAO,YACZ,QAAQ,OAAO,QAAQ,CACxB"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
3
+ const require_init_utils_astImports = require('../../utils/astImports.cjs');
3
4
  let recast = require("recast");
4
5
  recast = require_runtime.__toESM(recast);
5
6
 
@@ -35,22 +36,6 @@ const findDefaultExportFunction = (ast) => {
35
36
  }
36
37
  return null;
37
38
  };
38
- /** Index just past the leading directives + import declarations, where new top-level code is safe to insert. */
39
- const firstInsertIndex = (ast) => {
40
- const body = ast.program.body;
41
- let index = 0;
42
- while (index < body.length && (body[index].type === "ExpressionStatement" && body[index].expression?.type === "StringLiteral" || body[index].type === "ImportDeclaration")) index++;
43
- return index;
44
- };
45
- /** Ensures `import { importName } from source`, merging into an existing import from the same source. */
46
- const ensureNamedImport = (ast, importName, source) => {
47
- for (const stmt of ast.program.body) if (stmt.type === "ImportDeclaration" && stmt.source.value === source) {
48
- if (!stmt.specifiers.some((spec) => spec.type === "ImportSpecifier" && spec.imported?.name === importName)) stmt.specifiers.push(b.importSpecifier(b.identifier(importName)));
49
- return;
50
- }
51
- const declaration = b.importDeclaration([b.importSpecifier(b.identifier(importName))], b.stringLiteral(source));
52
- ast.program.body.splice(firstInsertIndex(ast), 0, declaration);
53
- };
54
39
  /** Ensures `export { exportedName } from source`, skipping when already declared/re-exported. */
55
40
  const ensureExportFrom = (ast, exportedName, source) => {
56
41
  if (ast.program.body.some((stmt) => {
@@ -63,7 +48,7 @@ const ensureExportFrom = (ast, exportedName, source) => {
63
48
  return false;
64
49
  })) return;
65
50
  const exportNode = parseTsx(`export { ${exportedName} } from "${source}";`).program.body[0];
66
- ast.program.body.splice(firstInsertIndex(ast), 0, exportNode);
51
+ ast.program.body.splice(require_init_utils_astImports.firstInsertIndex(ast), 0, exportNode);
67
52
  };
68
53
  /** Makes the function async and inserts `const locale = await getLocale();` once, at the top of its body. */
69
54
  const ensureAwaitedLocale = (funcNode) => {
@@ -130,8 +115,8 @@ const wrapLayoutWithProvider = (code) => {
130
115
  code,
131
116
  status: "skipped"
132
117
  };
133
- ensureNamedImport(ast, "IntlayerClientProvider", "next-intlayer");
134
- ensureNamedImport(ast, "getLocale", "next-intlayer/server");
118
+ require_init_utils_astImports.ensureNamedImport(ast, "IntlayerClientProvider", "next-intlayer");
119
+ require_init_utils_astImports.ensureNamedImport(ast, "getLocale", "next-intlayer/server");
135
120
  ensureExportFrom(ast, "generateStaticParams", "next-intlayer");
136
121
  ensureAwaitedLocale(funcNode);
137
122
  setHtmlLang(ast);
@@ -196,8 +181,8 @@ const wrapPageWithProvider = (code) => {
196
181
  };
197
182
  if (jsxReturn.kind === "return") jsxReturn.node.argument = buildProviderElement("IntlayerServerProvider", jsxReturn.node.argument);
198
183
  else jsxReturn.node.body = buildProviderElement("IntlayerServerProvider", jsxReturn.node.body);
199
- ensureNamedImport(ast, "IntlayerServerProvider", "next-intlayer/server");
200
- ensureNamedImport(ast, "getLocale", "next-intlayer/server");
184
+ require_init_utils_astImports.ensureNamedImport(ast, "IntlayerServerProvider", "next-intlayer/server");
185
+ require_init_utils_astImports.ensureNamedImport(ast, "getLocale", "next-intlayer/server");
201
186
  ensureAwaitedLocale(funcNode);
202
187
  return {
203
188
  code: recast.print(ast).code,
@@ -1 +1 @@
1
- {"version":3,"file":"transforms.cjs","names":[],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/transforms.ts"],"sourcesContent":["import * as recast from 'recast';\n\nconst { builders: b } = recast.types;\n\n/** babel-ts parser handles TypeScript *and* JSX (the `typescript` parser does not). */\nconst parseTsx = (code: string): any =>\n recast.parse(code, { parser: require('recast/parsers/babel-ts') });\n\n/** Result of a source transform. `code` is unchanged for any non-`wrapped` status. */\nexport type TransformResult = {\n code: string;\n status: 'wrapped' | 'already' | 'skipped-client' | 'skipped';\n};\n\n/** Detects a top-level `'use client'` directive (client components can't be async server providers). */\nconst isClientComponent = (ast: any): boolean => {\n const directives = ast.program.directives ?? [];\n if (\n directives.some((directive: any) => directive.value?.value === 'use client')\n ) {\n return true;\n }\n return ast.program.body.some(\n (stmt: any) =>\n stmt.type === 'ExpressionStatement' &&\n stmt.expression?.type === 'StringLiteral' &&\n stmt.expression.value === 'use client'\n );\n};\n\n/** Finds the function node behind `export default`, following an identifier reference if needed. */\nconst findDefaultExportFunction = (ast: any): any => {\n const body = ast.program.body;\n\n const asFunction = (node: any): any =>\n node &&\n (node.type === 'ArrowFunctionExpression' ||\n node.type === 'FunctionExpression' ||\n node.type === 'FunctionDeclaration')\n ? node\n : null;\n\n for (const stmt of body) {\n if (stmt.type !== 'ExportDefaultDeclaration') continue;\n\n const direct = asFunction(stmt.declaration);\n if (direct) return direct;\n\n if (stmt.declaration?.type === 'Identifier') {\n const name = stmt.declaration.name;\n for (const candidate of body) {\n if (candidate.type === 'VariableDeclaration') {\n for (const declarator of candidate.declarations) {\n if (\n declarator.id?.type === 'Identifier' &&\n declarator.id.name === name\n ) {\n const fn = asFunction(declarator.init);\n if (fn) return fn;\n }\n }\n }\n if (\n candidate.type === 'FunctionDeclaration' &&\n candidate.id?.name === name\n ) {\n return candidate;\n }\n }\n }\n }\n\n return null;\n};\n\n/** Index just past the leading directives + import declarations, where new top-level code is safe to insert. */\nconst firstInsertIndex = (ast: any): number => {\n const body = ast.program.body;\n let index = 0;\n while (\n index < body.length &&\n ((body[index].type === 'ExpressionStatement' &&\n body[index].expression?.type === 'StringLiteral') ||\n body[index].type === 'ImportDeclaration')\n ) {\n index++;\n }\n return index;\n};\n\n/** Ensures `import { importName } from source`, merging into an existing import from the same source. */\nconst ensureNamedImport = (\n ast: any,\n importName: string,\n source: string\n): void => {\n for (const stmt of ast.program.body) {\n if (stmt.type === 'ImportDeclaration' && stmt.source.value === source) {\n const hasSpecifier = stmt.specifiers.some(\n (spec: any) =>\n spec.type === 'ImportSpecifier' && spec.imported?.name === importName\n );\n if (!hasSpecifier) {\n stmt.specifiers.push(b.importSpecifier(b.identifier(importName)));\n }\n return;\n }\n }\n\n const declaration = b.importDeclaration(\n [b.importSpecifier(b.identifier(importName))],\n b.stringLiteral(source)\n );\n ast.program.body.splice(firstInsertIndex(ast), 0, declaration);\n};\n\n/** Ensures `export { exportedName } from source`, skipping when already declared/re-exported. */\nconst ensureExportFrom = (\n ast: any,\n exportedName: string,\n source: string\n): void => {\n const body = ast.program.body;\n\n const alreadyPresent = body.some((stmt: any) => {\n if (\n stmt.type === 'ExportNamedDeclaration' &&\n stmt.source?.value === source\n ) {\n return stmt.specifiers.some(\n (spec: any) => spec.exported?.name === exportedName\n );\n }\n // Locally declared `export const/function generateStaticParams`\n if (stmt.type === 'ExportNamedDeclaration' && stmt.declaration) {\n const decl = stmt.declaration;\n if (decl.type === 'FunctionDeclaration' && decl.id?.name === exportedName)\n return true;\n if (decl.type === 'VariableDeclaration') {\n return decl.declarations.some(\n (d: any) => d.id?.type === 'Identifier' && d.id.name === exportedName\n );\n }\n }\n return false;\n });\n\n if (alreadyPresent) return;\n\n const exportNode = parseTsx(`export { ${exportedName} } from \"${source}\";`)\n .program.body[0];\n ast.program.body.splice(firstInsertIndex(ast), 0, exportNode);\n};\n\n/** Makes the function async and inserts `const locale = await getLocale();` once, at the top of its body. */\nconst ensureAwaitedLocale = (funcNode: any): void => {\n funcNode.async = true;\n\n if (funcNode.body.type !== 'BlockStatement') {\n funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);\n }\n\n const hasLocale = funcNode.body.body.some(\n (stmt: any) =>\n stmt.type === 'VariableDeclaration' &&\n stmt.declarations.some(\n (d: any) =>\n // const locale = ...\n (d.id?.type === 'Identifier' && d.id.name === 'locale') ||\n // const { locale } = ... or const { locale: locale } = ...\n (d.id?.type === 'ObjectPattern' &&\n d.id.properties?.some(\n (prop: any) =>\n prop.value?.type === 'Identifier' &&\n prop.value?.name === 'locale'\n ))\n )\n );\n\n if (!hasLocale) {\n const localeStatement = parseTsx('const locale = await getLocale();')\n .program.body[0];\n funcNode.body.body.unshift(localeStatement);\n }\n};\n\n/** Builds `<providerName locale={locale}>{child}</providerName>` around an existing JSX child node. */\nconst buildProviderElement = (providerName: string, childNode: any): any => {\n const template = parseTsx(\n `const __wrap = <${providerName} locale={locale}>{__child__}</${providerName}>;`\n );\n const providerElement = template.program.body[0].declarations[0].init;\n providerElement.children = [childNode];\n return providerElement;\n};\n\n/** Sets `lang={locale}` on the first `<html>` element, if present. */\nconst setHtmlLang = (ast: any): void => {\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'html') {\n const langAttr = node.attributes?.find(\n (attr: any) =>\n attr.type === 'JSXAttribute' && attr.name?.name === 'lang'\n ) as any;\n const localeExpression = b.jsxExpressionContainer(\n b.identifier('locale')\n );\n if (langAttr) {\n langAttr.value = localeExpression;\n } else {\n node.attributes.push(\n b.jsxAttribute(b.jsxIdentifier('lang'), localeExpression)\n );\n }\n return false;\n }\n this.traverse(path);\n },\n });\n};\n\n/**\n * Wraps the `{children}` of a Next.js App Router **layout** with\n * `IntlayerClientProvider`, deriving the locale via `getLocale()`. Safe and\n * idempotent: bails (returns the original code) for client components, when no\n * `{children}` placeholder is found, or when there is no default export.\n */\nexport const wrapLayoutWithProvider = (code: string): TransformResult => {\n const ast = parseTsx(code);\n\n if (isClientComponent(ast)) return { code, status: 'skipped-client' };\n if (code.includes('IntlayerClientProvider'))\n return { code, status: 'already' };\n\n const funcNode = findDefaultExportFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n let wrapped = false;\n recast.visit(funcNode, {\n visitJSXExpressionContainer(path) {\n if (wrapped) return false;\n const expression = path.node.expression;\n if (expression?.type === 'Identifier' && expression.name === 'children') {\n path.replace(buildProviderElement('IntlayerClientProvider', path.node));\n wrapped = true;\n return false;\n }\n this.traverse(path);\n },\n });\n\n if (!wrapped) return { code, status: 'skipped' };\n\n ensureNamedImport(ast, 'IntlayerClientProvider', 'next-intlayer');\n ensureNamedImport(ast, 'getLocale', 'next-intlayer/server');\n ensureExportFrom(ast, 'generateStaticParams', 'next-intlayer');\n ensureAwaitedLocale(funcNode);\n setHtmlLang(ast);\n\n return { code: recast.print(ast).code, status: 'wrapped' };\n};\n\n/** Returns the single top-level JSX return of a function, or the JSX of an expression-bodied arrow. */\nconst findSingleJsxReturn = (\n funcNode: any\n): { kind: 'return'; node: any } | { kind: 'arrow'; node: any } | null => {\n if (\n funcNode.type === 'ArrowFunctionExpression' &&\n funcNode.body.type !== 'BlockStatement'\n ) {\n const body =\n funcNode.body.type === 'JSXElement' ||\n funcNode.body.type === 'JSXFragment'\n ? funcNode.body\n : null;\n return body ? { kind: 'arrow', node: funcNode } : null;\n }\n\n if (funcNode.body.type !== 'BlockStatement') return null;\n\n // Collect every JSX-returning `return` inside this function, but do not\n // descend into nested function scopes (e.g. an inner component defined in the\n // same file). More than one top-level JSX return is ambiguous — bail.\n const jsxReturns: any[] = [];\n let functionDepth = 0;\n recast.visit(funcNode, {\n visitFunction(path) {\n functionDepth++;\n if (functionDepth === 1) {\n this.traverse(path);\n }\n functionDepth--;\n return false;\n },\n visitReturnStatement(path) {\n const argument = path.node.argument;\n if (argument?.type === 'JSXElement' || argument?.type === 'JSXFragment') {\n jsxReturns.push(path.node);\n }\n this.traverse(path);\n },\n });\n\n if (jsxReturns.length !== 1) return null;\n return { kind: 'return', node: jsxReturns[0] };\n};\n\n/**\n * Wraps the returned JSX of a Next.js App Router **page** with\n * `IntlayerServerProvider`, deriving the locale via `getLocale()`. Safe and\n * idempotent: bails for client components, when the page has no single\n * top-level JSX return, or when there is no default export.\n */\nexport const wrapPageWithProvider = (code: string): TransformResult => {\n const ast = parseTsx(code);\n\n if (isClientComponent(ast)) return { code, status: 'skipped-client' };\n if (code.includes('IntlayerServerProvider'))\n return { code, status: 'already' };\n\n const funcNode = findDefaultExportFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n const jsxReturn = findSingleJsxReturn(funcNode);\n if (!jsxReturn) return { code, status: 'skipped' };\n\n if (jsxReturn.kind === 'return') {\n jsxReturn.node.argument = buildProviderElement(\n 'IntlayerServerProvider',\n jsxReturn.node.argument\n );\n } else {\n jsxReturn.node.body = buildProviderElement(\n 'IntlayerServerProvider',\n jsxReturn.node.body\n );\n }\n\n ensureNamedImport(ast, 'IntlayerServerProvider', 'next-intlayer/server');\n ensureNamedImport(ast, 'getLocale', 'next-intlayer/server');\n ensureAwaitedLocale(funcNode);\n\n return { code: recast.print(ast).code, status: 'wrapped' };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"transforms.cjs","names":["firstInsertIndex"],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/transforms.ts"],"sourcesContent":["import * as recast from 'recast';\nimport { ensureNamedImport, firstInsertIndex } from '../../utils/astImports';\n\nconst { builders: b } = recast.types;\n\n/** babel-ts parser handles TypeScript *and* JSX (the `typescript` parser does not). */\nconst parseTsx = (code: string): any =>\n recast.parse(code, { parser: require('recast/parsers/babel-ts') });\n\n/** Result of a source transform. `code` is unchanged for any non-`wrapped` status. */\nexport type TransformResult = {\n code: string;\n status: 'wrapped' | 'already' | 'skipped-client' | 'skipped';\n};\n\n/** Detects a top-level `'use client'` directive (client components can't be async server providers). */\nconst isClientComponent = (ast: any): boolean => {\n const directives = ast.program.directives ?? [];\n if (\n directives.some((directive: any) => directive.value?.value === 'use client')\n ) {\n return true;\n }\n return ast.program.body.some(\n (stmt: any) =>\n stmt.type === 'ExpressionStatement' &&\n stmt.expression?.type === 'StringLiteral' &&\n stmt.expression.value === 'use client'\n );\n};\n\n/** Finds the function node behind `export default`, following an identifier reference if needed. */\nconst findDefaultExportFunction = (ast: any): any => {\n const body = ast.program.body;\n\n const asFunction = (node: any): any =>\n node &&\n (node.type === 'ArrowFunctionExpression' ||\n node.type === 'FunctionExpression' ||\n node.type === 'FunctionDeclaration')\n ? node\n : null;\n\n for (const stmt of body) {\n if (stmt.type !== 'ExportDefaultDeclaration') continue;\n\n const direct = asFunction(stmt.declaration);\n if (direct) return direct;\n\n if (stmt.declaration?.type === 'Identifier') {\n const name = stmt.declaration.name;\n for (const candidate of body) {\n if (candidate.type === 'VariableDeclaration') {\n for (const declarator of candidate.declarations) {\n if (\n declarator.id?.type === 'Identifier' &&\n declarator.id.name === name\n ) {\n const fn = asFunction(declarator.init);\n if (fn) return fn;\n }\n }\n }\n if (\n candidate.type === 'FunctionDeclaration' &&\n candidate.id?.name === name\n ) {\n return candidate;\n }\n }\n }\n }\n\n return null;\n};\n\n/** Ensures `export { exportedName } from source`, skipping when already declared/re-exported. */\nconst ensureExportFrom = (\n ast: any,\n exportedName: string,\n source: string\n): void => {\n const body = ast.program.body;\n\n const alreadyPresent = body.some((stmt: any) => {\n if (\n stmt.type === 'ExportNamedDeclaration' &&\n stmt.source?.value === source\n ) {\n return stmt.specifiers.some(\n (spec: any) => spec.exported?.name === exportedName\n );\n }\n // Locally declared `export const/function generateStaticParams`\n if (stmt.type === 'ExportNamedDeclaration' && stmt.declaration) {\n const decl = stmt.declaration;\n if (decl.type === 'FunctionDeclaration' && decl.id?.name === exportedName)\n return true;\n if (decl.type === 'VariableDeclaration') {\n return decl.declarations.some(\n (d: any) => d.id?.type === 'Identifier' && d.id.name === exportedName\n );\n }\n }\n return false;\n });\n\n if (alreadyPresent) return;\n\n const exportNode = parseTsx(`export { ${exportedName} } from \"${source}\";`)\n .program.body[0];\n ast.program.body.splice(firstInsertIndex(ast), 0, exportNode);\n};\n\n/** Makes the function async and inserts `const locale = await getLocale();` once, at the top of its body. */\nconst ensureAwaitedLocale = (funcNode: any): void => {\n funcNode.async = true;\n\n if (funcNode.body.type !== 'BlockStatement') {\n funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);\n }\n\n const hasLocale = funcNode.body.body.some(\n (stmt: any) =>\n stmt.type === 'VariableDeclaration' &&\n stmt.declarations.some(\n (d: any) =>\n // const locale = ...\n (d.id?.type === 'Identifier' && d.id.name === 'locale') ||\n // const { locale } = ... or const { locale: locale } = ...\n (d.id?.type === 'ObjectPattern' &&\n d.id.properties?.some(\n (prop: any) =>\n prop.value?.type === 'Identifier' &&\n prop.value?.name === 'locale'\n ))\n )\n );\n\n if (!hasLocale) {\n const localeStatement = parseTsx('const locale = await getLocale();')\n .program.body[0];\n funcNode.body.body.unshift(localeStatement);\n }\n};\n\n/** Builds `<providerName locale={locale}>{child}</providerName>` around an existing JSX child node. */\nconst buildProviderElement = (providerName: string, childNode: any): any => {\n const template = parseTsx(\n `const __wrap = <${providerName} locale={locale}>{__child__}</${providerName}>;`\n );\n const providerElement = template.program.body[0].declarations[0].init;\n providerElement.children = [childNode];\n return providerElement;\n};\n\n/** Sets `lang={locale}` on the first `<html>` element, if present. */\nconst setHtmlLang = (ast: any): void => {\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'html') {\n const langAttr = node.attributes?.find(\n (attr: any) =>\n attr.type === 'JSXAttribute' && attr.name?.name === 'lang'\n ) as any;\n const localeExpression = b.jsxExpressionContainer(\n b.identifier('locale')\n );\n if (langAttr) {\n langAttr.value = localeExpression;\n } else {\n node.attributes.push(\n b.jsxAttribute(b.jsxIdentifier('lang'), localeExpression)\n );\n }\n return false;\n }\n this.traverse(path);\n },\n });\n};\n\n/**\n * Wraps the `{children}` of a Next.js App Router **layout** with\n * `IntlayerClientProvider`, deriving the locale via `getLocale()`. Safe and\n * idempotent: bails (returns the original code) for client components, when no\n * `{children}` placeholder is found, or when there is no default export.\n */\nexport const wrapLayoutWithProvider = (code: string): TransformResult => {\n const ast = parseTsx(code);\n\n if (isClientComponent(ast)) return { code, status: 'skipped-client' };\n if (code.includes('IntlayerClientProvider'))\n return { code, status: 'already' };\n\n const funcNode = findDefaultExportFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n let wrapped = false;\n recast.visit(funcNode, {\n visitJSXExpressionContainer(path) {\n if (wrapped) return false;\n const expression = path.node.expression;\n if (expression?.type === 'Identifier' && expression.name === 'children') {\n path.replace(buildProviderElement('IntlayerClientProvider', path.node));\n wrapped = true;\n return false;\n }\n this.traverse(path);\n },\n });\n\n if (!wrapped) return { code, status: 'skipped' };\n\n ensureNamedImport(ast, 'IntlayerClientProvider', 'next-intlayer');\n ensureNamedImport(ast, 'getLocale', 'next-intlayer/server');\n ensureExportFrom(ast, 'generateStaticParams', 'next-intlayer');\n ensureAwaitedLocale(funcNode);\n setHtmlLang(ast);\n\n return { code: recast.print(ast).code, status: 'wrapped' };\n};\n\n/** Returns the single top-level JSX return of a function, or the JSX of an expression-bodied arrow. */\nconst findSingleJsxReturn = (\n funcNode: any\n): { kind: 'return'; node: any } | { kind: 'arrow'; node: any } | null => {\n if (\n funcNode.type === 'ArrowFunctionExpression' &&\n funcNode.body.type !== 'BlockStatement'\n ) {\n const body =\n funcNode.body.type === 'JSXElement' ||\n funcNode.body.type === 'JSXFragment'\n ? funcNode.body\n : null;\n return body ? { kind: 'arrow', node: funcNode } : null;\n }\n\n if (funcNode.body.type !== 'BlockStatement') return null;\n\n // Collect every JSX-returning `return` inside this function, but do not\n // descend into nested function scopes (e.g. an inner component defined in the\n // same file). More than one top-level JSX return is ambiguous — bail.\n const jsxReturns: any[] = [];\n let functionDepth = 0;\n recast.visit(funcNode, {\n visitFunction(path) {\n functionDepth++;\n if (functionDepth === 1) {\n this.traverse(path);\n }\n functionDepth--;\n return false;\n },\n visitReturnStatement(path) {\n const argument = path.node.argument;\n if (argument?.type === 'JSXElement' || argument?.type === 'JSXFragment') {\n jsxReturns.push(path.node);\n }\n this.traverse(path);\n },\n });\n\n if (jsxReturns.length !== 1) return null;\n return { kind: 'return', node: jsxReturns[0] };\n};\n\n/**\n * Wraps the returned JSX of a Next.js App Router **page** with\n * `IntlayerServerProvider`, deriving the locale via `getLocale()`. Safe and\n * idempotent: bails for client components, when the page has no single\n * top-level JSX return, or when there is no default export.\n */\nexport const wrapPageWithProvider = (code: string): TransformResult => {\n const ast = parseTsx(code);\n\n if (isClientComponent(ast)) return { code, status: 'skipped-client' };\n if (code.includes('IntlayerServerProvider'))\n return { code, status: 'already' };\n\n const funcNode = findDefaultExportFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n const jsxReturn = findSingleJsxReturn(funcNode);\n if (!jsxReturn) return { code, status: 'skipped' };\n\n if (jsxReturn.kind === 'return') {\n jsxReturn.node.argument = buildProviderElement(\n 'IntlayerServerProvider',\n jsxReturn.node.argument\n );\n } else {\n jsxReturn.node.body = buildProviderElement(\n 'IntlayerServerProvider',\n jsxReturn.node.body\n );\n }\n\n ensureNamedImport(ast, 'IntlayerServerProvider', 'next-intlayer/server');\n ensureNamedImport(ast, 'getLocale', 'next-intlayer/server');\n ensureAwaitedLocale(funcNode);\n\n return { code: recast.print(ast).code, status: 'wrapped' };\n};\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
3
+ const require_init_utils_astImports = require('../../utils/astImports.cjs');
3
4
  let recast = require("recast");
4
5
  recast = require_runtime.__toESM(recast);
5
6
 
@@ -7,21 +8,6 @@ recast = require_runtime.__toESM(recast);
7
8
  const { builders: b, namedTypes: n } = recast.types;
8
9
  /** babel-ts parser handles TypeScript *and* JSX. */
9
10
  const parseTsx = (code) => recast.parse(code, { parser: require("recast/parsers/babel-ts") });
10
- /** Index just past the leading directives + import declarations. */
11
- const firstInsertIndex = (ast) => {
12
- const body = ast.program.body;
13
- let index = 0;
14
- while (index < body.length && (body[index].type === "ExpressionStatement" && body[index].expression?.type === "StringLiteral" || body[index].type === "ImportDeclaration")) index++;
15
- return index;
16
- };
17
- /** Ensures `import { importName } from source`, merging into an existing import from the same source. */
18
- const ensureNamedImport = (ast, importName, source) => {
19
- for (const stmt of ast.program.body) if (stmt.type === "ImportDeclaration" && stmt.source.value === source) {
20
- if (!stmt.specifiers.some((spec) => spec.type === "ImportSpecifier" && spec.imported?.name === importName)) stmt.specifiers.push(b.importSpecifier(b.identifier(importName)));
21
- return;
22
- }
23
- ast.program.body.splice(firstInsertIndex(ast), 0, b.importDeclaration([b.importSpecifier(b.identifier(importName))], b.stringLiteral(source)));
24
- };
25
11
  /**
26
12
  * Finds the single function that returns the `<html>` document element. Returns
27
13
  * `null` when there is no such function, or more than one (ambiguous).
@@ -46,15 +32,42 @@ const findHtmlDocumentFunction = (ast) => {
46
32
  return found.size === 1 ? [...found][0] : null;
47
33
  };
48
34
  /**
35
+ * Whether the binding pattern `node` introduces a variable named `name`.
36
+ * Recurses through destructuring patterns so all of `const locale`,
37
+ * `const { locale } = …`, `const { locale = fallback } = …`,
38
+ * `const { foo: locale } = …`, `const [locale] = …` and `...locale` are
39
+ * detected — not just a bare identifier.
40
+ */
41
+ const patternBindsName = (node, name) => {
42
+ if (!node) return false;
43
+ switch (node.type) {
44
+ case "Identifier": return node.name === name;
45
+ case "AssignmentPattern": return patternBindsName(node.left, name);
46
+ case "RestElement": return patternBindsName(node.argument, name);
47
+ case "ArrayPattern": return node.elements.some((element) => patternBindsName(element, name));
48
+ case "ObjectPattern": return node.properties.some((property) => property.type === "RestElement" ? patternBindsName(property.argument, name) : patternBindsName(property.value, name));
49
+ default: return false;
50
+ }
51
+ };
52
+ /**
49
53
  * Inserts `const locale = …` once, at the top of the function body. A single
50
54
  * self-contained statement is used (rather than an intermediate `params` const)
51
55
  * so it can never collide with a `params` variable the document already
52
56
  * declares. Expression-bodied arrow functions are first converted to a block so
53
57
  * the declaration has somewhere to live.
58
+ *
59
+ * No-ops when `locale` is already bound in the function — whether as a simple
60
+ * identifier, through a destructuring pattern (e.g.
61
+ * `const { locale = defaultLocale } = useLoaderData()`), or as a function
62
+ * parameter — so the injected declaration never collides with one the document
63
+ * already provides (which would produce an
64
+ * `Identifier 'locale' has already been declared` parse error).
54
65
  */
55
66
  const ensureLocaleFromParams = (funcNode) => {
56
67
  if (funcNode.body?.type !== "BlockStatement") funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);
57
- if (funcNode.body.body.some((stmt) => stmt.type === "VariableDeclaration" && stmt.declarations.some((d) => d.id?.type === "Identifier" && d.id.name === "locale"))) return;
68
+ const declaredInBody = funcNode.body.body.some((stmt) => stmt.type === "VariableDeclaration" && stmt.declarations.some((d) => patternBindsName(d.id, "locale")));
69
+ const declaredInParams = (funcNode.params ?? []).some((param) => patternBindsName(param, "locale"));
70
+ if (declaredInBody || declaredInParams) return;
58
71
  const statement = parseTsx("const locale = useParams({ strict: false }).locale ?? defaultLocale;").program.body[0];
59
72
  funcNode.body.body.unshift(statement);
60
73
  };
@@ -111,10 +124,10 @@ const wrapRootWithProvider = (code) => {
111
124
  code,
112
125
  status: "skipped"
113
126
  };
114
- ensureNamedImport(ast, "IntlayerProvider", "react-intlayer");
115
- ensureNamedImport(ast, "useParams", "@tanstack/react-router");
116
- ensureNamedImport(ast, "defaultLocale", "intlayer");
117
- ensureNamedImport(ast, "getHTMLTextDir", "intlayer");
127
+ require_init_utils_astImports.ensureNamedImport(ast, "IntlayerProvider", "react-intlayer");
128
+ require_init_utils_astImports.ensureNamedImport(ast, "useParams", "@tanstack/react-router");
129
+ require_init_utils_astImports.ensureNamedImport(ast, "defaultLocale", "intlayer");
130
+ require_init_utils_astImports.ensureNamedImport(ast, "getHTMLTextDir", "intlayer");
118
131
  ensureLocaleFromParams(funcNode);
119
132
  setHtmlLangAndDir(ast);
120
133
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"transforms.cjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/transforms.ts"],"sourcesContent":["import * as recast from 'recast';\n\nconst { builders: b, namedTypes: n } = recast.types;\n\n/** babel-ts parser handles TypeScript *and* JSX. */\nconst parseTsx = (code: string): any =>\n recast.parse(code, { parser: require('recast/parsers/babel-ts') });\n\n/** Result of a source transform. `code` is unchanged for any non-`wrapped` status. */\nexport type TransformResult = {\n code: string;\n status: 'wrapped' | 'already' | 'skipped';\n};\n\n/** Index just past the leading directives + import declarations. */\nconst firstInsertIndex = (ast: any): number => {\n const body = ast.program.body;\n let index = 0;\n while (\n index < body.length &&\n ((body[index].type === 'ExpressionStatement' &&\n body[index].expression?.type === 'StringLiteral') ||\n body[index].type === 'ImportDeclaration')\n ) {\n index++;\n }\n return index;\n};\n\n/** Ensures `import { importName } from source`, merging into an existing import from the same source. */\nconst ensureNamedImport = (\n ast: any,\n importName: string,\n source: string\n): void => {\n for (const stmt of ast.program.body) {\n if (stmt.type === 'ImportDeclaration' && stmt.source.value === source) {\n const hasSpecifier = stmt.specifiers.some(\n (spec: any) =>\n spec.type === 'ImportSpecifier' && spec.imported?.name === importName\n );\n if (!hasSpecifier) {\n stmt.specifiers.push(b.importSpecifier(b.identifier(importName)));\n }\n return;\n }\n }\n\n ast.program.body.splice(\n firstInsertIndex(ast),\n 0,\n b.importDeclaration(\n [b.importSpecifier(b.identifier(importName))],\n b.stringLiteral(source)\n )\n );\n};\n\n/**\n * Finds the single function that returns the `<html>` document element. Returns\n * `null` when there is no such function, or more than one (ambiguous).\n */\nconst findHtmlDocumentFunction = (ast: any): any => {\n const found = new Set<any>();\n\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'html') {\n // Walk up to the nearest enclosing function node.\n let current = path.parentPath;\n while (current) {\n const value = current.value;\n if (\n value &&\n (n.FunctionDeclaration.check(value) ||\n n.FunctionExpression.check(value) ||\n n.ArrowFunctionExpression.check(value))\n ) {\n found.add(value);\n break;\n }\n current = current.parentPath;\n }\n }\n this.traverse(path);\n },\n });\n\n return found.size === 1 ? [...found][0] : null;\n};\n\n/**\n * Inserts `const locale = …` once, at the top of the function body. A single\n * self-contained statement is used (rather than an intermediate `params` const)\n * so it can never collide with a `params` variable the document already\n * declares. Expression-bodied arrow functions are first converted to a block so\n * the declaration has somewhere to live.\n */\nconst ensureLocaleFromParams = (funcNode: any): void => {\n if (funcNode.body?.type !== 'BlockStatement') {\n funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);\n }\n\n const hasLocale = funcNode.body.body.some(\n (stmt: any) =>\n stmt.type === 'VariableDeclaration' &&\n stmt.declarations.some(\n (d: any) => d.id?.type === 'Identifier' && d.id.name === 'locale'\n )\n );\n if (hasLocale) return;\n\n const statement = parseTsx(\n 'const locale = useParams({ strict: false }).locale ?? defaultLocale;'\n ).program.body[0];\n funcNode.body.body.unshift(statement);\n};\n\n/** Sets `lang={locale}` and `dir={getHTMLTextDir(locale)}` on the first `<html>` element. */\nconst setHtmlLangAndDir = (ast: any): void => {\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'html') {\n this.traverse(path);\n return;\n }\n\n const upsertAttribute = (name: string, value: any): void => {\n const existing = node.attributes?.find(\n (attr: any) =>\n attr.type === 'JSXAttribute' && attr.name?.name === name\n ) as any;\n if (existing) {\n existing.value = value;\n } else {\n node.attributes.push(b.jsxAttribute(b.jsxIdentifier(name), value));\n }\n };\n\n upsertAttribute('lang', b.jsxExpressionContainer(b.identifier('locale')));\n upsertAttribute(\n 'dir',\n b.jsxExpressionContainer(\n b.callExpression(b.identifier('getHTMLTextDir'), [\n b.identifier('locale'),\n ])\n )\n );\n return false;\n },\n });\n};\n\n/**\n * Wraps the `{children}` of a TanStack Start root document with\n * `IntlayerProvider`, deriving the locale from the `{-$locale}` route param.\n * Safe and idempotent: bails (returns the original code) when the provider is\n * already present, when no `<html>` document function is found, or when there is\n * no `{children}` placeholder to wrap.\n */\nexport const wrapRootWithProvider = (code: string): TransformResult => {\n const ast = parseTsx(code);\n\n if (code.includes('IntlayerProvider')) return { code, status: 'already' };\n\n const funcNode = findHtmlDocumentFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n let wrapped = false;\n recast.visit(funcNode, {\n visitJSXExpressionContainer(path) {\n if (wrapped) return false;\n const expression = path.node.expression;\n if (expression?.type === 'Identifier' && expression.name === 'children') {\n const template = parseTsx(\n 'const __wrap = <IntlayerProvider locale={locale}>{__child__}</IntlayerProvider>;'\n );\n const providerElement = template.program.body[0].declarations[0].init;\n providerElement.children = [path.node];\n path.replace(providerElement);\n wrapped = true;\n return false;\n }\n this.traverse(path);\n },\n });\n\n if (!wrapped) return { code, status: 'skipped' };\n\n ensureNamedImport(ast, 'IntlayerProvider', 'react-intlayer');\n ensureNamedImport(ast, 'useParams', '@tanstack/react-router');\n ensureNamedImport(ast, 'defaultLocale', 'intlayer');\n ensureNamedImport(ast, 'getHTMLTextDir', 'intlayer');\n ensureLocaleFromParams(funcNode);\n setHtmlLangAndDir(ast);\n\n return { code: recast.print(ast).code, status: 'wrapped' };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"transforms.cjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/transforms.ts"],"sourcesContent":["import * as recast from 'recast';\nimport { ensureNamedImport } from '../../utils/astImports';\n\nconst { builders: b, namedTypes: n } = recast.types;\n\n/** babel-ts parser handles TypeScript *and* JSX. */\nconst parseTsx = (code: string): any =>\n recast.parse(code, { parser: require('recast/parsers/babel-ts') });\n\n/** Result of a source transform. `code` is unchanged for any non-`wrapped` status. */\nexport type TransformResult = {\n code: string;\n status: 'wrapped' | 'already' | 'skipped';\n};\n\n/**\n * Finds the single function that returns the `<html>` document element. Returns\n * `null` when there is no such function, or more than one (ambiguous).\n */\nconst findHtmlDocumentFunction = (ast: any): any => {\n const found = new Set<any>();\n\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type === 'JSXIdentifier' && node.name.name === 'html') {\n // Walk up to the nearest enclosing function node.\n let current = path.parentPath;\n while (current) {\n const value = current.value;\n if (\n value &&\n (n.FunctionDeclaration.check(value) ||\n n.FunctionExpression.check(value) ||\n n.ArrowFunctionExpression.check(value))\n ) {\n found.add(value);\n break;\n }\n current = current.parentPath;\n }\n }\n this.traverse(path);\n },\n });\n\n return found.size === 1 ? [...found][0] : null;\n};\n\n/**\n * Whether the binding pattern `node` introduces a variable named `name`.\n * Recurses through destructuring patterns so all of `const locale`,\n * `const { locale } = …`, `const { locale = fallback } = …`,\n * `const { foo: locale } = …`, `const [locale] = …` and `...locale` are\n * detected — not just a bare identifier.\n */\nconst patternBindsName = (node: any, name: string): boolean => {\n if (!node) return false;\n switch (node.type) {\n case 'Identifier':\n return node.name === name;\n case 'AssignmentPattern':\n // `locale = fallback`\n return patternBindsName(node.left, name);\n case 'RestElement':\n // `...locale`\n return patternBindsName(node.argument, name);\n case 'ArrayPattern':\n return node.elements.some((element: any) =>\n patternBindsName(element, name)\n );\n case 'ObjectPattern':\n return node.properties.some((property: any) =>\n property.type === 'RestElement'\n ? patternBindsName(property.argument, name)\n : // The *value* is the binding target (`{ foo: locale }` binds `locale`).\n patternBindsName(property.value, name)\n );\n default:\n return false;\n }\n};\n\n/**\n * Inserts `const locale = …` once, at the top of the function body. A single\n * self-contained statement is used (rather than an intermediate `params` const)\n * so it can never collide with a `params` variable the document already\n * declares. Expression-bodied arrow functions are first converted to a block so\n * the declaration has somewhere to live.\n *\n * No-ops when `locale` is already bound in the function — whether as a simple\n * identifier, through a destructuring pattern (e.g.\n * `const { locale = defaultLocale } = useLoaderData()`), or as a function\n * parameter — so the injected declaration never collides with one the document\n * already provides (which would produce an\n * `Identifier 'locale' has already been declared` parse error).\n */\nconst ensureLocaleFromParams = (funcNode: any): void => {\n if (funcNode.body?.type !== 'BlockStatement') {\n funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);\n }\n\n const declaredInBody = funcNode.body.body.some(\n (stmt: any) =>\n stmt.type === 'VariableDeclaration' &&\n stmt.declarations.some((d: any) => patternBindsName(d.id, 'locale'))\n );\n const declaredInParams = (funcNode.params ?? []).some((param: any) =>\n patternBindsName(param, 'locale')\n );\n if (declaredInBody || declaredInParams) return;\n\n const statement = parseTsx(\n 'const locale = useParams({ strict: false }).locale ?? defaultLocale;'\n ).program.body[0];\n funcNode.body.body.unshift(statement);\n};\n\n/** Sets `lang={locale}` and `dir={getHTMLTextDir(locale)}` on the first `<html>` element. */\nconst setHtmlLangAndDir = (ast: any): void => {\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n if (node.name?.type !== 'JSXIdentifier' || node.name.name !== 'html') {\n this.traverse(path);\n return;\n }\n\n const upsertAttribute = (name: string, value: any): void => {\n const existing = node.attributes?.find(\n (attr: any) =>\n attr.type === 'JSXAttribute' && attr.name?.name === name\n ) as any;\n if (existing) {\n existing.value = value;\n } else {\n node.attributes.push(b.jsxAttribute(b.jsxIdentifier(name), value));\n }\n };\n\n upsertAttribute('lang', b.jsxExpressionContainer(b.identifier('locale')));\n upsertAttribute(\n 'dir',\n b.jsxExpressionContainer(\n b.callExpression(b.identifier('getHTMLTextDir'), [\n b.identifier('locale'),\n ])\n )\n );\n return false;\n },\n });\n};\n\n/**\n * Wraps the `{children}` of a TanStack Start root document with\n * `IntlayerProvider`, deriving the locale from the `{-$locale}` route param.\n * Safe and idempotent: bails (returns the original code) when the provider is\n * already present, when no `<html>` document function is found, or when there is\n * no `{children}` placeholder to wrap.\n */\nexport const wrapRootWithProvider = (code: string): TransformResult => {\n const ast = parseTsx(code);\n\n if (code.includes('IntlayerProvider')) return { code, status: 'already' };\n\n const funcNode = findHtmlDocumentFunction(ast);\n if (!funcNode) return { code, status: 'skipped' };\n\n let wrapped = false;\n recast.visit(funcNode, {\n visitJSXExpressionContainer(path) {\n if (wrapped) return false;\n const expression = path.node.expression;\n if (expression?.type === 'Identifier' && expression.name === 'children') {\n const template = parseTsx(\n 'const __wrap = <IntlayerProvider locale={locale}>{__child__}</IntlayerProvider>;'\n );\n const providerElement = template.program.body[0].declarations[0].init;\n providerElement.children = [path.node];\n path.replace(providerElement);\n wrapped = true;\n return false;\n }\n this.traverse(path);\n },\n });\n\n if (!wrapped) return { code, status: 'skipped' };\n\n ensureNamedImport(ast, 'IntlayerProvider', 'react-intlayer');\n ensureNamedImport(ast, 'useParams', '@tanstack/react-router');\n ensureNamedImport(ast, 'defaultLocale', 'intlayer');\n ensureNamedImport(ast, 'getHTMLTextDir', 'intlayer');\n ensureLocaleFromParams(funcNode);\n setHtmlLangAndDir(ast);\n\n return { code: recast.print(ast).code, status: 'wrapped' };\n};\n"],"mappings":"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"}
@@ -126,8 +126,15 @@ const initIntlayer = async (rootDir, options) => {
126
126
  ...packageJson.dependencies ?? {},
127
127
  ...packageJson.devDependencies ?? {}
128
128
  };
129
+ const effectiveAllDeps = {
130
+ ...allDeps,
131
+ ...options?.hintDependencies ?? {}
132
+ };
129
133
  const packageManager = require_init_utils_packageManager.detectPackageManager(rootDir);
130
- const { packagesToInstall, devPackagesToInstall, compatSyncConfig, compatVitePluginConfig } = require_init_utils_packageManager.detectMissingIntlayerPackages(allDeps);
134
+ const linguiPresent = Boolean(effectiveAllDeps["@lingui/core"] || effectiveAllDeps["@lingui/react"] || effectiveAllDeps["@intlayer/lingui"]);
135
+ const linguiCatalog = linguiPresent ? await require_init_utils_fileSystem.detectLinguiCatalogPattern(rootDir) : null;
136
+ const hintLinguiJson = Boolean(options?.hintDependencies?.["__lingui_json__"]);
137
+ const { packagesToInstall, devPackagesToInstall, compatSyncConfig, compatVitePluginConfig } = require_init_utils_packageManager.detectMissingIntlayerPackages(effectiveAllDeps, { linguiCatalogFormat: linguiCatalog?.format ?? (linguiPresent ? hintLinguiJson ? "json" : "po" : null) });
131
138
  if (!options?.noInstallPackages) {
132
139
  const withVersion = (packages) => options?.upgradeToVersion ? packages.map((pkg) => `${pkg}@${options.upgradeToVersion}`) : packages;
133
140
  if (packagesToInstall.length > 0) {
@@ -249,11 +256,14 @@ const initIntlayer = async (rootDir, options) => {
249
256
  const detectedPattern = await require_init_utils_fileSystem.detectJsonLocalePattern(rootDir);
250
257
  await require_initConfig_index.initConfig(format, rootDir, detectedPattern?.locales);
251
258
  if (compatSyncConfig) {
252
- const sourceTemplate = (allDeps["next-intl"] || allDeps["@intlayer/next-intl"] || allDeps["use-intl"] || allDeps["@intlayer/use-intl"] ? await require_init_utils_fileSystem.detectNextIntlMessagesPattern(rootDir) : null)?.template ?? detectedPattern?.template;
259
+ const nextIntlMessagesPattern = allDeps["next-intl"] || allDeps["@intlayer/next-intl"] || allDeps["use-intl"] || allDeps["@intlayer/use-intl"] ? await require_init_utils_fileSystem.detectNextIntlMessagesPattern(rootDir) : null;
260
+ const sourceTemplate = linguiCatalog?.template ?? nextIntlMessagesPattern?.template ?? detectedPattern?.template;
253
261
  const resolvedSyncConfig = {
254
262
  ...compatSyncConfig,
255
263
  ...sourceTemplate ? { sourceTemplate } : {}
256
264
  };
265
+ const syncPluginPackage = resolvedSyncConfig.plugin === "po" ? "@intlayer/sync-po-plugin" : "@intlayer/sync-json-plugin";
266
+ const syncPluginName = resolvedSyncConfig.plugin === "po" ? "syncPO" : "syncJSON";
257
267
  if (resolvedSyncConfig.splitKeys && resolvedSyncConfig.sourceTemplate.includes("${key}")) resolvedSyncConfig.splitKeys = false;
258
268
  for (const configFile of [
259
269
  "intlayer.config.ts",
@@ -262,10 +272,10 @@ const initIntlayer = async (rootDir, options) => {
262
272
  "intlayer.config.cjs"
263
273
  ]) if (await require_init_utils_fileSystem.exists(rootDir, configFile)) {
264
274
  const configContent = await require_init_utils_fileSystem.readFileFromRoot(rootDir, configFile);
265
- if (!configContent.includes("@intlayer/sync-json-plugin")) {
275
+ if (!configContent.includes(syncPluginPackage)) {
266
276
  await require_init_utils_fileSystem.writeFileToRoot(rootDir, configFile, require_init_utils_configManipulation.updateIntlayerConfigWithSyncPlugin(configContent, configFile.split(".").pop(), resolvedSyncConfig));
267
- (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} Updated ${(0, _intlayer_config_logger.colorizePath)(configFile)} with syncJSON compat plugin`);
268
- } else (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} ${(0, _intlayer_config_logger.colorizePath)(configFile)} already includes syncJSON plugin`);
277
+ (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} Updated ${(0, _intlayer_config_logger.colorizePath)(configFile)} with ${syncPluginName} compat plugin`);
278
+ } else (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} ${(0, _intlayer_config_logger.colorizePath)(configFile)} already includes ${syncPluginName} plugin`);
269
279
  break;
270
280
  }
271
281
  }