@intlayer/chokidar 9.0.0-canary.6 → 9.0.0-canary.8

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 (87) hide show
  1. package/dist/assets/initConfig/templates/cjs.txt +2 -2
  2. package/dist/assets/initConfig/templates/json.txt +2 -2
  3. package/dist/assets/initConfig/templates/mjs.txt +2 -2
  4. package/dist/assets/initConfig/templates/ts.txt +2 -2
  5. package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
  6. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +3 -3
  7. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
  8. package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs.map +1 -1
  9. package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs +5 -1
  10. package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs.map +1 -1
  11. package/dist/cjs/init/frameworkSetup/nextAppRouter/restructure.cjs +21 -5
  12. package/dist/cjs/init/frameworkSetup/nextAppRouter/restructure.cjs.map +1 -1
  13. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs +6 -21
  14. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs.map +1 -1
  15. package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs +8 -6
  16. package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs.map +1 -1
  17. package/dist/cjs/init/frameworkSetup/tanstackStart/restructure.cjs +47 -10
  18. package/dist/cjs/init/frameworkSetup/tanstackStart/restructure.cjs.map +1 -1
  19. package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs +9 -7
  20. package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs.map +1 -1
  21. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +33 -20
  22. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
  23. package/dist/cjs/init/index.cjs +23 -11
  24. package/dist/cjs/init/index.cjs.map +1 -1
  25. package/dist/cjs/init/utils/astImports.cjs +56 -0
  26. package/dist/cjs/init/utils/astImports.cjs.map +1 -0
  27. package/dist/cjs/init/utils/configManipulation.cjs +53 -14
  28. package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
  29. package/dist/cjs/init/utils/fileSystem.cjs +50 -0
  30. package/dist/cjs/init/utils/fileSystem.cjs.map +1 -1
  31. package/dist/cjs/init/utils/index.cjs +6 -0
  32. package/dist/cjs/init/utils/packageManager.cjs +18 -9
  33. package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
  34. package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs.map +1 -1
  35. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +3 -3
  36. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
  37. package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs.map +1 -1
  38. package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs +5 -1
  39. package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs.map +1 -1
  40. package/dist/esm/init/frameworkSetup/nextAppRouter/restructure.mjs +21 -6
  41. package/dist/esm/init/frameworkSetup/nextAppRouter/restructure.mjs.map +1 -1
  42. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs +1 -16
  43. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs.map +1 -1
  44. package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs +10 -8
  45. package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs.map +1 -1
  46. package/dist/esm/init/frameworkSetup/tanstackStart/restructure.mjs +45 -11
  47. package/dist/esm/init/frameworkSetup/tanstackStart/restructure.mjs.map +1 -1
  48. package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs +9 -7
  49. package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs.map +1 -1
  50. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +29 -16
  51. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
  52. package/dist/esm/init/index.mjs +25 -13
  53. package/dist/esm/init/index.mjs.map +1 -1
  54. package/dist/esm/init/utils/astImports.mjs +51 -0
  55. package/dist/esm/init/utils/astImports.mjs.map +1 -0
  56. package/dist/esm/init/utils/configManipulation.mjs +53 -15
  57. package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
  58. package/dist/esm/init/utils/fileSystem.mjs +50 -1
  59. package/dist/esm/init/utils/fileSystem.mjs.map +1 -1
  60. package/dist/esm/init/utils/index.mjs +4 -3
  61. package/dist/esm/init/utils/packageManager.mjs +18 -9
  62. package/dist/esm/init/utils/packageManager.mjs.map +1 -1
  63. package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts +3 -3
  64. package/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts +1 -1
  65. package/dist/types/formatDictionary.d.ts +2 -4
  66. package/dist/types/formatDictionary.d.ts.map +1 -1
  67. package/dist/types/init/frameworkSetup/nextAppRouter/restructure.d.ts +15 -3
  68. package/dist/types/init/frameworkSetup/nextAppRouter/restructure.d.ts.map +1 -1
  69. package/dist/types/init/frameworkSetup/nextAppRouter/transforms.d.ts.map +1 -1
  70. package/dist/types/init/frameworkSetup/tanstackStart/restructure.d.ts +40 -7
  71. package/dist/types/init/frameworkSetup/tanstackStart/restructure.d.ts.map +1 -1
  72. package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts +7 -5
  73. package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts.map +1 -1
  74. package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts.map +1 -1
  75. package/dist/types/init/frameworkSetup/types.d.ts +8 -0
  76. package/dist/types/init/frameworkSetup/types.d.ts.map +1 -1
  77. package/dist/types/init/index.d.ts.map +1 -1
  78. package/dist/types/init/utils/astImports.d.ts +29 -0
  79. package/dist/types/init/utils/astImports.d.ts.map +1 -0
  80. package/dist/types/init/utils/configManipulation.d.ts +14 -4
  81. package/dist/types/init/utils/configManipulation.d.ts.map +1 -1
  82. package/dist/types/init/utils/fileSystem.d.ts +29 -1
  83. package/dist/types/init/utils/fileSystem.d.ts.map +1 -1
  84. package/dist/types/init/utils/index.d.ts +5 -4
  85. package/dist/types/init/utils/packageManager.d.ts +34 -3
  86. package/dist/types/init/utils/packageManager.d.ts.map +1 -1
  87. package/package.json +8 -8
@@ -30,7 +30,7 @@ const config = {
30
30
  * Handles locale detection, redirects and rewrites in dev, preview and SSR.
31
31
  * Default: true
32
32
  */
33
- enableProxy: true,
33
+ enableProxy: false,
34
34
  },
35
35
  editor: {
36
36
  /**
@@ -59,7 +59,7 @@ const config = {
59
59
  applicationContext: [''].join('\n'),
60
60
  },
61
61
  compiler: {
62
- enabled: true,
62
+ enabled: false,
63
63
 
64
64
  /**
65
65
  * Defines the output files path for autogenerated content (compiler / autofill / extract)
@@ -25,7 +25,7 @@
25
25
  * Handles locale detection, redirects and rewrites in dev, preview and SSR.
26
26
  * Default: true
27
27
  */
28
- "enableProxy": true
28
+ "enableProxy": false
29
29
  },
30
30
  "editor": {
31
31
  /**
@@ -55,7 +55,7 @@
55
55
  "applicationContext": ""
56
56
  },
57
57
  "compiler": {
58
- "enabled": true,
58
+ "enabled": false,
59
59
  /**
60
60
  * Defines the output files path for autogenerated content.
61
61
  * Example: "./{{fileName}}.content.ts"
@@ -30,7 +30,7 @@ const config = {
30
30
  * Handles locale detection, redirects and rewrites in dev, preview and SSR.
31
31
  * Default: true
32
32
  */
33
- enableProxy: true,
33
+ enableProxy: false,
34
34
  },
35
35
  editor: {
36
36
  /**
@@ -59,7 +59,7 @@ const config = {
59
59
  applicationContext: [''].join('\n'),
60
60
  },
61
61
  compiler: {
62
- enabled: true,
62
+ enabled: false,
63
63
 
64
64
  /**
65
65
  * Defines the output files path for autogenerated content (compiler / autofill / extract)
@@ -29,7 +29,7 @@ const config: IntlayerConfig = {
29
29
  * Handles locale detection, redirects and rewrites in dev, preview and SSR.
30
30
  * Default: true
31
31
  */
32
- enableProxy: true,
32
+ enableProxy: false,
33
33
  },
34
34
  editor: {
35
35
  /**
@@ -58,7 +58,7 @@ const config: IntlayerConfig = {
58
58
  applicationContext: [''].join('\n'),
59
59
  },
60
60
  compiler: {
61
- enabled: true,
61
+ enabled: false,
62
62
 
63
63
  /**
64
64
  * Defines the output files path for autogenerated content (compiler / autofill / extract)
@@ -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"}
@@ -85,7 +85,11 @@ const nextAppRouterAdapter = {
85
85
  await ensureProxyOrMiddleware(rootDir, srcDir, isNext16);
86
86
  const restructureResult = await require_init_frameworkSetup_nextAppRouter_restructure.restructureAppIntoLocale(rootDir, appDir);
87
87
  if (restructureResult.status === "moved") (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} Restructured app routes under ${(0, _intlayer_config_logger.colorizePath)((0, node_path.join)(appDir, "[locale]"))}: ${restructureResult.movedEntries.map((entry) => (0, _intlayer_config_logger.colorize)(entry, _intlayer_config_colors.MAGENTA)).join(", ")}`);
88
- else if (restructureResult.status === "already-structured") (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} ${(0, _intlayer_config_logger.colorizePath)((0, node_path.join)(appDir, "[locale]"))} already exists, skipping restructure`);
88
+ else if (restructureResult.status === "already-structured") (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} ${(0, _intlayer_config_logger.colorizePath)((0, node_path.join)(appDir, restructureResult.localeSegment))} already exists, skipping restructure`);
89
+ if (restructureResult.status === "already-structured" && restructureResult.localeSegment !== "[locale]") {
90
+ (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} Detected existing locale routing (${(0, _intlayer_config_logger.colorize)(restructureResult.localeSegment, _intlayer_config_colors.MAGENTA)}), skipping locale layout/page scaffold`);
91
+ return;
92
+ }
89
93
  const localeDir = (0, node_path.join)(appDir, "[locale]");
90
94
  await require_init_utils_fileSystem.ensureDirectory(rootDir, localeDir);
91
95
  await createIfMissing(rootDir, (0, node_path.join)(appDir, `layout.${scriptExtension}`), useTypeScript ? require_init_frameworkSetup_nextAppRouter_templates.ROOT_LAYOUT_TEMPLATE_TS : require_init_frameworkSetup_nextAppRouter_templates.ROOT_LAYOUT_TEMPLATE_JS, "root layout");
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["exists","v","PROXY_TEMPLATE","MIDDLEWARE_TEMPLATE","ensureDirectory","writeFileToRoot","x","readFileFromRoot","detectNextAppDir","isVersionAtLeast","ANSIColors","restructureAppIntoLocale","ROOT_LAYOUT_TEMPLATE_TS","ROOT_LAYOUT_TEMPLATE_JS","findAppFile","wrapLayoutWithProvider","LOCALE_LAYOUT_TEMPLATE_TS","LOCALE_LAYOUT_TEMPLATE_JS","wrapPageWithProvider","LOCALE_PAGE_TEMPLATE_TS","LOCALE_PAGE_TEMPLATE_JS"],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport {\n ensureDirectory,\n exists,\n readFileFromRoot,\n writeFileToRoot,\n} from '../../utils/fileSystem';\nimport type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectNextAppDir, findAppFile, isVersionAtLeast } from './detect';\nimport { restructureAppIntoLocale } from './restructure';\nimport {\n LOCALE_LAYOUT_TEMPLATE_JS,\n LOCALE_LAYOUT_TEMPLATE_TS,\n LOCALE_PAGE_TEMPLATE_JS,\n LOCALE_PAGE_TEMPLATE_TS,\n MIDDLEWARE_TEMPLATE,\n PROXY_TEMPLATE,\n ROOT_LAYOUT_TEMPLATE_JS,\n ROOT_LAYOUT_TEMPLATE_TS,\n} from './templates';\nimport {\n type TransformResult,\n wrapLayoutWithProvider,\n wrapPageWithProvider,\n} from './transforms';\n\n/**\n * Creates the locale-detection proxy (Next >= 16) or middleware (Next < 16),\n * but only when neither already exists, so a user's custom file is never\n * touched. Placed next to the app directory's parent (`src/` when present).\n */\nconst ensureProxyOrMiddleware = async (\n rootDir: string,\n srcDir: string,\n isNext16: boolean\n): Promise<void> => {\n const baseDir = srcDir || '.';\n const proxyCandidates = [\n 'proxy.ts',\n 'proxy.js',\n 'middleware.ts',\n 'middleware.js',\n ];\n\n for (const candidate of proxyCandidates) {\n if (await exists(rootDir, join(baseDir, candidate))) {\n logger(\n `${v} ${colorizePath(join(baseDir, candidate))} already exists, leaving it untouched`\n );\n return;\n }\n }\n\n const fileName = isNext16 ? 'proxy.ts' : 'middleware.ts';\n const content = isNext16 ? PROXY_TEMPLATE : MIDDLEWARE_TEMPLATE;\n const targetPath = join(baseDir, fileName);\n\n if (srcDir) await ensureDirectory(rootDir, srcDir);\n await writeFileToRoot(rootDir, targetPath, content);\n logger(`${v} Created ${colorizePath(targetPath)} for locale detection`);\n};\n\n/** Logs the outcome of a layout/page provider-wrap transform. */\nconst logTransformOutcome = (\n filePath: string,\n result: TransformResult\n): void => {\n switch (result.status) {\n case 'wrapped':\n logger(`${v} Added Intlayer provider to ${colorizePath(filePath)}`);\n break;\n case 'already':\n logger(\n `${v} ${colorizePath(filePath)} already wraps the Intlayer provider`\n );\n break;\n case 'skipped-client':\n logger(\n `${x} ${colorizePath(filePath)} is a client component — add the Intlayer provider manually.`,\n { level: 'warn' }\n );\n break;\n case 'skipped':\n logger(\n `${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wire it manually.`,\n { level: 'warn' }\n );\n break;\n }\n};\n\n/** Transforms an existing file in place, writing it back only when changed. */\nconst transformExistingFile = async (\n rootDir: string,\n filePath: string,\n transform: (code: string) => TransformResult\n): Promise<void> => {\n const code = await readFileFromRoot(rootDir, filePath);\n const result = transform(code);\n if (result.status === 'wrapped') {\n await writeFileToRoot(rootDir, filePath, result.code);\n }\n logTransformOutcome(filePath, result);\n};\n\n/** Creates a file from a template only when it does not already exist. */\nconst createIfMissing = async (\n rootDir: string,\n filePath: string,\n content: string,\n description: string\n): Promise<boolean> => {\n if (await exists(rootDir, filePath)) return false;\n await writeFileToRoot(rootDir, filePath, content);\n logger(`${v} Created ${description} ${colorizePath(filePath)}`);\n return true;\n};\n\n/**\n * Next.js App Router adapter. Scaffolds the locale proxy/middleware, restructures\n * the app into a `[locale]` segment, and wraps the layout/page with the Intlayer\n * providers — all idempotently and without overwriting recognizable user code.\n */\nexport const nextAppRouterAdapter: FrameworkAdapter = {\n name: 'Next.js App Router',\n\n detect: async ({ rootDir, allDeps }) => {\n if (!allDeps.next) return false;\n return (await detectNextAppDir(rootDir)) !== null;\n },\n\n setup: async ({ rootDir, allDeps, useTypeScript }: FrameworkSetupContext) => {\n const appDirInfo = await detectNextAppDir(rootDir);\n if (!appDirInfo) return;\n\n const { appDir, srcDir } = appDirInfo;\n const isNext16 = isVersionAtLeast(allDeps.next, 16);\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n logger(\n colorize('Setting up Next.js App Router integration...', ANSIColors.CYAN)\n );\n\n // 1. Locale detection proxy / middleware (create only when absent).\n await ensureProxyOrMiddleware(rootDir, srcDir, isNext16);\n\n // 2. Move routable files under a `[locale]` segment (idempotent).\n const restructureResult = await restructureAppIntoLocale(rootDir, appDir);\n if (restructureResult.status === 'moved') {\n logger(\n `${v} Restructured app routes under ${colorizePath(join(appDir, '[locale]'))}: ${restructureResult.movedEntries\n .map((entry) => colorize(entry, ANSIColors.MAGENTA))\n .join(', ')}`\n );\n } else if (restructureResult.status === 'already-structured') {\n logger(\n `${v} ${colorizePath(join(appDir, '[locale]'))} already exists, skipping restructure`\n );\n }\n\n const localeDir = join(appDir, '[locale]');\n await ensureDirectory(rootDir, localeDir);\n\n // 3. Minimal pass-through root layout (the `[locale]` layout owns `<html>`).\n await createIfMissing(\n rootDir,\n join(appDir, `layout.${scriptExtension}`),\n useTypeScript ? ROOT_LAYOUT_TEMPLATE_TS : ROOT_LAYOUT_TEMPLATE_JS,\n 'root layout'\n );\n\n // 4. Locale layout — transform an existing one, else scaffold from template.\n const existingLayout = await findAppFile(rootDir, localeDir, 'layout');\n if (existingLayout) {\n await transformExistingFile(\n rootDir,\n existingLayout,\n wrapLayoutWithProvider\n );\n } else {\n await createIfMissing(\n rootDir,\n join(localeDir, `layout.${scriptExtension}`),\n useTypeScript ? LOCALE_LAYOUT_TEMPLATE_TS : LOCALE_LAYOUT_TEMPLATE_JS,\n 'locale layout'\n );\n }\n\n // 5. Locale page — transform an existing one, else scaffold from template.\n const existingPage = await findAppFile(rootDir, localeDir, 'page');\n if (existingPage) {\n await transformExistingFile(rootDir, existingPage, wrapPageWithProvider);\n } else {\n await createIfMissing(\n rootDir,\n join(localeDir, `page.${scriptExtension}`),\n useTypeScript ? LOCALE_PAGE_TEMPLATE_TS : LOCALE_PAGE_TEMPLATE_JS,\n 'locale page'\n );\n }\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["exists","v","PROXY_TEMPLATE","MIDDLEWARE_TEMPLATE","ensureDirectory","writeFileToRoot","x","readFileFromRoot","detectNextAppDir","isVersionAtLeast","ANSIColors","restructureAppIntoLocale","ROOT_LAYOUT_TEMPLATE_TS","ROOT_LAYOUT_TEMPLATE_JS","findAppFile","wrapLayoutWithProvider","LOCALE_LAYOUT_TEMPLATE_TS","LOCALE_LAYOUT_TEMPLATE_JS","wrapPageWithProvider","LOCALE_PAGE_TEMPLATE_TS","LOCALE_PAGE_TEMPLATE_JS"],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, colorizePath, logger, v, x } from '@intlayer/config/logger';\nimport {\n ensureDirectory,\n exists,\n readFileFromRoot,\n writeFileToRoot,\n} from '../../utils/fileSystem';\nimport type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectNextAppDir, findAppFile, isVersionAtLeast } from './detect';\nimport { restructureAppIntoLocale } from './restructure';\nimport {\n LOCALE_LAYOUT_TEMPLATE_JS,\n LOCALE_LAYOUT_TEMPLATE_TS,\n LOCALE_PAGE_TEMPLATE_JS,\n LOCALE_PAGE_TEMPLATE_TS,\n MIDDLEWARE_TEMPLATE,\n PROXY_TEMPLATE,\n ROOT_LAYOUT_TEMPLATE_JS,\n ROOT_LAYOUT_TEMPLATE_TS,\n} from './templates';\nimport {\n type TransformResult,\n wrapLayoutWithProvider,\n wrapPageWithProvider,\n} from './transforms';\n\n/**\n * Creates the locale-detection proxy (Next >= 16) or middleware (Next < 16),\n * but only when neither already exists, so a user's custom file is never\n * touched. Placed next to the app directory's parent (`src/` when present).\n */\nconst ensureProxyOrMiddleware = async (\n rootDir: string,\n srcDir: string,\n isNext16: boolean\n): Promise<void> => {\n const baseDir = srcDir || '.';\n const proxyCandidates = [\n 'proxy.ts',\n 'proxy.js',\n 'middleware.ts',\n 'middleware.js',\n ];\n\n for (const candidate of proxyCandidates) {\n if (await exists(rootDir, join(baseDir, candidate))) {\n logger(\n `${v} ${colorizePath(join(baseDir, candidate))} already exists, leaving it untouched`\n );\n return;\n }\n }\n\n const fileName = isNext16 ? 'proxy.ts' : 'middleware.ts';\n const content = isNext16 ? PROXY_TEMPLATE : MIDDLEWARE_TEMPLATE;\n const targetPath = join(baseDir, fileName);\n\n if (srcDir) await ensureDirectory(rootDir, srcDir);\n await writeFileToRoot(rootDir, targetPath, content);\n logger(`${v} Created ${colorizePath(targetPath)} for locale detection`);\n};\n\n/** Logs the outcome of a layout/page provider-wrap transform. */\nconst logTransformOutcome = (\n filePath: string,\n result: TransformResult\n): void => {\n switch (result.status) {\n case 'wrapped':\n logger(`${v} Added Intlayer provider to ${colorizePath(filePath)}`);\n break;\n case 'already':\n logger(\n `${v} ${colorizePath(filePath)} already wraps the Intlayer provider`\n );\n break;\n case 'skipped-client':\n logger(\n `${x} ${colorizePath(filePath)} is a client component — add the Intlayer provider manually.`,\n { level: 'warn' }\n );\n break;\n case 'skipped':\n logger(\n `${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wire it manually.`,\n { level: 'warn' }\n );\n break;\n }\n};\n\n/** Transforms an existing file in place, writing it back only when changed. */\nconst transformExistingFile = async (\n rootDir: string,\n filePath: string,\n transform: (code: string) => TransformResult\n): Promise<void> => {\n const code = await readFileFromRoot(rootDir, filePath);\n const result = transform(code);\n if (result.status === 'wrapped') {\n await writeFileToRoot(rootDir, filePath, result.code);\n }\n logTransformOutcome(filePath, result);\n};\n\n/** Creates a file from a template only when it does not already exist. */\nconst createIfMissing = async (\n rootDir: string,\n filePath: string,\n content: string,\n description: string\n): Promise<boolean> => {\n if (await exists(rootDir, filePath)) return false;\n await writeFileToRoot(rootDir, filePath, content);\n logger(`${v} Created ${description} ${colorizePath(filePath)}`);\n return true;\n};\n\n/**\n * Next.js App Router adapter. Scaffolds the locale proxy/middleware, restructures\n * the app into a `[locale]` segment, and wraps the layout/page with the Intlayer\n * providers — all idempotently and without overwriting recognizable user code.\n */\nexport const nextAppRouterAdapter: FrameworkAdapter = {\n name: 'Next.js App Router',\n\n detect: async ({ rootDir, allDeps }) => {\n if (!allDeps.next) return false;\n return (await detectNextAppDir(rootDir)) !== null;\n },\n\n setup: async ({ rootDir, allDeps, useTypeScript }: FrameworkSetupContext) => {\n const appDirInfo = await detectNextAppDir(rootDir);\n if (!appDirInfo) return;\n\n const { appDir, srcDir } = appDirInfo;\n const isNext16 = isVersionAtLeast(allDeps.next, 16);\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n logger(\n colorize('Setting up Next.js App Router integration...', ANSIColors.CYAN)\n );\n\n // 1. Locale detection proxy / middleware (create only when absent).\n await ensureProxyOrMiddleware(rootDir, srcDir, isNext16);\n\n // 2. Move routable files under a `[locale]` segment (idempotent).\n const restructureResult = await restructureAppIntoLocale(rootDir, appDir);\n if (restructureResult.status === 'moved') {\n logger(\n `${v} Restructured app routes under ${colorizePath(join(appDir, '[locale]'))}: ${restructureResult.movedEntries\n .map((entry) => colorize(entry, ANSIColors.MAGENTA))\n .join(', ')}`\n );\n } else if (restructureResult.status === 'already-structured') {\n logger(\n `${v} ${colorizePath(join(appDir, restructureResult.localeSegment))} already exists, skipping restructure`\n );\n }\n\n // When the app already uses a different locale-prefix scheme (e.g. an\n // `[...locale]` / `[[...locale]]` catch-all), respect the user's routing:\n // don't scaffold a second, conflicting `[locale]` segment.\n if (\n restructureResult.status === 'already-structured' &&\n restructureResult.localeSegment !== '[locale]'\n ) {\n logger(\n `${v} Detected existing locale routing (${colorize(restructureResult.localeSegment, ANSIColors.MAGENTA)}), skipping locale layout/page scaffold`\n );\n return;\n }\n\n const localeDir = join(appDir, '[locale]');\n await ensureDirectory(rootDir, localeDir);\n\n // 3. Minimal pass-through root layout (the `[locale]` layout owns `<html>`).\n await createIfMissing(\n rootDir,\n join(appDir, `layout.${scriptExtension}`),\n useTypeScript ? ROOT_LAYOUT_TEMPLATE_TS : ROOT_LAYOUT_TEMPLATE_JS,\n 'root layout'\n );\n\n // 4. Locale layout — transform an existing one, else scaffold from template.\n const existingLayout = await findAppFile(rootDir, localeDir, 'layout');\n if (existingLayout) {\n await transformExistingFile(\n rootDir,\n existingLayout,\n wrapLayoutWithProvider\n );\n } else {\n await createIfMissing(\n rootDir,\n join(localeDir, `layout.${scriptExtension}`),\n useTypeScript ? LOCALE_LAYOUT_TEMPLATE_TS : LOCALE_LAYOUT_TEMPLATE_JS,\n 'locale layout'\n );\n }\n\n // 5. Locale page — transform an existing one, else scaffold from template.\n const existingPage = await findAppFile(rootDir, localeDir, 'page');\n if (existingPage) {\n await transformExistingFile(rootDir, existingPage, wrapPageWithProvider);\n } else {\n await createIfMissing(\n rootDir,\n join(localeDir, `page.${scriptExtension}`),\n useTypeScript ? LOCALE_PAGE_TEMPLATE_TS : LOCALE_PAGE_TEMPLATE_JS,\n 'locale page'\n );\n }\n },\n};\n"],"mappings":"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"}
@@ -4,7 +4,6 @@ let node_fs_promises = require("node:fs/promises");
4
4
  let node_path = require("node:path");
5
5
  let fast_glob = require("fast-glob");
6
6
  fast_glob = require_runtime.__toESM(fast_glob);
7
- let node_fs = require("node:fs");
8
7
  let recast = require("recast");
9
8
  recast = require_runtime.__toESM(recast);
10
9
 
@@ -15,6 +14,16 @@ const SCRIPT_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs}";
15
14
  /** Strips a known script extension from a file name, e.g. `page.tsx` -> `page`. */
16
15
  const stripScriptExtension = (fileName) => fileName.replace(/\.(tsx|ts|jsx|js|mjs|cjs)$/, "");
17
16
  /**
17
+ * Detects whether a top-level App Router entry is already a locale segment, in
18
+ * any of the Next.js dynamic-segment forms:
19
+ * - `[locale]` — required segment (prefix every locale),
20
+ * - `[...locale]` / `[[...locale]]` — catch-all / optional catch-all segments.
21
+ *
22
+ * Used to skip the restructure when the project is already locale-aware, so an
23
+ * existing locale segment is never nested under a freshly created `[locale]`.
24
+ */
25
+ const isLocaleSegment = (entryName) => /^\[\[?\.{0,3}locale\]\]?$/.test(entryName);
26
+ /**
18
27
  * Top-level App Router entries that must stay at the app root and never be
19
28
  * moved under `[locale]`:
20
29
  * - `api/` route handlers (not locale-prefixed),
@@ -107,14 +116,20 @@ const rewriteRelativeImports = (code, oldAbs, newAbs, context) => {
107
116
  /**
108
117
  * Moves the routable App Router entries of `appDir` under a new `[locale]`
109
118
  * segment and rewrites relative imports in the moved files. Idempotent: it is a
110
- * no-op when `[locale]` already exists. Root-only files (see
111
- * {@link shouldKeepAppEntryAtRoot}) are left in place.
119
+ * no-op when the app is already locale-aware in any prefix mode (see
120
+ * {@link isLocaleSegment}), which is reported via `localeSegment`. Root-only
121
+ * files (see {@link shouldKeepAppEntryAtRoot}) are left in place.
112
122
  */
113
123
  const restructureAppIntoLocale = async (rootDir, appDir) => {
114
124
  const appDirAbs = (0, node_path.join)(rootDir, appDir);
115
125
  const localeDirAbs = (0, node_path.join)(appDirAbs, "[locale]");
116
- if ((0, node_fs.existsSync)(localeDirAbs)) return { status: "already-structured" };
117
- const movedTopLevelNames = (await (0, node_fs_promises.readdir)(appDirAbs, { withFileTypes: true })).map((entry) => entry.name).filter((name) => !shouldKeepAppEntryAtRoot(name));
126
+ const entries = await (0, node_fs_promises.readdir)(appDirAbs, { withFileTypes: true });
127
+ const existingLocaleSegment = entries.find((entry) => isLocaleSegment(entry.name));
128
+ if (existingLocaleSegment) return {
129
+ status: "already-structured",
130
+ localeSegment: existingLocaleSegment.name
131
+ };
132
+ const movedTopLevelNames = entries.map((entry) => entry.name).filter((name) => !shouldKeepAppEntryAtRoot(name));
118
133
  if (movedTopLevelNames.length === 0) return { status: "nothing-to-move" };
119
134
  await (0, node_fs_promises.mkdir)(localeDirAbs, { recursive: true });
120
135
  for (const name of movedTopLevelNames) await (0, node_fs_promises.rename)((0, node_path.join)(appDirAbs, name), (0, node_path.join)(localeDirAbs, name));
@@ -141,6 +156,7 @@ const restructureAppIntoLocale = async (rootDir, appDir) => {
141
156
  };
142
157
 
143
158
  //#endregion
159
+ exports.isLocaleSegment = isLocaleSegment;
144
160
  exports.restructureAppIntoLocale = restructureAppIntoLocale;
145
161
  exports.rewriteRelativeImports = rewriteRelativeImports;
146
162
  exports.shouldKeepAppEntryAtRoot = shouldKeepAppEntryAtRoot;
@@ -1 +1 @@
1
- {"version":3,"file":"restructure.cjs","names":["sep"],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/restructure.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport fg from 'fast-glob';\nimport * as recast from 'recast';\n\nconst { namedTypes: n } = recast.types;\n\n/** Source file extensions whose relative imports must be rewritten after a move. */\nconst SCRIPT_GLOB = '**/*.{ts,tsx,js,jsx,mjs,cjs}';\n\n/** Strips a known script extension from a file name, e.g. `page.tsx` -> `page`. */\nconst stripScriptExtension = (fileName: string): string =>\n fileName.replace(/\\.(tsx|ts|jsx|js|mjs|cjs)$/, '');\n\n/**\n * Top-level App Router entries that must stay at the app root and never be\n * moved under `[locale]`:\n * - `api/` route handlers (not locale-prefixed),\n * - global stylesheets,\n * - metadata/asset file conventions (favicon, icon, sitemap, robots, manifest…),\n * - `global-error` and `not-found` boundaries (kept as root fallbacks).\n *\n * Everything else (`page`, `loading`, `error`, `template`, `default`, the root\n * `layout`, and nested route folders) is moved so it becomes locale-aware.\n */\nexport const shouldKeepAppEntryAtRoot = (entryName: string): boolean => {\n if (entryName === 'api') return true;\n if (entryName.toLowerCase().endsWith('.css')) return true;\n if (entryName === 'favicon.ico') return true;\n\n const base = stripScriptExtension(entryName);\n const keepExactBases = new Set([\n 'not-found',\n 'global-error',\n 'sitemap',\n 'robots',\n 'manifest',\n ]);\n if (keepExactBases.has(base)) return true;\n\n // Image metadata conventions: favicon, icon, apple-icon, opengraph-image,\n // twitter-image — optionally suffixed (e.g. `icon1`, `opengraph-image-alt`).\n const imageConventionPrefixes = [\n 'favicon',\n 'icon',\n 'apple-icon',\n 'opengraph-image',\n 'twitter-image',\n ];\n if (imageConventionPrefixes.some((prefix) => base.startsWith(prefix))) {\n return true;\n }\n\n return false;\n};\n\ntype RewriteContext = {\n appDirAbs: string;\n localeDirAbs: string;\n movedTopLevelNames: string[];\n};\n\n/**\n * Maps an import target's pre-move absolute path to its post-move absolute path.\n * Targets that live inside a moved top-level entry are relocated under\n * `[locale]`; targets that stayed at the app root or live outside the app\n * directory are returned unchanged.\n */\nconst mapTargetPath = (\n oldTargetAbs: string,\n { appDirAbs, localeDirAbs, movedTopLevelNames }: RewriteContext\n): string => {\n const relFromApp = relative(appDirAbs, oldTargetAbs);\n\n // Outside the app directory (e.g. `../components/...`) — never moved.\n if (relFromApp.startsWith('..') || isAbsolute(relFromApp)) {\n return oldTargetAbs;\n }\n\n const firstSegment = relFromApp.split(sep)[0] ?? '';\n const isMoved = movedTopLevelNames.some(\n (name) =>\n name === firstSegment || stripScriptExtension(name) === firstSegment\n );\n\n if (!isMoved) return oldTargetAbs;\n\n return join(localeDirAbs, relFromApp);\n};\n\n/**\n * Rewrites relative import/export/`import()`/`require()` specifiers in a file\n * that has moved from `oldAbs` to `newAbs`, so they keep resolving to the same\n * modules after the move. Non-relative specifiers (bare packages, `@/` aliases)\n * are left untouched. Returns the original code unchanged when nothing matched.\n */\nexport const rewriteRelativeImports = (\n code: string,\n oldAbs: string,\n newAbs: string,\n context: RewriteContext\n): string => {\n // babel-ts handles TypeScript *and* JSX (App Router files are `.tsx`).\n const ast = recast.parse(code, {\n parser: require('recast/parsers/babel-ts'),\n });\n\n let changed = false;\n\n const rewriteSource = (sourceNode: any): void => {\n if (!sourceNode || !n.StringLiteral.check(sourceNode)) return;\n const specifier = sourceNode.value;\n if (typeof specifier !== 'string' || !specifier.startsWith('.')) return;\n\n const oldTargetAbs = resolve(dirname(oldAbs), specifier);\n const newTargetAbs = mapTargetPath(oldTargetAbs, context);\n\n let newSpecifier = relative(dirname(newAbs), newTargetAbs)\n .split(sep)\n .join('/');\n if (!newSpecifier.startsWith('.')) {\n newSpecifier = `./${newSpecifier}`;\n }\n\n if (newSpecifier !== specifier) {\n sourceNode.value = newSpecifier;\n changed = true;\n }\n };\n\n recast.visit(ast, {\n visitImportDeclaration(path) {\n rewriteSource(path.node.source);\n return false;\n },\n visitExportAllDeclaration(path) {\n rewriteSource(path.node.source);\n return false;\n },\n visitExportNamedDeclaration(path) {\n if (path.node.source) rewriteSource(path.node.source);\n return false;\n },\n visitCallExpression(path) {\n const { callee, arguments: args } = path.node;\n const isDynamicImport = callee.type === 'Import';\n const isRequire = n.Identifier.check(callee) && callee.name === 'require';\n if ((isDynamicImport || isRequire) && args.length > 0) {\n rewriteSource(args[0]);\n }\n this.traverse(path);\n },\n });\n\n if (!changed) return code;\n return recast.print(ast).code;\n};\n\n/** Outcome of an attempted `[locale]` restructure. */\nexport type RestructureResult =\n | { status: 'already-structured' }\n | { status: 'nothing-to-move' }\n | { status: 'moved'; movedEntries: string[] };\n\n/**\n * Moves the routable App Router entries of `appDir` under a new `[locale]`\n * segment and rewrites relative imports in the moved files. Idempotent: it is a\n * no-op when `[locale]` already exists. Root-only files (see\n * {@link shouldKeepAppEntryAtRoot}) are left in place.\n */\nexport const restructureAppIntoLocale = async (\n rootDir: string,\n appDir: string\n): Promise<RestructureResult> => {\n const appDirAbs = join(rootDir, appDir);\n const localeDirAbs = join(appDirAbs, '[locale]');\n\n if (existsSync(localeDirAbs)) {\n return { status: 'already-structured' };\n }\n\n const entries = await readdir(appDirAbs, { withFileTypes: true });\n const movedTopLevelNames = entries\n .map((entry) => entry.name)\n .filter((name) => !shouldKeepAppEntryAtRoot(name));\n\n if (movedTopLevelNames.length === 0) {\n return { status: 'nothing-to-move' };\n }\n\n await mkdir(localeDirAbs, { recursive: true });\n\n for (const name of movedTopLevelNames) {\n await rename(join(appDirAbs, name), join(localeDirAbs, name));\n }\n\n const movedFiles = await fg(SCRIPT_GLOB, {\n cwd: localeDirAbs,\n absolute: true,\n onlyFiles: true,\n });\n\n const rewriteContext: RewriteContext = {\n appDirAbs,\n localeDirAbs,\n movedTopLevelNames,\n };\n\n for (const newAbs of movedFiles) {\n const relFromLocale = relative(localeDirAbs, newAbs);\n const oldAbs = join(appDirAbs, relFromLocale);\n const code = await readFile(newAbs, 'utf8');\n const rewritten = rewriteRelativeImports(\n code,\n oldAbs,\n newAbs,\n rewriteContext\n );\n if (rewritten !== code) {\n await writeFile(newAbs, rewritten, 'utf8');\n }\n }\n\n return { status: 'moved', movedEntries: movedTopLevelNames };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"restructure.cjs","names":["sep"],"sources":["../../../../../src/init/frameworkSetup/nextAppRouter/restructure.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport fg from 'fast-glob';\nimport * as recast from 'recast';\n\nconst { namedTypes: n } = recast.types;\n\n/** Source file extensions whose relative imports must be rewritten after a move. */\nconst SCRIPT_GLOB = '**/*.{ts,tsx,js,jsx,mjs,cjs}';\n\n/** Strips a known script extension from a file name, e.g. `page.tsx` -> `page`. */\nconst stripScriptExtension = (fileName: string): string =>\n fileName.replace(/\\.(tsx|ts|jsx|js|mjs|cjs)$/, '');\n\n/**\n * Detects whether a top-level App Router entry is already a locale segment, in\n * any of the Next.js dynamic-segment forms:\n * - `[locale]` — required segment (prefix every locale),\n * - `[...locale]` / `[[...locale]]` — catch-all / optional catch-all segments.\n *\n * Used to skip the restructure when the project is already locale-aware, so an\n * existing locale segment is never nested under a freshly created `[locale]`.\n */\nexport const isLocaleSegment = (entryName: string): boolean =>\n /^\\[\\[?\\.{0,3}locale\\]\\]?$/.test(entryName);\n\n/**\n * Top-level App Router entries that must stay at the app root and never be\n * moved under `[locale]`:\n * - `api/` route handlers (not locale-prefixed),\n * - global stylesheets,\n * - metadata/asset file conventions (favicon, icon, sitemap, robots, manifest…),\n * - `global-error` and `not-found` boundaries (kept as root fallbacks).\n *\n * Everything else (`page`, `loading`, `error`, `template`, `default`, the root\n * `layout`, and nested route folders) is moved so it becomes locale-aware.\n */\nexport const shouldKeepAppEntryAtRoot = (entryName: string): boolean => {\n if (entryName === 'api') return true;\n if (entryName.toLowerCase().endsWith('.css')) return true;\n if (entryName === 'favicon.ico') return true;\n\n const base = stripScriptExtension(entryName);\n const keepExactBases = new Set([\n 'not-found',\n 'global-error',\n 'sitemap',\n 'robots',\n 'manifest',\n ]);\n if (keepExactBases.has(base)) return true;\n\n // Image metadata conventions: favicon, icon, apple-icon, opengraph-image,\n // twitter-image — optionally suffixed (e.g. `icon1`, `opengraph-image-alt`).\n const imageConventionPrefixes = [\n 'favicon',\n 'icon',\n 'apple-icon',\n 'opengraph-image',\n 'twitter-image',\n ];\n if (imageConventionPrefixes.some((prefix) => base.startsWith(prefix))) {\n return true;\n }\n\n return false;\n};\n\ntype RewriteContext = {\n appDirAbs: string;\n localeDirAbs: string;\n movedTopLevelNames: string[];\n};\n\n/**\n * Maps an import target's pre-move absolute path to its post-move absolute path.\n * Targets that live inside a moved top-level entry are relocated under\n * `[locale]`; targets that stayed at the app root or live outside the app\n * directory are returned unchanged.\n */\nconst mapTargetPath = (\n oldTargetAbs: string,\n { appDirAbs, localeDirAbs, movedTopLevelNames }: RewriteContext\n): string => {\n const relFromApp = relative(appDirAbs, oldTargetAbs);\n\n // Outside the app directory (e.g. `../components/...`) — never moved.\n if (relFromApp.startsWith('..') || isAbsolute(relFromApp)) {\n return oldTargetAbs;\n }\n\n const firstSegment = relFromApp.split(sep)[0] ?? '';\n const isMoved = movedTopLevelNames.some(\n (name) =>\n name === firstSegment || stripScriptExtension(name) === firstSegment\n );\n\n if (!isMoved) return oldTargetAbs;\n\n return join(localeDirAbs, relFromApp);\n};\n\n/**\n * Rewrites relative import/export/`import()`/`require()` specifiers in a file\n * that has moved from `oldAbs` to `newAbs`, so they keep resolving to the same\n * modules after the move. Non-relative specifiers (bare packages, `@/` aliases)\n * are left untouched. Returns the original code unchanged when nothing matched.\n */\nexport const rewriteRelativeImports = (\n code: string,\n oldAbs: string,\n newAbs: string,\n context: RewriteContext\n): string => {\n // babel-ts handles TypeScript *and* JSX (App Router files are `.tsx`).\n const ast = recast.parse(code, {\n parser: require('recast/parsers/babel-ts'),\n });\n\n let changed = false;\n\n const rewriteSource = (sourceNode: any): void => {\n if (!sourceNode || !n.StringLiteral.check(sourceNode)) return;\n const specifier = sourceNode.value;\n if (typeof specifier !== 'string' || !specifier.startsWith('.')) return;\n\n const oldTargetAbs = resolve(dirname(oldAbs), specifier);\n const newTargetAbs = mapTargetPath(oldTargetAbs, context);\n\n let newSpecifier = relative(dirname(newAbs), newTargetAbs)\n .split(sep)\n .join('/');\n if (!newSpecifier.startsWith('.')) {\n newSpecifier = `./${newSpecifier}`;\n }\n\n if (newSpecifier !== specifier) {\n sourceNode.value = newSpecifier;\n changed = true;\n }\n };\n\n recast.visit(ast, {\n visitImportDeclaration(path) {\n rewriteSource(path.node.source);\n return false;\n },\n visitExportAllDeclaration(path) {\n rewriteSource(path.node.source);\n return false;\n },\n visitExportNamedDeclaration(path) {\n if (path.node.source) rewriteSource(path.node.source);\n return false;\n },\n visitCallExpression(path) {\n const { callee, arguments: args } = path.node;\n const isDynamicImport = callee.type === 'Import';\n const isRequire = n.Identifier.check(callee) && callee.name === 'require';\n if ((isDynamicImport || isRequire) && args.length > 0) {\n rewriteSource(args[0]);\n }\n this.traverse(path);\n },\n });\n\n if (!changed) return code;\n return recast.print(ast).code;\n};\n\n/** Outcome of an attempted `[locale]` restructure. */\nexport type RestructureResult =\n | { status: 'already-structured'; localeSegment: string }\n | { status: 'nothing-to-move' }\n | { status: 'moved'; movedEntries: string[] };\n\n/**\n * Moves the routable App Router entries of `appDir` under a new `[locale]`\n * segment and rewrites relative imports in the moved files. Idempotent: it is a\n * no-op when the app is already locale-aware in any prefix mode (see\n * {@link isLocaleSegment}), which is reported via `localeSegment`. Root-only\n * files (see {@link shouldKeepAppEntryAtRoot}) are left in place.\n */\nexport const restructureAppIntoLocale = async (\n rootDir: string,\n appDir: string\n): Promise<RestructureResult> => {\n const appDirAbs = join(rootDir, appDir);\n const localeDirAbs = join(appDirAbs, '[locale]');\n\n const entries = await readdir(appDirAbs, { withFileTypes: true });\n\n // Skip when the app is already locale-aware in any prefix mode — a fresh\n // `[locale]`, or an existing `[...locale]` / `[[...locale]]` catch-all segment.\n const existingLocaleSegment = entries.find((entry) =>\n isLocaleSegment(entry.name)\n );\n if (existingLocaleSegment) {\n return {\n status: 'already-structured',\n localeSegment: existingLocaleSegment.name,\n };\n }\n\n const movedTopLevelNames = entries\n .map((entry) => entry.name)\n .filter((name) => !shouldKeepAppEntryAtRoot(name));\n\n if (movedTopLevelNames.length === 0) {\n return { status: 'nothing-to-move' };\n }\n\n await mkdir(localeDirAbs, { recursive: true });\n\n for (const name of movedTopLevelNames) {\n await rename(join(appDirAbs, name), join(localeDirAbs, name));\n }\n\n const movedFiles = await fg(SCRIPT_GLOB, {\n cwd: localeDirAbs,\n absolute: true,\n onlyFiles: true,\n });\n\n const rewriteContext: RewriteContext = {\n appDirAbs,\n localeDirAbs,\n movedTopLevelNames,\n };\n\n for (const newAbs of movedFiles) {\n const relFromLocale = relative(localeDirAbs, newAbs);\n const oldAbs = join(appDirAbs, relFromLocale);\n const code = await readFile(newAbs, 'utf8');\n const rewritten = rewriteRelativeImports(\n code,\n oldAbs,\n newAbs,\n rewriteContext\n );\n if (rewritten !== code) {\n await writeFile(newAbs, rewritten, 'utf8');\n }\n }\n\n return { status: 'moved', movedEntries: movedTopLevelNames };\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
 
@@ -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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"}
@@ -51,18 +51,20 @@ const tanStackStartAdapter = {
51
51
  if (!require_init_frameworkSetup_tanstackStart_detect.hasTanStackStartDeps(allDeps)) return false;
52
52
  return await require_init_frameworkSetup_tanstackStart_detect.detectTanStackRoutesDir(rootDir) !== null;
53
53
  },
54
- setup: async ({ rootDir, useTypeScript }) => {
54
+ setup: async ({ rootDir, useTypeScript, routingMode }) => {
55
55
  const routesInfo = await require_init_frameworkSetup_tanstackStart_detect.detectTanStackRoutesDir(rootDir);
56
56
  if (!routesInfo) return;
57
57
  const { routesDir } = routesInfo;
58
58
  const scriptExtension = useTypeScript ? "tsx" : "jsx";
59
+ const targetLocaleSegment = require_init_frameworkSetup_tanstackStart_restructure.getLocaleSegment(routingMode);
59
60
  (0, _intlayer_config_logger.logger)((0, _intlayer_config_logger.colorize)("Setting up TanStack Start integration...", _intlayer_config_colors.CYAN));
60
- const restructureResult = await require_init_frameworkSetup_tanstackStart_restructure.restructureRoutesIntoLocale(rootDir, routesDir);
61
- if (restructureResult.status === "moved") (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} Restructured routes under ${(0, _intlayer_config_logger.colorizePath)((0, node_path.join)(routesDir, require_init_frameworkSetup_tanstackStart_restructure.LOCALE_SEGMENT))}: ${restructureResult.movedEntries.map((entry) => (0, _intlayer_config_logger.colorize)(entry, _intlayer_config_colors.MAGENTA)).join(", ")}`);
62
- else if (restructureResult.status === "already-structured") (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} ${(0, _intlayer_config_logger.colorizePath)((0, node_path.join)(routesDir, require_init_frameworkSetup_tanstackStart_restructure.LOCALE_SEGMENT))} already exists, skipping restructure`);
63
- const localeDir = (0, node_path.join)(routesDir, require_init_frameworkSetup_tanstackStart_restructure.LOCALE_SEGMENT);
61
+ const restructureResult = await require_init_frameworkSetup_tanstackStart_restructure.restructureRoutesIntoLocale(rootDir, routesDir, targetLocaleSegment);
62
+ if (restructureResult.status === "moved") (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} Restructured routes under ${(0, _intlayer_config_logger.colorizePath)((0, node_path.join)(routesDir, targetLocaleSegment))}: ${restructureResult.movedEntries.map((entry) => (0, _intlayer_config_logger.colorize)(entry, _intlayer_config_colors.MAGENTA)).join(", ")}`);
63
+ else if (restructureResult.status === "already-structured") (0, _intlayer_config_logger.logger)(`${_intlayer_config_logger.v} ${(0, _intlayer_config_logger.colorizePath)((0, node_path.join)(routesDir, restructureResult.localeSegment))} already exists, skipping restructure`);
64
+ const activeLocaleSegment = restructureResult.status === "already-structured" ? restructureResult.localeSegment : targetLocaleSegment;
65
+ const localeDir = (0, node_path.join)(routesDir, activeLocaleSegment);
64
66
  await require_init_utils_fileSystem.ensureDirectory(rootDir, localeDir);
65
- await createIfMissing(rootDir, (0, node_path.join)(localeDir, `route.${scriptExtension}`), require_init_frameworkSetup_tanstackStart_templates.LOCALE_ROUTE_TEMPLATE, "locale route");
67
+ await createIfMissing(rootDir, (0, node_path.join)(localeDir, `route.${scriptExtension}`), require_init_frameworkSetup_tanstackStart_templates.buildLocaleRouteTemplate(activeLocaleSegment), "locale route");
66
68
  const existingRoot = await require_init_frameworkSetup_nextAppRouter_detect.findAppFile(rootDir, routesDir, "__root");
67
69
  if (existingRoot) await transformExistingFile(rootDir, existingRoot, require_init_frameworkSetup_tanstackStart_transforms.wrapRootWithProvider);
68
70
  else await createIfMissing(rootDir, (0, node_path.join)(routesDir, `__root.${scriptExtension}`), useTypeScript ? require_init_frameworkSetup_tanstackStart_templates.ROOT_TEMPLATE_TS : require_init_frameworkSetup_tanstackStart_templates.ROOT_TEMPLATE_JS, "root document");