@intlayer/engine 9.3.0 → 9.3.2

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 (297) hide show
  1. package/README.md +24 -24
  2. package/dist/assets/initConfig/templates/cjs.txt +1 -1
  3. package/dist/assets/initConfig/templates/mjs.txt +1 -1
  4. package/dist/assets/initConfig/templates/ts.txt +1 -1
  5. package/dist/cjs/_virtual/_rolldown/runtime.cjs +1 -2
  6. package/dist/cjs/_virtual/_utils_asset.cjs +2 -1
  7. package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
  8. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +2 -1
  9. package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
  10. package/dist/cjs/buildIntlayerDictionary/writeFetchDictionary.cjs.map +1 -1
  11. package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs +2 -1
  12. package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs.map +1 -1
  13. package/dist/cjs/buildIntlayerDictionary/writeNestedDictionary.cjs.map +1 -1
  14. package/dist/cjs/buildIntlayerDictionary/writeRemoteDictionary.cjs +4 -2
  15. package/dist/cjs/buildIntlayerDictionary/writeRemoteDictionary.cjs.map +1 -1
  16. package/dist/cjs/buildIntlayerDictionary/writeUnmergedDictionary.cjs.map +1 -1
  17. package/dist/cjs/cleanOutputDir.cjs.map +1 -1
  18. package/dist/cjs/cleanRemovedContentDeclaration.cjs +7 -4
  19. package/dist/cjs/cleanRemovedContentDeclaration.cjs.map +1 -1
  20. package/dist/cjs/cli.cjs +2 -0
  21. package/dist/cjs/createDictionaryEntryPoint/createDictionaryEntryPoint.cjs +5 -3
  22. package/dist/cjs/createDictionaryEntryPoint/createDictionaryEntryPoint.cjs.map +1 -1
  23. package/dist/cjs/createDictionaryEntryPoint/generateDictionaryListContent.cjs.map +1 -1
  24. package/dist/cjs/createDictionaryEntryPoint/getBuiltDictionariesPath.cjs.map +1 -1
  25. package/dist/cjs/createDictionaryEntryPoint/getBuiltDynamicDictionariesPath.cjs.map +1 -1
  26. package/dist/cjs/createDictionaryEntryPoint/getBuiltFetchDictionariesPath.cjs.map +1 -1
  27. package/dist/cjs/createDictionaryEntryPoint/getBuiltRemoteDictionariesPath.cjs.map +1 -1
  28. package/dist/cjs/createDictionaryEntryPoint/getBuiltUnmergedDictionariesPath.cjs.map +1 -1
  29. package/dist/cjs/createType/createModuleAugmentation.cjs +2 -1
  30. package/dist/cjs/createType/createModuleAugmentation.cjs.map +1 -1
  31. package/dist/cjs/createType/createType.cjs.map +1 -1
  32. package/dist/cjs/detectFormatCommand.cjs.map +1 -1
  33. package/dist/cjs/docReview/alignBlocks.cjs +1 -1
  34. package/dist/cjs/docReview/alignBlocks.cjs.map +1 -1
  35. package/dist/cjs/docReview/computeSimilarity.cjs +0 -1
  36. package/dist/cjs/docReview/computeSimilarity.cjs.map +1 -1
  37. package/dist/cjs/docReview/mapChangedLinesToBlocks.cjs +0 -1
  38. package/dist/cjs/docReview/mapChangedLinesToBlocks.cjs.map +1 -1
  39. package/dist/cjs/docReview/normalizeBlock.cjs +0 -1
  40. package/dist/cjs/docReview/normalizeBlock.cjs.map +1 -1
  41. package/dist/cjs/docReview/planActions.cjs +0 -1
  42. package/dist/cjs/docReview/planActions.cjs.map +1 -1
  43. package/dist/cjs/docReview/rebuildDocument.cjs +0 -1
  44. package/dist/cjs/docReview/rebuildDocument.cjs.map +1 -1
  45. package/dist/cjs/docReview/reviewReport.cjs +2 -1
  46. package/dist/cjs/docReview/reviewReport.cjs.map +1 -1
  47. package/dist/cjs/docReview/segmentDocument.cjs +0 -1
  48. package/dist/cjs/docReview/segmentDocument.cjs.map +1 -1
  49. package/dist/cjs/fetchDistantDictionaries.cjs.map +1 -1
  50. package/dist/cjs/filterInvalidDictionaries.cjs.map +1 -1
  51. package/dist/cjs/formatDictionary.cjs.map +1 -1
  52. package/dist/cjs/getContentDeclarationFileTemplate/getContentDeclarationFileTemplate.cjs.map +1 -1
  53. package/dist/cjs/handleAdditionalContentDeclarationFile.cjs +4 -2
  54. package/dist/cjs/handleAdditionalContentDeclarationFile.cjs.map +1 -1
  55. package/dist/cjs/handleContentDeclarationFileChange.cjs +6 -2
  56. package/dist/cjs/handleContentDeclarationFileChange.cjs.map +1 -1
  57. package/dist/cjs/handleContentDeclarationFileMoved.cjs.map +1 -1
  58. package/dist/cjs/handleUnlinkedContentDeclarationFile.cjs +4 -2
  59. package/dist/cjs/handleUnlinkedContentDeclarationFile.cjs.map +1 -1
  60. package/dist/cjs/init/cms.cjs.map +1 -1
  61. package/dist/cjs/init/frameworkSetup/nextAppRouter/detect.cjs.map +1 -1
  62. package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs +1 -3
  63. package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs.map +1 -1
  64. package/dist/cjs/init/frameworkSetup/nextAppRouter/restructure.cjs +2 -1
  65. package/dist/cjs/init/frameworkSetup/nextAppRouter/restructure.cjs.map +1 -1
  66. package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs +0 -1
  67. package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs.map +1 -1
  68. package/dist/cjs/init/frameworkSetup/tanstackStart/detect.cjs.map +1 -1
  69. package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs +3 -5
  70. package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs.map +1 -1
  71. package/dist/cjs/init/frameworkSetup/tanstackStart/restructure.cjs +2 -1
  72. package/dist/cjs/init/frameworkSetup/tanstackStart/restructure.cjs.map +1 -1
  73. package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs +18 -12
  74. package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs.map +1 -1
  75. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +57 -14
  76. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
  77. package/dist/cjs/init/index.cjs +124 -45
  78. package/dist/cjs/init/index.cjs.map +1 -1
  79. package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
  80. package/dist/cjs/init/utils/fileSystem.cjs +3 -1
  81. package/dist/cjs/init/utils/fileSystem.cjs.map +1 -1
  82. package/dist/cjs/init/utils/githubActions.cjs.map +1 -1
  83. package/dist/cjs/init/utils/index.cjs +3 -0
  84. package/dist/cjs/init/utils/jsonParser.cjs +0 -1
  85. package/dist/cjs/init/utils/jsonParser.cjs.map +1 -1
  86. package/dist/cjs/init/utils/nextCompilerBabel.cjs.map +1 -1
  87. package/dist/cjs/init/utils/packageManager.cjs +105 -7
  88. package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
  89. package/dist/cjs/init/utils/tsConfig.cjs.map +1 -1
  90. package/dist/cjs/init/utils/version.cjs +0 -1
  91. package/dist/cjs/init/utils/version.cjs.map +1 -1
  92. package/dist/cjs/initConfig/index.cjs +12 -8
  93. package/dist/cjs/initConfig/index.cjs.map +1 -1
  94. package/dist/cjs/installSkills/index.cjs.map +1 -1
  95. package/dist/cjs/listDictionariesPath.cjs +6 -5
  96. package/dist/cjs/listDictionariesPath.cjs.map +1 -1
  97. package/dist/cjs/listGitFiles.cjs.map +1 -1
  98. package/dist/cjs/listProjects.cjs +2 -1
  99. package/dist/cjs/listProjects.cjs.map +1 -1
  100. package/dist/cjs/loadDictionaries/getIntlayerBundle.cjs +2 -1
  101. package/dist/cjs/loadDictionaries/getIntlayerBundle.cjs.map +1 -1
  102. package/dist/cjs/loadDictionaries/loadContentDeclaration.cjs +8 -5
  103. package/dist/cjs/loadDictionaries/loadContentDeclaration.cjs.map +1 -1
  104. package/dist/cjs/loadDictionaries/loadDictionaries.cjs.map +1 -1
  105. package/dist/cjs/loadDictionaries/loadLocalDictionaries.cjs.map +1 -1
  106. package/dist/cjs/loadDictionaries/loadMarkdownContentDeclaration.cjs.map +1 -1
  107. package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs.map +1 -1
  108. package/dist/cjs/loadDictionaries/loadYamlContentDeclaration.cjs +2 -1
  109. package/dist/cjs/loadDictionaries/loadYamlContentDeclaration.cjs.map +1 -1
  110. package/dist/cjs/loadDictionaries/log.cjs +10 -6
  111. package/dist/cjs/loadDictionaries/log.cjs.map +1 -1
  112. package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs.map +1 -1
  113. package/dist/cjs/logConfigDetails.cjs +4 -3
  114. package/dist/cjs/logConfigDetails.cjs.map +1 -1
  115. package/dist/cjs/prepareIntlayer.cjs +5 -3
  116. package/dist/cjs/prepareIntlayer.cjs.map +1 -1
  117. package/dist/cjs/reduceDictionaryContent/applyMask.cjs +0 -1
  118. package/dist/cjs/reduceDictionaryContent/applyMask.cjs.map +1 -1
  119. package/dist/cjs/reduceDictionaryContent/reduceDictionaryContent.cjs +2 -1
  120. package/dist/cjs/reduceDictionaryContent/reduceDictionaryContent.cjs.map +1 -1
  121. package/dist/cjs/scan/calculateScore.cjs +0 -1
  122. package/dist/cjs/scan/calculateScore.cjs.map +1 -1
  123. package/dist/cjs/scan/checks.cjs +1 -1
  124. package/dist/cjs/scan/checks.cjs.map +1 -1
  125. package/dist/cjs/scan/parseHtml.cjs +0 -1
  126. package/dist/cjs/scan/parseHtml.cjs.map +1 -1
  127. package/dist/cjs/scan/scanWebsite.cjs.map +1 -1
  128. package/dist/cjs/syncPluginKit/createFileAdapter.cjs +2 -1
  129. package/dist/cjs/syncPluginKit/createFileAdapter.cjs.map +1 -1
  130. package/dist/cjs/syncPluginKit/createSyncPlugin.cjs +4 -1
  131. package/dist/cjs/syncPluginKit/createSyncPlugin.cjs.map +1 -1
  132. package/dist/cjs/syncPluginKit/extractKeyAndLocaleFromPath.cjs +0 -1
  133. package/dist/cjs/syncPluginKit/extractKeyAndLocaleFromPath.cjs.map +1 -1
  134. package/dist/cjs/syncPluginKit/filePathPatternHelpers.cjs +0 -1
  135. package/dist/cjs/syncPluginKit/filePathPatternHelpers.cjs.map +1 -1
  136. package/dist/cjs/utils/autoDecorateContent.cjs.map +1 -1
  137. package/dist/cjs/utils/babelParser.cjs.map +1 -1
  138. package/dist/cjs/utils/buildComponentFilesList.cjs.map +1 -1
  139. package/dist/cjs/utils/buildFilesList.cjs.map +1 -1
  140. package/dist/cjs/utils/chunkArray.cjs +0 -1
  141. package/dist/cjs/utils/chunkArray.cjs.map +1 -1
  142. package/dist/cjs/utils/formatter.cjs.map +1 -1
  143. package/dist/cjs/utils/getContentExtension.cjs +1 -4
  144. package/dist/cjs/utils/getContentExtension.cjs.map +1 -1
  145. package/dist/cjs/utils/getFormatFromExtension.cjs +0 -1
  146. package/dist/cjs/utils/getFormatFromExtension.cjs.map +1 -1
  147. package/dist/cjs/utils/getPathHash.cjs +0 -1
  148. package/dist/cjs/utils/getPathHash.cjs.map +1 -1
  149. package/dist/cjs/utils/mergeChunks.cjs +0 -1
  150. package/dist/cjs/utils/mergeChunks.cjs.map +1 -1
  151. package/dist/cjs/utils/pLimit.cjs +0 -1
  152. package/dist/cjs/utils/pLimit.cjs.map +1 -1
  153. package/dist/cjs/utils/readDictionariesFromDisk.cjs.map +1 -1
  154. package/dist/cjs/utils/reduceObjectFormat.cjs +0 -1
  155. package/dist/cjs/utils/reduceObjectFormat.cjs.map +1 -1
  156. package/dist/cjs/utils/resolveObjectPromises.cjs +0 -1
  157. package/dist/cjs/utils/resolveObjectPromises.cjs.map +1 -1
  158. package/dist/cjs/utils/resolveRelativePath.cjs +4 -1
  159. package/dist/cjs/utils/resolveRelativePath.cjs.map +1 -1
  160. package/dist/cjs/utils/runOnce.cjs +2 -2
  161. package/dist/cjs/utils/runOnce.cjs.map +1 -1
  162. package/dist/cjs/utils/runParallel/bin.cjs.map +1 -1
  163. package/dist/cjs/utils/runParallel/bootstrap.cjs +0 -1
  164. package/dist/cjs/utils/runParallel/bootstrap.cjs.map +1 -1
  165. package/dist/cjs/utils/runParallel/index.cjs.map +1 -1
  166. package/dist/cjs/utils/runParallel/spawnPosix.cjs.map +1 -1
  167. package/dist/cjs/utils/runParallel/spawnWin32.cjs.map +1 -1
  168. package/dist/cjs/utils/sortAlphabetically.cjs +0 -1
  169. package/dist/cjs/utils/sortAlphabetically.cjs.map +1 -1
  170. package/dist/cjs/utils/splitTextByLine.cjs +0 -1
  171. package/dist/cjs/utils/splitTextByLine.cjs.map +1 -1
  172. package/dist/cjs/utils/verifyIdenticObjectFormat.cjs +0 -1
  173. package/dist/cjs/utils/verifyIdenticObjectFormat.cjs.map +1 -1
  174. package/dist/cjs/watcher.cjs +4 -2
  175. package/dist/cjs/watcher.cjs.map +1 -1
  176. package/dist/cjs/writeConfiguration/index.cjs +2 -1
  177. package/dist/cjs/writeConfiguration/index.cjs.map +1 -1
  178. package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs +0 -1
  179. package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs.map +1 -1
  180. package/dist/cjs/writeContentDeclaration/processContentDeclarationContent.cjs.map +1 -1
  181. package/dist/cjs/writeContentDeclaration/transformJSFile.cjs +26 -23
  182. package/dist/cjs/writeContentDeclaration/transformJSFile.cjs.map +1 -1
  183. package/dist/cjs/writeContentDeclaration/transformJSONFile.cjs +6 -5
  184. package/dist/cjs/writeContentDeclaration/transformJSONFile.cjs.map +1 -1
  185. package/dist/cjs/writeContentDeclaration/writeContentDeclaration.cjs +9 -3
  186. package/dist/cjs/writeContentDeclaration/writeContentDeclaration.cjs.map +1 -1
  187. package/dist/cjs/writeContentDeclaration/writeJSFile.cjs +2 -1
  188. package/dist/cjs/writeContentDeclaration/writeJSFile.cjs.map +1 -1
  189. package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs +2 -1
  190. package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs.map +1 -1
  191. package/dist/cjs/writeContentDeclaration/writeYamlFile.cjs +4 -2
  192. package/dist/cjs/writeContentDeclaration/writeYamlFile.cjs.map +1 -1
  193. package/dist/cjs/writeFileIfChanged.cjs.map +1 -1
  194. package/dist/esm/_virtual/_utils_asset.mjs +2 -1
  195. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +2 -1
  196. package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
  197. package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs +2 -1
  198. package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs.map +1 -1
  199. package/dist/esm/buildIntlayerDictionary/writeRemoteDictionary.mjs +4 -2
  200. package/dist/esm/buildIntlayerDictionary/writeRemoteDictionary.mjs.map +1 -1
  201. package/dist/esm/cleanRemovedContentDeclaration.mjs +7 -4
  202. package/dist/esm/cleanRemovedContentDeclaration.mjs.map +1 -1
  203. package/dist/esm/cli.mjs +2 -2
  204. package/dist/esm/createDictionaryEntryPoint/createDictionaryEntryPoint.mjs +5 -3
  205. package/dist/esm/createDictionaryEntryPoint/createDictionaryEntryPoint.mjs.map +1 -1
  206. package/dist/esm/createType/createModuleAugmentation.mjs +2 -1
  207. package/dist/esm/createType/createModuleAugmentation.mjs.map +1 -1
  208. package/dist/esm/docReview/alignBlocks.mjs +1 -1
  209. package/dist/esm/docReview/alignBlocks.mjs.map +1 -1
  210. package/dist/esm/docReview/reviewReport.mjs +2 -1
  211. package/dist/esm/docReview/reviewReport.mjs.map +1 -1
  212. package/dist/esm/handleAdditionalContentDeclarationFile.mjs +4 -2
  213. package/dist/esm/handleAdditionalContentDeclarationFile.mjs.map +1 -1
  214. package/dist/esm/handleContentDeclarationFileChange.mjs +6 -2
  215. package/dist/esm/handleContentDeclarationFileChange.mjs.map +1 -1
  216. package/dist/esm/handleUnlinkedContentDeclarationFile.mjs +4 -2
  217. package/dist/esm/handleUnlinkedContentDeclarationFile.mjs.map +1 -1
  218. package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs +1 -3
  219. package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs.map +1 -1
  220. package/dist/esm/init/frameworkSetup/nextAppRouter/restructure.mjs +2 -1
  221. package/dist/esm/init/frameworkSetup/nextAppRouter/restructure.mjs.map +1 -1
  222. package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs +4 -6
  223. package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs.map +1 -1
  224. package/dist/esm/init/frameworkSetup/tanstackStart/restructure.mjs +2 -1
  225. package/dist/esm/init/frameworkSetup/tanstackStart/restructure.mjs.map +1 -1
  226. package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs +17 -10
  227. package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs.map +1 -1
  228. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +58 -15
  229. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
  230. package/dist/esm/init/index.mjs +125 -46
  231. package/dist/esm/init/index.mjs.map +1 -1
  232. package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
  233. package/dist/esm/init/utils/fileSystem.mjs +3 -1
  234. package/dist/esm/init/utils/fileSystem.mjs.map +1 -1
  235. package/dist/esm/init/utils/index.mjs +2 -2
  236. package/dist/esm/init/utils/packageManager.mjs +103 -8
  237. package/dist/esm/init/utils/packageManager.mjs.map +1 -1
  238. package/dist/esm/initConfig/index.mjs +12 -8
  239. package/dist/esm/initConfig/index.mjs.map +1 -1
  240. package/dist/esm/listDictionariesPath.mjs +6 -5
  241. package/dist/esm/listDictionariesPath.mjs.map +1 -1
  242. package/dist/esm/listProjects.mjs +2 -1
  243. package/dist/esm/listProjects.mjs.map +1 -1
  244. package/dist/esm/loadDictionaries/getIntlayerBundle.mjs +2 -1
  245. package/dist/esm/loadDictionaries/getIntlayerBundle.mjs.map +1 -1
  246. package/dist/esm/loadDictionaries/loadContentDeclaration.mjs +8 -5
  247. package/dist/esm/loadDictionaries/loadContentDeclaration.mjs.map +1 -1
  248. package/dist/esm/loadDictionaries/loadYamlContentDeclaration.mjs +2 -1
  249. package/dist/esm/loadDictionaries/loadYamlContentDeclaration.mjs.map +1 -1
  250. package/dist/esm/loadDictionaries/log.mjs +10 -6
  251. package/dist/esm/loadDictionaries/log.mjs.map +1 -1
  252. package/dist/esm/logConfigDetails.mjs +4 -3
  253. package/dist/esm/logConfigDetails.mjs.map +1 -1
  254. package/dist/esm/prepareIntlayer.mjs +5 -3
  255. package/dist/esm/prepareIntlayer.mjs.map +1 -1
  256. package/dist/esm/reduceDictionaryContent/reduceDictionaryContent.mjs +2 -1
  257. package/dist/esm/reduceDictionaryContent/reduceDictionaryContent.mjs.map +1 -1
  258. package/dist/esm/scan/checks.mjs +1 -1
  259. package/dist/esm/scan/checks.mjs.map +1 -1
  260. package/dist/esm/syncPluginKit/createFileAdapter.mjs +2 -1
  261. package/dist/esm/syncPluginKit/createFileAdapter.mjs.map +1 -1
  262. package/dist/esm/syncPluginKit/createSyncPlugin.mjs +4 -1
  263. package/dist/esm/syncPluginKit/createSyncPlugin.mjs.map +1 -1
  264. package/dist/esm/utils/babelParser.mjs.map +1 -1
  265. package/dist/esm/utils/getContentExtension.mjs +1 -3
  266. package/dist/esm/utils/getContentExtension.mjs.map +1 -1
  267. package/dist/esm/utils/resolveRelativePath.mjs +4 -1
  268. package/dist/esm/utils/resolveRelativePath.mjs.map +1 -1
  269. package/dist/esm/utils/runOnce.mjs +2 -2
  270. package/dist/esm/utils/runOnce.mjs.map +1 -1
  271. package/dist/esm/watcher.mjs +4 -2
  272. package/dist/esm/watcher.mjs.map +1 -1
  273. package/dist/esm/writeConfiguration/index.mjs +2 -1
  274. package/dist/esm/writeConfiguration/index.mjs.map +1 -1
  275. package/dist/esm/writeContentDeclaration/transformJSFile.mjs +26 -23
  276. package/dist/esm/writeContentDeclaration/transformJSFile.mjs.map +1 -1
  277. package/dist/esm/writeContentDeclaration/transformJSONFile.mjs +6 -5
  278. package/dist/esm/writeContentDeclaration/transformJSONFile.mjs.map +1 -1
  279. package/dist/esm/writeContentDeclaration/writeContentDeclaration.mjs +9 -3
  280. package/dist/esm/writeContentDeclaration/writeContentDeclaration.mjs.map +1 -1
  281. package/dist/esm/writeContentDeclaration/writeJSFile.mjs +2 -1
  282. package/dist/esm/writeContentDeclaration/writeJSFile.mjs.map +1 -1
  283. package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs +2 -1
  284. package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs.map +1 -1
  285. package/dist/esm/writeContentDeclaration/writeYamlFile.mjs +4 -2
  286. package/dist/esm/writeContentDeclaration/writeYamlFile.mjs.map +1 -1
  287. package/dist/types/cli.d.ts +2 -2
  288. package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts +9 -4
  289. package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts.map +1 -1
  290. package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts +6 -2
  291. package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts.map +1 -1
  292. package/dist/types/init/index.d.ts +2 -7
  293. package/dist/types/init/index.d.ts.map +1 -1
  294. package/dist/types/init/utils/index.d.ts +2 -2
  295. package/dist/types/init/utils/packageManager.d.ts +39 -6
  296. package/dist/types/init/utils/packageManager.d.ts.map +1 -1
  297. package/package.json +10 -10
@@ -22,7 +22,8 @@ const handleContentDeclarationFileChange = async (filePath, config) => {
22
22
  const updatedDictionariesPaths = updatedDictionaries.map((dictionary) => dictionary.dictionaryPath);
23
23
  const { excludeKeys, hasRebuilt } = await cleanRemovedContentDeclaration(filePath, localeDictionaries.map((dictionary) => dictionary.key), config);
24
24
  const hasNewDictionaries = updatedDictionariesPaths.some((updatedDictionaryPath) => !allDictionariesPaths.includes(updatedDictionaryPath));
25
- await createTypes(updatedDictionaries.map((dictionary) => dictionary.dictionary), config);
25
+ const dictionariesToBuild = updatedDictionaries.map((dictionary) => dictionary.dictionary);
26
+ await createTypes(dictionariesToBuild, config);
26
27
  appLogger("TypeScript types built", { isVerbose: true });
27
28
  await createModuleAugmentation(config);
28
29
  appLogger("Module augmentation built", { isVerbose: true });
@@ -35,7 +36,10 @@ const handleContentDeclarationFileChange = async (filePath, config) => {
35
36
  const { mainDir } = config.system;
36
37
  for (const extension of config.build.outputFormat) {
37
38
  const entryPointPath = join(mainDir, `dictionaries.${extension}`);
38
- if (existsSync(entryPointPath)) await writeFile(entryPointPath, await readFile(entryPointPath, "utf-8"));
39
+ if (existsSync(entryPointPath)) {
40
+ const content = await readFile(entryPointPath, "utf-8");
41
+ await writeFile(entryPointPath, content);
42
+ }
39
43
  }
40
44
  for await (const plugin of config.plugins ?? []) {
41
45
  const { unmergedDictionaries, mergedDictionaries } = dictionariesOutput;
@@ -1 +1 @@
1
- {"version":3,"file":"handleContentDeclarationFileChange.mjs","names":[],"sources":["../../src/handleContentDeclarationFileChange.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { buildDictionary } from './buildIntlayerDictionary/buildIntlayerDictionary';\nimport { cleanRemovedContentDeclaration } from './cleanRemovedContentDeclaration';\nimport { createDictionaryEntryPoint } from './createDictionaryEntryPoint/createDictionaryEntryPoint';\nimport { getBuiltDictionariesPath } from './createDictionaryEntryPoint/getBuiltDictionariesPath';\nimport { createTypes } from './createType';\nimport { createModuleAugmentation } from './createType/createModuleAugmentation';\nimport { loadLocalDictionaries } from './loadDictionaries/loadLocalDictionaries';\nimport { formatPath } from './utils/formatter';\n\nexport const handleContentDeclarationFileChange = async (\n filePath: string,\n config: IntlayerConfig\n) => {\n const appLogger = getAppLogger(config);\n\n // Process the file with the functionToRun\n appLogger(`Change detected: ${formatPath(filePath)}`, {\n isVerbose: true,\n });\n\n const allDictionariesPaths: string[] = await getBuiltDictionariesPath(config);\n\n const localeDictionaries = await loadLocalDictionaries(filePath, config);\n\n const dictionariesOutput = await buildDictionary(localeDictionaries, config);\n const updatedDictionaries = Object.values(\n dictionariesOutput?.mergedDictionaries ?? {}\n );\n const updatedDictionariesPaths = updatedDictionaries.map(\n (dictionary) => dictionary.dictionaryPath\n );\n\n const { excludeKeys, hasRebuilt } = await cleanRemovedContentDeclaration(\n filePath,\n localeDictionaries.map((dictionary) => dictionary.key),\n config\n );\n\n const hasNewDictionaries = updatedDictionariesPaths.some(\n (updatedDictionaryPath) =>\n !allDictionariesPaths.includes(updatedDictionaryPath)\n );\n\n // Rebuild Types first — types must be ready before anything triggers a bundler rebuild.\n // esbuild (used by Angular, Vite, etc.) only watches files in its JS bundle graph; it does\n // NOT watch .intlayer/types/*.ts (TypeScript-only files). Generating types here, before\n // updating the entry point, guarantees that when the entry point write below fires esbuild's\n // file watcher the types are already up-to-date.\n const dictionariesToBuild = updatedDictionaries.map(\n (dictionary) => dictionary.dictionary\n );\n\n await createTypes(dictionariesToBuild, config);\n appLogger('TypeScript types built', {\n isVerbose: true,\n });\n\n // Always regenerate the module augmentation so that structural changes to existing\n // dictionaries (new content keys, type changes) are reflected in intlayer.d.ts.\n await createModuleAugmentation(config);\n appLogger('Module augmentation built', {\n isVerbose: true,\n });\n\n // Rebuild Entry Point\n // Only needed when the *list* of dictionaries changed (Add/Remove/Rename).\n if (hasRebuilt || hasNewDictionaries) {\n // If hasRebuilt is true, cleanRemovedContentDeclaration has already updated the entry point\n // to remove the old keys (and it likely included the new ones if they were already on disk).\n // If NOT hasRebuilt, we explicitly need to update the entry point to include the new dictionaries.\n if (!hasRebuilt) {\n await createDictionaryEntryPoint(config, { excludeKeys });\n appLogger('Dictionary list built', {\n isVerbose: true,\n });\n }\n }\n\n // Force-write the entry point files so esbuild detects a change and triggers a fresh rebuild\n // now that all types are ready. Without this, the only rebuild esbuild sees is the one\n // triggered by buildDictionary's JSON writes above — which races against type generation\n // and often runs before the new .intlayer/types/*.ts files are on disk.\n const { mainDir } = config.system;\n\n for (const extension of config.build.outputFormat) {\n const entryPointPath = join(mainDir, `dictionaries.${extension}`);\n\n if (existsSync(entryPointPath)) {\n const content = await readFile(entryPointPath, 'utf-8');\n await writeFile(entryPointPath, content);\n }\n }\n\n // Plugin transformation\n // Allow plugins to post-process the final build output (e.g., write back ICU JSON)\n for await (const plugin of config.plugins ?? []) {\n const { unmergedDictionaries, mergedDictionaries } = dictionariesOutput;\n\n await plugin.afterBuild?.({\n dictionaries: {\n unmergedDictionaries,\n mergedDictionaries,\n },\n configuration: config,\n });\n }\n};\n"],"mappings":";;;;;;;;;;;;;;AAcA,MAAa,qCAAqC,OAChD,UACA,WACG;CACH,MAAM,YAAY,aAAa,MAAM;CAGrC,UAAU,oBAAoB,WAAW,QAAQ,KAAK,EACpD,WAAW,KACb,CAAC;CAED,MAAM,uBAAiC,MAAM,yBAAyB,MAAM;CAE5E,MAAM,qBAAqB,MAAM,sBAAsB,UAAU,MAAM;CAEvE,MAAM,qBAAqB,MAAM,gBAAgB,oBAAoB,MAAM;CAC3E,MAAM,sBAAsB,OAAO,OACjC,oBAAoB,sBAAsB,CAAC,CAC7C;CACA,MAAM,2BAA2B,oBAAoB,KAClD,eAAe,WAAW,cAC7B;CAEA,MAAM,EAAE,aAAa,eAAe,MAAM,+BACxC,UACA,mBAAmB,KAAK,eAAe,WAAW,GAAG,GACrD,MACF;CAEA,MAAM,qBAAqB,yBAAyB,MACjD,0BACC,CAAC,qBAAqB,SAAS,qBAAqB,CACxD;CAWA,MAAM,YAJsB,oBAAoB,KAC7C,eAAe,WAAW,UAGO,GAAG,MAAM;CAC7C,UAAU,0BAA0B,EAClC,WAAW,KACb,CAAC;CAID,MAAM,yBAAyB,MAAM;CACrC,UAAU,6BAA6B,EACrC,WAAW,KACb,CAAC;CAID,IAAI,cAAc,oBAIhB;MAAI,CAAC,YAAY;GACf,MAAM,2BAA2B,QAAQ,EAAE,YAAY,CAAC;GACxD,UAAU,yBAAyB,EACjC,WAAW,KACb,CAAC;EACH;;CAOF,MAAM,EAAE,YAAY,OAAO;CAE3B,KAAK,MAAM,aAAa,OAAO,MAAM,cAAc;EACjD,MAAM,iBAAiB,KAAK,SAAS,gBAAgB,WAAW;EAEhE,IAAI,WAAW,cAAc,GAE3B,MAAM,UAAU,gBAAgB,MADV,SAAS,gBAAgB,OAAO,CACf;CAE3C;CAIA,WAAW,MAAM,UAAU,OAAO,WAAW,CAAC,GAAG;EAC/C,MAAM,EAAE,sBAAsB,uBAAuB;EAErD,MAAM,OAAO,aAAa;GACxB,cAAc;IACZ;IACA;GACF;GACA,eAAe;EACjB,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"handleContentDeclarationFileChange.mjs","names":[],"sources":["../../src/handleContentDeclarationFileChange.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { buildDictionary } from './buildIntlayerDictionary/buildIntlayerDictionary';\nimport { cleanRemovedContentDeclaration } from './cleanRemovedContentDeclaration';\nimport { createDictionaryEntryPoint } from './createDictionaryEntryPoint/createDictionaryEntryPoint';\nimport { getBuiltDictionariesPath } from './createDictionaryEntryPoint/getBuiltDictionariesPath';\nimport { createTypes } from './createType';\nimport { createModuleAugmentation } from './createType/createModuleAugmentation';\nimport { loadLocalDictionaries } from './loadDictionaries/loadLocalDictionaries';\nimport { formatPath } from './utils/formatter';\n\nexport const handleContentDeclarationFileChange = async (\n filePath: string,\n config: IntlayerConfig\n) => {\n const appLogger = getAppLogger(config);\n\n // Process the file with the functionToRun\n appLogger(`Change detected: ${formatPath(filePath)}`, {\n isVerbose: true,\n });\n\n const allDictionariesPaths: string[] = await getBuiltDictionariesPath(config);\n\n const localeDictionaries = await loadLocalDictionaries(filePath, config);\n\n const dictionariesOutput = await buildDictionary(localeDictionaries, config);\n const updatedDictionaries = Object.values(\n dictionariesOutput?.mergedDictionaries ?? {}\n );\n const updatedDictionariesPaths = updatedDictionaries.map(\n (dictionary) => dictionary.dictionaryPath\n );\n\n const { excludeKeys, hasRebuilt } = await cleanRemovedContentDeclaration(\n filePath,\n localeDictionaries.map((dictionary) => dictionary.key),\n config\n );\n\n const hasNewDictionaries = updatedDictionariesPaths.some(\n (updatedDictionaryPath) =>\n !allDictionariesPaths.includes(updatedDictionaryPath)\n );\n\n // Rebuild Types first — types must be ready before anything triggers a bundler rebuild.\n // esbuild (used by Angular, Vite, etc.) only watches files in its JS bundle graph; it does\n // NOT watch .intlayer/types/*.ts (TypeScript-only files). Generating types here, before\n // updating the entry point, guarantees that when the entry point write below fires esbuild's\n // file watcher the types are already up-to-date.\n const dictionariesToBuild = updatedDictionaries.map(\n (dictionary) => dictionary.dictionary\n );\n\n await createTypes(dictionariesToBuild, config);\n appLogger('TypeScript types built', {\n isVerbose: true,\n });\n\n // Always regenerate the module augmentation so that structural changes to existing\n // dictionaries (new content keys, type changes) are reflected in intlayer.d.ts.\n await createModuleAugmentation(config);\n appLogger('Module augmentation built', {\n isVerbose: true,\n });\n\n // Rebuild Entry Point\n // Only needed when the *list* of dictionaries changed (Add/Remove/Rename).\n if (hasRebuilt || hasNewDictionaries) {\n // If hasRebuilt is true, cleanRemovedContentDeclaration has already updated the entry point\n // to remove the old keys (and it likely included the new ones if they were already on disk).\n // If NOT hasRebuilt, we explicitly need to update the entry point to include the new dictionaries.\n if (!hasRebuilt) {\n await createDictionaryEntryPoint(config, { excludeKeys });\n appLogger('Dictionary list built', {\n isVerbose: true,\n });\n }\n }\n\n // Force-write the entry point files so esbuild detects a change and triggers a fresh rebuild\n // now that all types are ready. Without this, the only rebuild esbuild sees is the one\n // triggered by buildDictionary's JSON writes above — which races against type generation\n // and often runs before the new .intlayer/types/*.ts files are on disk.\n const { mainDir } = config.system;\n\n for (const extension of config.build.outputFormat) {\n const entryPointPath = join(mainDir, `dictionaries.${extension}`);\n\n if (existsSync(entryPointPath)) {\n const content = await readFile(entryPointPath, 'utf-8');\n await writeFile(entryPointPath, content);\n }\n }\n\n // Plugin transformation\n // Allow plugins to post-process the final build output (e.g., write back ICU JSON)\n for await (const plugin of config.plugins ?? []) {\n const { unmergedDictionaries, mergedDictionaries } = dictionariesOutput;\n\n await plugin.afterBuild?.({\n dictionaries: {\n unmergedDictionaries,\n mergedDictionaries,\n },\n configuration: config,\n });\n }\n};\n"],"mappings":";;;;;;;;;;;;;;AAcA,MAAa,qCAAqC,OAChD,UACA,WACG;CACH,MAAM,YAAY,aAAa,MAAM;CAGrC,UAAU,oBAAoB,WAAW,QAAQ,KAAK,EACpD,WAAW,KACb,CAAC;CAED,MAAM,uBAAiC,MAAM,yBAAyB,MAAM;CAE5E,MAAM,qBAAqB,MAAM,sBAAsB,UAAU,MAAM;CAEvE,MAAM,qBAAqB,MAAM,gBAAgB,oBAAoB,MAAM;CAC3E,MAAM,sBAAsB,OAAO,OACjC,oBAAoB,sBAAsB,CAAC,CAC7C;CACA,MAAM,2BAA2B,oBAAoB,KAClD,eAAe,WAAW,cAC7B;CAEA,MAAM,EAAE,aAAa,eAAe,MAAM,+BACxC,UACA,mBAAmB,KAAK,eAAe,WAAW,GAAG,GACrD,MACF;CAEA,MAAM,qBAAqB,yBAAyB,MACjD,0BACC,CAAC,qBAAqB,SAAS,qBAAqB,CACxD;CAOA,MAAM,sBAAsB,oBAAoB,KAC7C,eAAe,WAAW,UAC7B;CAEA,MAAM,YAAY,qBAAqB,MAAM;CAC7C,UAAU,0BAA0B,EAClC,WAAW,KACb,CAAC;CAID,MAAM,yBAAyB,MAAM;CACrC,UAAU,6BAA6B,EACrC,WAAW,KACb,CAAC;CAID,IAAI,cAAc,oBAIhB;MAAI,CAAC,YAAY;GACf,MAAM,2BAA2B,QAAQ,EAAE,YAAY,CAAC;GACxD,UAAU,yBAAyB,EACjC,WAAW,KACb,CAAC;EACH;;CAOF,MAAM,EAAE,YAAY,OAAO;CAE3B,KAAK,MAAM,aAAa,OAAO,MAAM,cAAc;EACjD,MAAM,iBAAiB,KAAK,SAAS,gBAAgB,WAAW;EAEhE,IAAI,WAAW,cAAc,GAAG;GAC9B,MAAM,UAAU,MAAM,SAAS,gBAAgB,OAAO;GACtD,MAAM,UAAU,gBAAgB,OAAO;EACzC;CACF;CAIA,WAAW,MAAM,UAAU,OAAO,WAAW,CAAC,GAAG;EAC/C,MAAM,EAAE,sBAAsB,uBAAuB;EAErD,MAAM,OAAO,aAAa;GACxB,cAAc;IACZ;IACA;GACF;GACA,eAAe;EACjB,CAAC;CACH;AACF"}
@@ -12,10 +12,12 @@ import { getAppLogger } from "@intlayer/config/logger";
12
12
  const handleUnlinkedContentDeclarationFile = async (filePath, config) => {
13
13
  const appLogger = getAppLogger(config);
14
14
  appLogger(`Unlinked detected: ${formatPath(filePath)}`, { isVerbose: true });
15
- const localeDictionaries = await loadLocalDictionaries((await listDictionaries(config)).filter((file) => normalize(file) !== normalize(filePath)), config);
15
+ const existingFiles = (await listDictionaries(config)).filter((file) => normalize(file) !== normalize(filePath));
16
+ const localeDictionaries = await loadLocalDictionaries(existingFiles, config);
16
17
  await cleanRemovedContentDeclaration(filePath, [], config);
17
18
  const dictionariesOutput = await buildDictionary(localeDictionaries, config);
18
- await createTypes(Object.values(dictionariesOutput?.mergedDictionaries ?? {}).map((dictionary) => dictionary.dictionary), config);
19
+ const dictionariesToBuild = Object.values(dictionariesOutput?.mergedDictionaries ?? {}).map((dictionary) => dictionary.dictionary);
20
+ await createTypes(dictionariesToBuild, config);
19
21
  appLogger("Dictionaries rebuilt", { isVerbose: true });
20
22
  await createModuleAugmentation(config);
21
23
  appLogger("Module augmentation built", { isVerbose: true });
@@ -1 +1 @@
1
- {"version":3,"file":"handleUnlinkedContentDeclarationFile.mjs","names":[],"sources":["../../src/handleUnlinkedContentDeclarationFile.ts"],"sourcesContent":["import { normalize } from 'node:path';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { buildDictionary } from './buildIntlayerDictionary/buildIntlayerDictionary';\nimport { cleanRemovedContentDeclaration } from './cleanRemovedContentDeclaration';\nimport { createTypes } from './createType';\nimport { createModuleAugmentation } from './createType/createModuleAugmentation';\nimport { listDictionaries } from './listDictionariesPath';\nimport { loadLocalDictionaries } from './loadDictionaries/loadLocalDictionaries';\nimport { formatPath } from './utils/formatter';\n\nexport const handleUnlinkedContentDeclarationFile = async (\n filePath: string,\n config: IntlayerConfig\n) => {\n const appLogger = getAppLogger(config);\n\n // Process the file with the functionToRun\n appLogger(`Unlinked detected: ${formatPath(filePath)}`, {\n isVerbose: true,\n });\n\n const files: string[] = await listDictionaries(config);\n\n const existingFiles = files.filter(\n (file) => normalize(file) !== normalize(filePath)\n );\n\n const localeDictionaries = await loadLocalDictionaries(existingFiles, config);\n\n await cleanRemovedContentDeclaration(filePath, [], config);\n\n const dictionariesOutput = await buildDictionary(localeDictionaries, config);\n\n const dictionariesToBuild = Object.values(\n dictionariesOutput?.mergedDictionaries ?? {}\n ).map((dictionary) => dictionary.dictionary);\n\n await createTypes(dictionariesToBuild, config);\n\n appLogger('Dictionaries rebuilt', {\n isVerbose: true,\n });\n\n await createModuleAugmentation(config);\n\n appLogger('Module augmentation built', {\n isVerbose: true,\n });\n\n // Plugin transformation\n // Allow plugins to post-process the final build output (e.g., write back ICU JSON)\n for await (const plugin of config.plugins ?? []) {\n const { unmergedDictionaries, mergedDictionaries } = dictionariesOutput;\n\n await plugin.afterBuild?.({\n dictionaries: {\n unmergedDictionaries,\n mergedDictionaries,\n },\n configuration: config,\n });\n }\n};\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,uCAAuC,OAClD,UACA,WACG;CACH,MAAM,YAAY,aAAa,MAAM;CAGrC,UAAU,sBAAsB,WAAW,QAAQ,KAAK,EACtD,WAAW,KACb,CAAC;CAQD,MAAM,qBAAqB,MAAM,uBAJX,MAFQ,iBAAiB,MAAM,EAE1B,CAAC,QACzB,SAAS,UAAU,IAAI,MAAM,UAAU,QAAQ,CAGiB,GAAG,MAAM;CAE5E,MAAM,+BAA+B,UAAU,CAAC,GAAG,MAAM;CAEzD,MAAM,qBAAqB,MAAM,gBAAgB,oBAAoB,MAAM;CAM3E,MAAM,YAJsB,OAAO,OACjC,oBAAoB,sBAAsB,CAAC,CAC7C,CAAC,CAAC,KAAK,eAAe,WAAW,UAEG,GAAG,MAAM;CAE7C,UAAU,wBAAwB,EAChC,WAAW,KACb,CAAC;CAED,MAAM,yBAAyB,MAAM;CAErC,UAAU,6BAA6B,EACrC,WAAW,KACb,CAAC;CAID,WAAW,MAAM,UAAU,OAAO,WAAW,CAAC,GAAG;EAC/C,MAAM,EAAE,sBAAsB,uBAAuB;EAErD,MAAM,OAAO,aAAa;GACxB,cAAc;IACZ;IACA;GACF;GACA,eAAe;EACjB,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"handleUnlinkedContentDeclarationFile.mjs","names":[],"sources":["../../src/handleUnlinkedContentDeclarationFile.ts"],"sourcesContent":["import { normalize } from 'node:path';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { buildDictionary } from './buildIntlayerDictionary/buildIntlayerDictionary';\nimport { cleanRemovedContentDeclaration } from './cleanRemovedContentDeclaration';\nimport { createTypes } from './createType';\nimport { createModuleAugmentation } from './createType/createModuleAugmentation';\nimport { listDictionaries } from './listDictionariesPath';\nimport { loadLocalDictionaries } from './loadDictionaries/loadLocalDictionaries';\nimport { formatPath } from './utils/formatter';\n\nexport const handleUnlinkedContentDeclarationFile = async (\n filePath: string,\n config: IntlayerConfig\n) => {\n const appLogger = getAppLogger(config);\n\n // Process the file with the functionToRun\n appLogger(`Unlinked detected: ${formatPath(filePath)}`, {\n isVerbose: true,\n });\n\n const files: string[] = await listDictionaries(config);\n\n const existingFiles = files.filter(\n (file) => normalize(file) !== normalize(filePath)\n );\n\n const localeDictionaries = await loadLocalDictionaries(existingFiles, config);\n\n await cleanRemovedContentDeclaration(filePath, [], config);\n\n const dictionariesOutput = await buildDictionary(localeDictionaries, config);\n\n const dictionariesToBuild = Object.values(\n dictionariesOutput?.mergedDictionaries ?? {}\n ).map((dictionary) => dictionary.dictionary);\n\n await createTypes(dictionariesToBuild, config);\n\n appLogger('Dictionaries rebuilt', {\n isVerbose: true,\n });\n\n await createModuleAugmentation(config);\n\n appLogger('Module augmentation built', {\n isVerbose: true,\n });\n\n // Plugin transformation\n // Allow plugins to post-process the final build output (e.g., write back ICU JSON)\n for await (const plugin of config.plugins ?? []) {\n const { unmergedDictionaries, mergedDictionaries } = dictionariesOutput;\n\n await plugin.afterBuild?.({\n dictionaries: {\n unmergedDictionaries,\n mergedDictionaries,\n },\n configuration: config,\n });\n }\n};\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,uCAAuC,OAClD,UACA,WACG;CACH,MAAM,YAAY,aAAa,MAAM;CAGrC,UAAU,sBAAsB,WAAW,QAAQ,KAAK,EACtD,WAAW,KACb,CAAC;CAID,MAAM,iBAAgB,MAFQ,iBAAiB,MAAM,EAE1B,CAAC,QACzB,SAAS,UAAU,IAAI,MAAM,UAAU,QAAQ,CAClD;CAEA,MAAM,qBAAqB,MAAM,sBAAsB,eAAe,MAAM;CAE5E,MAAM,+BAA+B,UAAU,CAAC,GAAG,MAAM;CAEzD,MAAM,qBAAqB,MAAM,gBAAgB,oBAAoB,MAAM;CAE3E,MAAM,sBAAsB,OAAO,OACjC,oBAAoB,sBAAsB,CAAC,CAC7C,CAAC,CAAC,KAAK,eAAe,WAAW,UAAU;CAE3C,MAAM,YAAY,qBAAqB,MAAM;CAE7C,UAAU,wBAAwB,EAChC,WAAW,KACb,CAAC;CAED,MAAM,yBAAyB,MAAM;CAErC,UAAU,6BAA6B,EACrC,WAAW,KACb,CAAC;CAID,WAAW,MAAM,UAAU,OAAO,WAAW,CAAC,GAAG;EAC/C,MAAM,EAAE,sBAAsB,uBAAuB;EAErD,MAAM,OAAO,aAAa;GACxB,cAAc;IACZ;IACA;GACF;GACA,eAAe;EACjB,CAAC;CACH;AACF"}
@@ -44,9 +44,7 @@ const logTransformOutcome = (filePath, result) => {
44
44
  case "skipped-client":
45
45
  logger(`${x} ${colorizePath(filePath)} is a client component — add the Intlayer provider manually.`, { level: "warn" });
46
46
  break;
47
- case "skipped":
48
- logger(`${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wire it manually.`, { level: "warn" });
49
- break;
47
+ case "skipped": logger(`${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wire it manually.`, { level: "warn" });
50
48
  }
51
49
  };
52
50
  /** Transforms an existing file in place, writing it back only when changed. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"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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"}
1
+ {"version":3,"file":"index.mjs","names":[],"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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"}
@@ -142,7 +142,8 @@ const restructureAppIntoLocale = async (rootDir, appDir) => {
142
142
  movedTopLevelNames
143
143
  };
144
144
  for (const newAbs of movedFiles) {
145
- const oldAbs = join(appDirAbs, relative(localeDirAbs, newAbs));
145
+ const relFromLocale = relative(localeDirAbs, newAbs);
146
+ const oldAbs = join(appDirAbs, relFromLocale);
146
147
  const code = await readFile(newAbs, "utf8");
147
148
  const rewritten = rewriteRelativeImports(code, oldAbs, newAbs, rewriteContext);
148
149
  if (rewritten !== code) await writeFile(newAbs, rewritten, "utf8");
@@ -1 +1 @@
1
- {"version":3,"file":"restructure.mjs","names":[],"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';\nimport { babelTsParser } from '../../../utils/babelParser';\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, { parser: babelTsParser });\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
+ {"version":3,"file":"restructure.mjs","names":[],"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';\nimport { babelTsParser } from '../../../utils/babelParser';\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, { parser: babelTsParser });\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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"}
@@ -2,7 +2,7 @@ import { ensureDirectory, exists, readFileFromRoot, writeFileToRoot } from "../.
2
2
  import { findAppFile } from "../nextAppRouter/detect.mjs";
3
3
  import { detectTanStackRoutesDir, hasTanStackStartDeps } from "./detect.mjs";
4
4
  import { getLocaleSegment, restructureRoutesIntoLocale } from "./restructure.mjs";
5
- import { ROOT_TEMPLATE_JS, ROOT_TEMPLATE_TS, buildLocaleRouteTemplate } from "./templates.mjs";
5
+ import { buildLocaleRouteTemplate, buildRootTemplateJs, buildRootTemplateTs } from "./templates.mjs";
6
6
  import { wrapRootWithProvider } from "./transforms.mjs";
7
7
  import { join } from "node:path";
8
8
  import { colorize, colorizePath, logger, v, x } from "@intlayer/config/logger";
@@ -18,9 +18,7 @@ const logTransformOutcome = (filePath, result) => {
18
18
  case "already":
19
19
  logger(`${v} ${colorizePath(filePath)} already wraps the Intlayer provider`);
20
20
  break;
21
- case "skipped":
22
- logger(`${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wrap the document's {children} with <IntlayerProvider locale={locale}> manually.`, { level: "warn" });
23
- break;
21
+ case "skipped": logger(`${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wrap the document's {children} with <IntlayerProvider locale={locale}> manually.`, { level: "warn" });
24
22
  }
25
23
  };
26
24
  /** Transforms an existing file in place, writing it back only when changed. */
@@ -63,8 +61,8 @@ const tanStackStartAdapter = {
63
61
  await ensureDirectory(rootDir, localeDir);
64
62
  await createIfMissing(rootDir, join(localeDir, `route.${scriptExtension}`), buildLocaleRouteTemplate(activeLocaleSegment), "locale route");
65
63
  const existingRoot = await findAppFile(rootDir, routesDir, "__root");
66
- if (existingRoot) await transformExistingFile(rootDir, existingRoot, wrapRootWithProvider);
67
- else await createIfMissing(rootDir, join(routesDir, `__root.${scriptExtension}`), useTypeScript ? ROOT_TEMPLATE_TS : ROOT_TEMPLATE_JS, "root document");
64
+ if (existingRoot) await transformExistingFile(rootDir, existingRoot, (code) => wrapRootWithProvider(code, activeLocaleSegment));
65
+ else await createIfMissing(rootDir, join(routesDir, `__root.${scriptExtension}`), useTypeScript ? buildRootTemplateTs(activeLocaleSegment) : buildRootTemplateJs(activeLocaleSegment), "root document");
68
66
  }
69
67
  };
70
68
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/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 { findAppFile } from '../nextAppRouter/detect';\nimport type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectTanStackRoutesDir, hasTanStackStartDeps } from './detect';\nimport { getLocaleSegment, restructureRoutesIntoLocale } from './restructure';\nimport {\n buildLocaleRouteTemplate,\n ROOT_TEMPLATE_JS,\n ROOT_TEMPLATE_TS,\n} from './templates';\nimport { type TransformResult, wrapRootWithProvider } from './transforms';\n\n/** Logs the outcome of the root-document 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':\n logger(\n `${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wrap the document's {children} with <IntlayerProvider locale={locale}> 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 * TanStack Start adapter. Restructures the file-based routes under a `{-$locale}`\n * segment, scaffolds the locale segment route, and wraps the root document with\n * the Intlayer provider — all idempotently and without overwriting user code it\n * cannot confidently transform.\n */\nexport const tanStackStartAdapter: FrameworkAdapter = {\n name: 'TanStack Start',\n\n detect: async ({ rootDir, allDeps }) => {\n if (!hasTanStackStartDeps(allDeps)) return false;\n return (await detectTanStackRoutesDir(rootDir)) !== null;\n },\n\n setup: async ({\n rootDir,\n useTypeScript,\n routingMode,\n }: FrameworkSetupContext) => {\n const routesInfo = await detectTanStackRoutesDir(rootDir);\n if (!routesInfo) return;\n\n const { routesDir } = routesInfo;\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n // `prefix-all` needs the required `$locale` segment; every other mode uses\n // the optional `{-$locale}` segment so the default locale stays prefix-free.\n const targetLocaleSegment = getLocaleSegment(routingMode);\n\n logger(\n colorize('Setting up TanStack Start integration...', ANSIColors.CYAN)\n );\n\n // 1. Move routable route entries under the locale segment (idempotent).\n const restructureResult = await restructureRoutesIntoLocale(\n rootDir,\n routesDir,\n targetLocaleSegment\n );\n if (restructureResult.status === 'moved') {\n logger(\n `${v} Restructured routes under ${colorizePath(join(routesDir, targetLocaleSegment))}: ${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(routesDir, restructureResult.localeSegment))} already exists, skipping restructure`\n );\n }\n\n // Scaffold the locale route for the segment actually in use: a pre-existing\n // segment (e.g. a prefix-all `$locale` the user already set up) wins over the\n // target, so we never create a second, conflicting locale segment.\n const activeLocaleSegment =\n restructureResult.status === 'already-structured'\n ? restructureResult.localeSegment\n : targetLocaleSegment;\n\n const localeDir = join(routesDir, activeLocaleSegment);\n await ensureDirectory(rootDir, localeDir);\n\n // 2. Locale segment route (`<segment>/route.tsx`) — create when absent.\n await createIfMissing(\n rootDir,\n join(localeDir, `route.${scriptExtension}`),\n buildLocaleRouteTemplate(activeLocaleSegment),\n 'locale route'\n );\n\n // 3. Root document — transform an existing `__root`, else scaffold one.\n const existingRoot = await findAppFile(rootDir, routesDir, '__root');\n if (existingRoot) {\n await transformExistingFile(rootDir, existingRoot, wrapRootWithProvider);\n } else {\n await createIfMissing(\n rootDir,\n join(routesDir, `__root.${scriptExtension}`),\n useTypeScript ? ROOT_TEMPLATE_TS : ROOT_TEMPLATE_JS,\n 'root document'\n );\n }\n },\n};\n"],"mappings":";;;;;;;;;;;;AAqBA,MAAM,uBACJ,UACA,WACS;CACT,QAAQ,OAAO,QAAf;EACE,KAAK;GACH,OAAO,GAAG,EAAE,8BAA8B,aAAa,QAAQ,GAAG;GAClE;EACF,KAAK;GACH,OACE,GAAG,EAAE,GAAG,aAAa,QAAQ,EAAE,qCACjC;GACA;EACF,KAAK;GACH,OACE,GAAG,EAAE,iDAAiD,aAAa,QAAQ,EAAE,6FAC7E,EAAE,OAAO,OAAO,CAClB;GACA;CACJ;AACF;;AAGA,MAAM,wBAAwB,OAC5B,SACA,UACA,cACkB;CAElB,MAAM,SAAS,UAAU,MADN,iBAAiB,SAAS,QAAQ,CACxB;CAC7B,IAAI,OAAO,WAAW,WACpB,MAAM,gBAAgB,SAAS,UAAU,OAAO,IAAI;CAEtD,oBAAoB,UAAU,MAAM;AACtC;;AAGA,MAAM,kBAAkB,OACtB,SACA,UACA,SACA,gBACqB;CACrB,IAAI,MAAM,OAAO,SAAS,QAAQ,GAAG,OAAO;CAC5C,MAAM,gBAAgB,SAAS,UAAU,OAAO;CAChD,OAAO,GAAG,EAAE,WAAW,YAAY,GAAG,aAAa,QAAQ,GAAG;CAC9D,OAAO;AACT;;;;;;;AAQA,MAAa,uBAAyC;CACpD,MAAM;CAEN,QAAQ,OAAO,EAAE,SAAS,cAAc;EACtC,IAAI,CAAC,qBAAqB,OAAO,GAAG,OAAO;EAC3C,OAAQ,MAAM,wBAAwB,OAAO,MAAO;CACtD;CAEA,OAAO,OAAO,EACZ,SACA,eACA,kBAC2B;EAC3B,MAAM,aAAa,MAAM,wBAAwB,OAAO;EACxD,IAAI,CAAC,YAAY;EAEjB,MAAM,EAAE,cAAc;EACtB,MAAM,kBAAkB,gBAAgB,QAAQ;EAIhD,MAAM,sBAAsB,iBAAiB,WAAW;EAExD,OACE,SAAS,4CAA4C,WAAW,IAAI,CACtE;EAGA,MAAM,oBAAoB,MAAM,4BAC9B,SACA,WACA,mBACF;EACA,IAAI,kBAAkB,WAAW,SAC/B,OACE,GAAG,EAAE,6BAA6B,aAAa,KAAK,WAAW,mBAAmB,CAAC,EAAE,IAAI,kBAAkB,aACxG,KAAK,UAAU,SAAS,OAAO,WAAW,OAAO,CAAC,CAAC,CACnD,KAAK,IAAI,GACd;OACK,IAAI,kBAAkB,WAAW,sBACtC,OACE,GAAG,EAAE,GAAG,aAAa,KAAK,WAAW,kBAAkB,aAAa,CAAC,EAAE,sCACzE;EAMF,MAAM,sBACJ,kBAAkB,WAAW,uBACzB,kBAAkB,gBAClB;EAEN,MAAM,YAAY,KAAK,WAAW,mBAAmB;EACrD,MAAM,gBAAgB,SAAS,SAAS;EAGxC,MAAM,gBACJ,SACA,KAAK,WAAW,SAAS,iBAAiB,GAC1C,yBAAyB,mBAAmB,GAC5C,cACF;EAGA,MAAM,eAAe,MAAM,YAAY,SAAS,WAAW,QAAQ;EACnE,IAAI,cACF,MAAM,sBAAsB,SAAS,cAAc,oBAAoB;OAEvE,MAAM,gBACJ,SACA,KAAK,WAAW,UAAU,iBAAiB,GAC3C,gBAAgB,mBAAmB,kBACnC,eACF;CAEJ;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/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 { findAppFile } from '../nextAppRouter/detect';\nimport type { FrameworkAdapter, FrameworkSetupContext } from '../types';\nimport { detectTanStackRoutesDir, hasTanStackStartDeps } from './detect';\nimport { getLocaleSegment, restructureRoutesIntoLocale } from './restructure';\nimport {\n buildLocaleRouteTemplate,\n buildRootTemplateJs,\n buildRootTemplateTs,\n} from './templates';\nimport { type TransformResult, wrapRootWithProvider } from './transforms';\n\n/** Logs the outcome of the root-document 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':\n logger(\n `${x} Could not safely add the Intlayer provider to ${colorizePath(filePath)} — please wrap the document's {children} with <IntlayerProvider locale={locale}> 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 * TanStack Start adapter. Restructures the file-based routes under a `{-$locale}`\n * segment, scaffolds the locale segment route, and wraps the root document with\n * the Intlayer provider — all idempotently and without overwriting user code it\n * cannot confidently transform.\n */\nexport const tanStackStartAdapter: FrameworkAdapter = {\n name: 'TanStack Start',\n\n detect: async ({ rootDir, allDeps }) => {\n if (!hasTanStackStartDeps(allDeps)) return false;\n return (await detectTanStackRoutesDir(rootDir)) !== null;\n },\n\n setup: async ({\n rootDir,\n useTypeScript,\n routingMode,\n }: FrameworkSetupContext) => {\n const routesInfo = await detectTanStackRoutesDir(rootDir);\n if (!routesInfo) return;\n\n const { routesDir } = routesInfo;\n const scriptExtension = useTypeScript ? 'tsx' : 'jsx';\n\n // `prefix-all` needs the required `$locale` segment; every other mode uses\n // the optional `{-$locale}` segment so the default locale stays prefix-free.\n const targetLocaleSegment = getLocaleSegment(routingMode);\n\n logger(\n colorize('Setting up TanStack Start integration...', ANSIColors.CYAN)\n );\n\n // 1. Move routable route entries under the locale segment (idempotent).\n const restructureResult = await restructureRoutesIntoLocale(\n rootDir,\n routesDir,\n targetLocaleSegment\n );\n if (restructureResult.status === 'moved') {\n logger(\n `${v} Restructured routes under ${colorizePath(join(routesDir, targetLocaleSegment))}: ${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(routesDir, restructureResult.localeSegment))} already exists, skipping restructure`\n );\n }\n\n // Scaffold the locale route for the segment actually in use: a pre-existing\n // segment (e.g. a prefix-all `$locale` the user already set up) wins over the\n // target, so we never create a second, conflicting locale segment.\n const activeLocaleSegment =\n restructureResult.status === 'already-structured'\n ? restructureResult.localeSegment\n : targetLocaleSegment;\n\n const localeDir = join(routesDir, activeLocaleSegment);\n await ensureDirectory(rootDir, localeDir);\n\n // 2. Locale segment route (`<segment>/route.tsx`) — create when absent.\n await createIfMissing(\n rootDir,\n join(localeDir, `route.${scriptExtension}`),\n buildLocaleRouteTemplate(activeLocaleSegment),\n 'locale route'\n );\n\n // 3. Root document — transform an existing `__root`, else scaffold one.\n const existingRoot = await findAppFile(rootDir, routesDir, '__root');\n if (existingRoot) {\n await transformExistingFile(rootDir, existingRoot, (code) =>\n wrapRootWithProvider(code, activeLocaleSegment)\n );\n } else {\n await createIfMissing(\n rootDir,\n join(routesDir, `__root.${scriptExtension}`),\n useTypeScript\n ? buildRootTemplateTs(activeLocaleSegment)\n : buildRootTemplateJs(activeLocaleSegment),\n 'root document'\n );\n }\n },\n};\n"],"mappings":";;;;;;;;;;;;AAqBA,MAAM,uBACJ,UACA,WACS;CACT,QAAQ,OAAO,QAAf;EACE,KAAK;GACH,OAAO,GAAG,EAAE,8BAA8B,aAAa,QAAQ,GAAG;GAClE;EACF,KAAK;GACH,OACE,GAAG,EAAE,GAAG,aAAa,QAAQ,EAAE,qCACjC;GACA;EACF,KAAK,WACH,OACE,GAAG,EAAE,iDAAiD,aAAa,QAAQ,EAAE,6FAC7E,EAAE,OAAO,OAAO,CAClB;CAEJ;AACF;;AAGA,MAAM,wBAAwB,OAC5B,SACA,UACA,cACkB;CAElB,MAAM,SAAS,UAAU,MADN,iBAAiB,SAAS,QAAQ,CACxB;CAC7B,IAAI,OAAO,WAAW,WACpB,MAAM,gBAAgB,SAAS,UAAU,OAAO,IAAI;CAEtD,oBAAoB,UAAU,MAAM;AACtC;;AAGA,MAAM,kBAAkB,OACtB,SACA,UACA,SACA,gBACqB;CACrB,IAAI,MAAM,OAAO,SAAS,QAAQ,GAAG,OAAO;CAC5C,MAAM,gBAAgB,SAAS,UAAU,OAAO;CAChD,OAAO,GAAG,EAAE,WAAW,YAAY,GAAG,aAAa,QAAQ,GAAG;CAC9D,OAAO;AACT;;;;;;;AAQA,MAAa,uBAAyC;CACpD,MAAM;CAEN,QAAQ,OAAO,EAAE,SAAS,cAAc;EACtC,IAAI,CAAC,qBAAqB,OAAO,GAAG,OAAO;EAC3C,OAAQ,MAAM,wBAAwB,OAAO,MAAO;CACtD;CAEA,OAAO,OAAO,EACZ,SACA,eACA,kBAC2B;EAC3B,MAAM,aAAa,MAAM,wBAAwB,OAAO;EACxD,IAAI,CAAC,YAAY;EAEjB,MAAM,EAAE,cAAc;EACtB,MAAM,kBAAkB,gBAAgB,QAAQ;EAIhD,MAAM,sBAAsB,iBAAiB,WAAW;EAExD,OACE,SAAS,4CAA4C,WAAW,IAAI,CACtE;EAGA,MAAM,oBAAoB,MAAM,4BAC9B,SACA,WACA,mBACF;EACA,IAAI,kBAAkB,WAAW,SAC/B,OACE,GAAG,EAAE,6BAA6B,aAAa,KAAK,WAAW,mBAAmB,CAAC,EAAE,IAAI,kBAAkB,aACxG,KAAK,UAAU,SAAS,OAAO,WAAW,OAAO,CAAC,CAAC,CACnD,KAAK,IAAI,GACd;OACK,IAAI,kBAAkB,WAAW,sBACtC,OACE,GAAG,EAAE,GAAG,aAAa,KAAK,WAAW,kBAAkB,aAAa,CAAC,EAAE,sCACzE;EAMF,MAAM,sBACJ,kBAAkB,WAAW,uBACzB,kBAAkB,gBAClB;EAEN,MAAM,YAAY,KAAK,WAAW,mBAAmB;EACrD,MAAM,gBAAgB,SAAS,SAAS;EAGxC,MAAM,gBACJ,SACA,KAAK,WAAW,SAAS,iBAAiB,GAC1C,yBAAyB,mBAAmB,GAC5C,cACF;EAGA,MAAM,eAAe,MAAM,YAAY,SAAS,WAAW,QAAQ;EACnE,IAAI,cACF,MAAM,sBAAsB,SAAS,eAAe,SAClD,qBAAqB,MAAM,mBAAmB,CAChD;OAEA,MAAM,gBACJ,SACA,KAAK,WAAW,UAAU,iBAAiB,GAC3C,gBACI,oBAAoB,mBAAmB,IACvC,oBAAoB,mBAAmB,GAC3C,eACF;CAEJ;AACF"}
@@ -92,7 +92,8 @@ const restructureRoutesIntoLocale = async (rootDir, routesDir, localeSegment = L
92
92
  movedTopLevelNames
93
93
  };
94
94
  for (const newAbs of movedFiles) {
95
- const oldAbs = join(routesDirAbs, relative(localeDirAbs, newAbs));
95
+ const relFromLocale = relative(localeDirAbs, newAbs);
96
+ const oldAbs = join(routesDirAbs, relFromLocale);
96
97
  const code = await readFile(newAbs, "utf8");
97
98
  const rewritten = rewriteRelativeImports(code, oldAbs, newAbs, rewriteContext);
98
99
  if (rewritten !== code) await writeFile(newAbs, rewritten, "utf8");
@@ -1 +1 @@
1
- {"version":3,"file":"restructure.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/restructure.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport fg from 'fast-glob';\nimport type { RoutingMode } from '../../utils/configManipulation';\nimport { rewriteRelativeImports } from '../nextAppRouter/restructure';\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/**\n * The optional TanStack Router locale segment directory (`{-$locale}` param),\n * used by every routing mode except `prefix-all` so the default locale stays\n * prefix-free.\n */\nexport const LOCALE_SEGMENT = '{-$locale}';\n\n/**\n * The required TanStack Router locale segment directory (`$locale` param), used\n * by `prefix-all` routing where every URL — including the default locale — is\n * prefixed.\n */\nexport const PREFIX_ALL_LOCALE_SEGMENT = '$locale';\n\n/**\n * Picks the locale segment directory for the project's routing mode. `prefix-all`\n * requires the mandatory `$locale` param so the default locale is also prefixed;\n * all other modes use the optional `{-$locale}` param so the default locale stays\n * prefix-free.\n */\nexport const getLocaleSegment = (routingMode: RoutingMode): string =>\n routingMode === 'prefix-all' ? PREFIX_ALL_LOCALE_SEGMENT : LOCALE_SEGMENT;\n\n/**\n * Detects whether a top-level route entry is already a locale segment, in any of\n * the TanStack Router param forms:\n * - `{-$locale}` — optional param (prefix every locale except the default),\n * - `$locale` / `{$locale}` — required param (prefix **all** locales).\n *\n * Used to skip the restructure when the project is already locale-aware, so an\n * existing prefix-all `$locale` directory is never nested under a freshly created\n * `{-$locale}` (which would produce `{-$locale}/$locale`).\n */\nexport const isLocaleSegment = (entryName: string): boolean =>\n /^(\\{-?\\$locale\\}|\\$locale)$/.test(entryName);\n\n/** Strips a known script extension from a file name, e.g. `index.tsx` -> `index`. */\nconst stripScriptExtension = (fileName: string): string =>\n fileName.replace(/\\.(tsx|ts|jsx|js|mjs|cjs)$/, '');\n\n/**\n * Routes-directory entries that must stay at the root and never be moved under\n * `{-$locale}`:\n * - `__root` (the root route document),\n * - `routeTree.gen` (generated route tree),\n * - `api` (server / API routes — these are not locale-prefixed),\n * - TanStack-ignored entries prefixed with `-` (e.g. `-components`, `-utils`),\n * - stylesheets.\n *\n * Everything else (route files and nested route folders) is moved so it becomes\n * locale-aware.\n */\nexport const shouldKeepRouteAtRoot = (entryName: string): boolean => {\n if (entryName.startsWith('-')) return true;\n if (entryName.toLowerCase().endsWith('.css')) return true;\n\n const base = stripScriptExtension(entryName);\n const keepExactBases = new Set(['__root', 'routeTree.gen', 'api']);\n return keepExactBases.has(base);\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 entries of `routesDir` under a new `localeSegment` directory\n * (the optional `{-$locale}` by default, or the required `$locale` for\n * `prefix-all`) and rewrites relative imports in the moved files. Idempotent: a\n * no-op when the routes are already locale-aware in any prefix mode (see\n * {@link isLocaleSegment}), which is reported via `localeSegment`. Root-only\n * files (see {@link shouldKeepRouteAtRoot}) are left in place.\n */\nexport const restructureRoutesIntoLocale = async (\n rootDir: string,\n routesDir: string,\n localeSegment: string = LOCALE_SEGMENT\n): Promise<RestructureResult> => {\n const routesDirAbs = join(rootDir, routesDir);\n const localeDirAbs = join(routesDirAbs, localeSegment);\n\n const entries = await readdir(routesDirAbs, { withFileTypes: true });\n\n // Skip when the routes are already locale-aware in any prefix mode — a fresh\n // `{-$locale}`, or an existing `$locale` / `{$locale}` (prefix-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) => !shouldKeepRouteAtRoot(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(routesDirAbs, 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 = {\n appDirAbs: routesDirAbs,\n localeDirAbs,\n movedTopLevelNames,\n };\n\n for (const newAbs of movedFiles) {\n const relFromLocale = relative(localeDirAbs, newAbs);\n const oldAbs = join(routesDirAbs, 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":";;;;;;;AAOA,MAAM,cAAc;;;;;;AAOpB,MAAa,iBAAiB;;;;;;AAO9B,MAAa,4BAA4B;;;;;;;AAQzC,MAAa,oBAAoB,gBAC/B,gBAAgB,eAAe,4BAA4B;;;;;;;;;;;AAY7D,MAAa,mBAAmB,cAC9B,8BAA8B,KAAK,SAAS;;AAG9C,MAAM,wBAAwB,aAC5B,SAAS,QAAQ,8BAA8B,EAAE;;;;;;;;;;;;;AAcnD,MAAa,yBAAyB,cAA+B;CACnE,IAAI,UAAU,WAAW,GAAG,GAAG,OAAO;CACtC,IAAI,UAAU,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,OAAO;CAErD,MAAM,OAAO,qBAAqB,SAAS;CAE3C,wBAAO,IADoB,IAAI;EAAC;EAAU;EAAiB;CAAK,CAC5C,EAAC,CAAC,IAAI,IAAI;AAChC;;;;;;;;;AAgBA,MAAa,8BAA8B,OACzC,SACA,WACA,gBAAwB,mBACO;CAC/B,MAAM,eAAe,KAAK,SAAS,SAAS;CAC5C,MAAM,eAAe,KAAK,cAAc,aAAa;CAErD,MAAM,UAAU,MAAM,QAAQ,cAAc,EAAE,eAAe,KAAK,CAAC;CAInE,MAAM,wBAAwB,QAAQ,MAAM,UAC1C,gBAAgB,MAAM,IAAI,CAC5B;CACA,IAAI,uBACF,OAAO;EACL,QAAQ;EACR,eAAe,sBAAsB;CACvC;CAGF,MAAM,qBAAqB,QACxB,KAAK,UAAU,MAAM,IAAI,CAAC,CAC1B,QAAQ,SAAS,CAAC,sBAAsB,IAAI,CAAC;CAEhD,IAAI,mBAAmB,WAAW,GAChC,OAAO,EAAE,QAAQ,kBAAkB;CAGrC,MAAM,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;CAE7C,KAAK,MAAM,QAAQ,oBACjB,MAAM,OAAO,KAAK,cAAc,IAAI,GAAG,KAAK,cAAc,IAAI,CAAC;CAGjE,MAAM,aAAa,MAAM,GAAG,aAAa;EACvC,KAAK;EACL,UAAU;EACV,WAAW;CACb,CAAC;CAED,MAAM,iBAAiB;EACrB,WAAW;EACX;EACA;CACF;CAEA,KAAK,MAAM,UAAU,YAAY;EAE/B,MAAM,SAAS,KAAK,cADE,SAAS,cAAc,MACC,CAAC;EAC/C,MAAM,OAAO,MAAM,SAAS,QAAQ,MAAM;EAC1C,MAAM,YAAY,uBAChB,MACA,QACA,QACA,cACF;EACA,IAAI,cAAc,MAChB,MAAM,UAAU,QAAQ,WAAW,MAAM;CAE7C;CAEA,OAAO;EAAE,QAAQ;EAAS,cAAc;CAAmB;AAC7D"}
1
+ {"version":3,"file":"restructure.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/restructure.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\nimport fg from 'fast-glob';\nimport type { RoutingMode } from '../../utils/configManipulation';\nimport { rewriteRelativeImports } from '../nextAppRouter/restructure';\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/**\n * The optional TanStack Router locale segment directory (`{-$locale}` param),\n * used by every routing mode except `prefix-all` so the default locale stays\n * prefix-free.\n */\nexport const LOCALE_SEGMENT = '{-$locale}';\n\n/**\n * The required TanStack Router locale segment directory (`$locale` param), used\n * by `prefix-all` routing where every URL — including the default locale — is\n * prefixed.\n */\nexport const PREFIX_ALL_LOCALE_SEGMENT = '$locale';\n\n/**\n * Picks the locale segment directory for the project's routing mode. `prefix-all`\n * requires the mandatory `$locale` param so the default locale is also prefixed;\n * all other modes use the optional `{-$locale}` param so the default locale stays\n * prefix-free.\n */\nexport const getLocaleSegment = (routingMode: RoutingMode): string =>\n routingMode === 'prefix-all' ? PREFIX_ALL_LOCALE_SEGMENT : LOCALE_SEGMENT;\n\n/**\n * Detects whether a top-level route entry is already a locale segment, in any of\n * the TanStack Router param forms:\n * - `{-$locale}` — optional param (prefix every locale except the default),\n * - `$locale` / `{$locale}` — required param (prefix **all** locales).\n *\n * Used to skip the restructure when the project is already locale-aware, so an\n * existing prefix-all `$locale` directory is never nested under a freshly created\n * `{-$locale}` (which would produce `{-$locale}/$locale`).\n */\nexport const isLocaleSegment = (entryName: string): boolean =>\n /^(\\{-?\\$locale\\}|\\$locale)$/.test(entryName);\n\n/** Strips a known script extension from a file name, e.g. `index.tsx` -> `index`. */\nconst stripScriptExtension = (fileName: string): string =>\n fileName.replace(/\\.(tsx|ts|jsx|js|mjs|cjs)$/, '');\n\n/**\n * Routes-directory entries that must stay at the root and never be moved under\n * `{-$locale}`:\n * - `__root` (the root route document),\n * - `routeTree.gen` (generated route tree),\n * - `api` (server / API routes — these are not locale-prefixed),\n * - TanStack-ignored entries prefixed with `-` (e.g. `-components`, `-utils`),\n * - stylesheets.\n *\n * Everything else (route files and nested route folders) is moved so it becomes\n * locale-aware.\n */\nexport const shouldKeepRouteAtRoot = (entryName: string): boolean => {\n if (entryName.startsWith('-')) return true;\n if (entryName.toLowerCase().endsWith('.css')) return true;\n\n const base = stripScriptExtension(entryName);\n const keepExactBases = new Set(['__root', 'routeTree.gen', 'api']);\n return keepExactBases.has(base);\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 entries of `routesDir` under a new `localeSegment` directory\n * (the optional `{-$locale}` by default, or the required `$locale` for\n * `prefix-all`) and rewrites relative imports in the moved files. Idempotent: a\n * no-op when the routes are already locale-aware in any prefix mode (see\n * {@link isLocaleSegment}), which is reported via `localeSegment`. Root-only\n * files (see {@link shouldKeepRouteAtRoot}) are left in place.\n */\nexport const restructureRoutesIntoLocale = async (\n rootDir: string,\n routesDir: string,\n localeSegment: string = LOCALE_SEGMENT\n): Promise<RestructureResult> => {\n const routesDirAbs = join(rootDir, routesDir);\n const localeDirAbs = join(routesDirAbs, localeSegment);\n\n const entries = await readdir(routesDirAbs, { withFileTypes: true });\n\n // Skip when the routes are already locale-aware in any prefix mode — a fresh\n // `{-$locale}`, or an existing `$locale` / `{$locale}` (prefix-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) => !shouldKeepRouteAtRoot(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(routesDirAbs, 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 = {\n appDirAbs: routesDirAbs,\n localeDirAbs,\n movedTopLevelNames,\n };\n\n for (const newAbs of movedFiles) {\n const relFromLocale = relative(localeDirAbs, newAbs);\n const oldAbs = join(routesDirAbs, 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":";;;;;;;AAOA,MAAM,cAAc;;;;;;AAOpB,MAAa,iBAAiB;;;;;;AAO9B,MAAa,4BAA4B;;;;;;;AAQzC,MAAa,oBAAoB,gBAC/B,gBAAgB,eAAe,4BAA4B;;;;;;;;;;;AAY7D,MAAa,mBAAmB,cAC9B,8BAA8B,KAAK,SAAS;;AAG9C,MAAM,wBAAwB,aAC5B,SAAS,QAAQ,8BAA8B,EAAE;;;;;;;;;;;;;AAcnD,MAAa,yBAAyB,cAA+B;CACnE,IAAI,UAAU,WAAW,GAAG,GAAG,OAAO;CACtC,IAAI,UAAU,YAAY,CAAC,CAAC,SAAS,MAAM,GAAG,OAAO;CAErD,MAAM,OAAO,qBAAqB,SAAS;CAE3C,wBAAO,IADoB,IAAI;EAAC;EAAU;EAAiB;CAAK,CAC5C,EAAC,CAAC,IAAI,IAAI;AAChC;;;;;;;;;AAgBA,MAAa,8BAA8B,OACzC,SACA,WACA,gBAAwB,mBACO;CAC/B,MAAM,eAAe,KAAK,SAAS,SAAS;CAC5C,MAAM,eAAe,KAAK,cAAc,aAAa;CAErD,MAAM,UAAU,MAAM,QAAQ,cAAc,EAAE,eAAe,KAAK,CAAC;CAInE,MAAM,wBAAwB,QAAQ,MAAM,UAC1C,gBAAgB,MAAM,IAAI,CAC5B;CACA,IAAI,uBACF,OAAO;EACL,QAAQ;EACR,eAAe,sBAAsB;CACvC;CAGF,MAAM,qBAAqB,QACxB,KAAK,UAAU,MAAM,IAAI,CAAC,CAC1B,QAAQ,SAAS,CAAC,sBAAsB,IAAI,CAAC;CAEhD,IAAI,mBAAmB,WAAW,GAChC,OAAO,EAAE,QAAQ,kBAAkB;CAGrC,MAAM,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;CAE7C,KAAK,MAAM,QAAQ,oBACjB,MAAM,OAAO,KAAK,cAAc,IAAI,GAAG,KAAK,cAAc,IAAI,CAAC;CAGjE,MAAM,aAAa,MAAM,GAAG,aAAa;EACvC,KAAK;EACL,UAAU;EACV,WAAW;CACb,CAAC;CAED,MAAM,iBAAiB;EACrB,WAAW;EACX;EACA;CACF;CAEA,KAAK,MAAM,UAAU,YAAY;EAC/B,MAAM,gBAAgB,SAAS,cAAc,MAAM;EACnD,MAAM,SAAS,KAAK,cAAc,aAAa;EAC/C,MAAM,OAAO,MAAM,SAAS,QAAQ,MAAM;EAC1C,MAAM,YAAY,uBAChB,MACA,QACA,QACA,cACF;EACA,IAAI,cAAc,MAChB,MAAM,UAAU,QAAQ,WAAW,MAAM;CAE7C;CAEA,OAAO;EAAE,QAAQ;EAAS,cAAc;CAAmB;AAC7D"}
@@ -33,25 +33,30 @@ export const Route = createFileRoute("/${localeSegment}")({
33
33
  `;
34
34
  /**
35
35
  * Root route document (`routes/__root.tsx`) providing `<html>` + the Intlayer
36
- * provider, with the locale read from the `{-$locale}` route param.
36
+ * provider, with the locale read from the locale segment route params.
37
+ *
38
+ * The route is reached through `getRouteApi(<route id>)` rather than by
39
+ * importing `<segment>/route`, so the root never imports a child route module —
40
+ * the recommended way to consume another route's typed hooks.
37
41
  */
38
- const ROOT_TEMPLATE_TS = `import {
42
+ const buildRootTemplateTs = (localeSegment) => `import {
39
43
  createRootRoute,
44
+ getRouteApi,
40
45
  HeadContent,
41
46
  Scripts,
42
- useParams,
43
47
  } from "@tanstack/react-router";
44
48
  import { defaultLocale, getHTMLTextDir } from "intlayer";
45
49
  import type { ReactNode } from "react";
46
50
  import { IntlayerProvider } from "react-intlayer";
47
51
 
52
+ const localeRoute = getRouteApi("/${localeSegment}");
53
+
48
54
  export const Route = createRootRoute({
49
55
  shellComponent: RootDocument,
50
56
  });
51
57
 
52
58
  function RootDocument({ children }: { children: ReactNode }) {
53
- const params = useParams({ strict: false });
54
- const locale = params.locale ?? defaultLocale;
59
+ const { locale = defaultLocale } = localeRoute.useParams();
55
60
 
56
61
  return (
57
62
  <html dir={getHTMLTextDir(locale)} lang={locale}>
@@ -66,22 +71,24 @@ function RootDocument({ children }: { children: ReactNode }) {
66
71
  );
67
72
  }
68
73
  `;
69
- const ROOT_TEMPLATE_JS = `import {
74
+ /** JavaScript counterpart of {@link buildRootTemplateTs}. */
75
+ const buildRootTemplateJs = (localeSegment) => `import {
70
76
  createRootRoute,
77
+ getRouteApi,
71
78
  HeadContent,
72
79
  Scripts,
73
- useParams,
74
80
  } from "@tanstack/react-router";
75
81
  import { defaultLocale, getHTMLTextDir } from "intlayer";
76
82
  import { IntlayerProvider } from "react-intlayer";
77
83
 
84
+ const localeRoute = getRouteApi("/${localeSegment}");
85
+
78
86
  export const Route = createRootRoute({
79
87
  shellComponent: RootDocument,
80
88
  });
81
89
 
82
90
  function RootDocument({ children }) {
83
- const params = useParams({ strict: false });
84
- const locale = params.locale ?? defaultLocale;
91
+ const { locale = defaultLocale } = localeRoute.useParams();
85
92
 
86
93
  return (
87
94
  <html dir={getHTMLTextDir(locale)} lang={locale}>
@@ -98,5 +105,5 @@ function RootDocument({ children }) {
98
105
  `;
99
106
 
100
107
  //#endregion
101
- export { ROOT_TEMPLATE_JS, ROOT_TEMPLATE_TS, buildLocaleRouteTemplate };
108
+ export { buildLocaleRouteTemplate, buildRootTemplateJs, buildRootTemplateTs };
102
109
  //# sourceMappingURL=templates.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"templates.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/templates.ts"],"sourcesContent":["/**\n * File templates for a fresh TanStack Start integration. These are only written\n * when the target file does not exist — they never overwrite user code.\n */\n\n/**\n * Builds the locale segment route (`routes/<localeSegment>/route.tsx`) for the\n * given segment. The optional `{-$locale}` param makes the default locale\n * prefix-free, while the required `$locale` param (`prefix-all`) prefixes every\n * locale; in both cases `validatePrefix` redirects unknown prefixes to a valid\n * one. Identical for TypeScript and JavaScript projects.\n */\nexport const buildLocaleRouteTemplate = (\n localeSegment: string\n): string => `import { createFileRoute, Outlet, redirect } from \"@tanstack/react-router\";\nimport { validatePrefix } from \"intlayer\";\n\nexport const Route = createFileRoute(\"/${localeSegment}\")({\n beforeLoad: ({ params }) => {\n // beforeLoad runs on both client and server, so resolve the locale from the\n // route params rather than from request headers.\n const { isValid, localePrefix } = validatePrefix(params.locale);\n\n // A valid (or absent, i.e. default) locale prefix is fine as-is.\n if (isValid) return;\n\n // Otherwise redirect to the same route with a valid locale prefix.\n throw redirect({\n params: { locale: localePrefix },\n to: \"/${localeSegment}\",\n });\n },\n component: Outlet,\n});\n`;\n\n/**\n * Root route document (`routes/__root.tsx`) providing `<html>` + the Intlayer\n * provider, with the locale read from the `{-$locale}` route param.\n */\nexport const ROOT_TEMPLATE_TS = `import {\n createRootRoute,\n HeadContent,\n Scripts,\n useParams,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport type { ReactNode } from \"react\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }: { children: ReactNode }) {\n const params = useParams({ strict: false });\n const locale = params.locale ?? defaultLocale;\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\nexport const ROOT_TEMPLATE_JS = `import {\n createRootRoute,\n HeadContent,\n Scripts,\n useParams,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }) {\n const params = useParams({ strict: false });\n const locale = params.locale ?? defaultLocale;\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,4BACX,kBACW;;;yCAG4B,cAAc;;;;;;;;;;;;cAYzC,cAAc;;;;;;;;;;AAW5B,MAAa,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgChC,MAAa,mBAAmB"}
1
+ {"version":3,"file":"templates.mjs","names":[],"sources":["../../../../../src/init/frameworkSetup/tanstackStart/templates.ts"],"sourcesContent":["/**\n * File templates for a fresh TanStack Start integration. These are only written\n * when the target file does not exist — they never overwrite user code.\n */\n\n/**\n * Builds the locale segment route (`routes/<localeSegment>/route.tsx`) for the\n * given segment. The optional `{-$locale}` param makes the default locale\n * prefix-free, while the required `$locale` param (`prefix-all`) prefixes every\n * locale; in both cases `validatePrefix` redirects unknown prefixes to a valid\n * one. Identical for TypeScript and JavaScript projects.\n */\nexport const buildLocaleRouteTemplate = (\n localeSegment: string\n): string => `import { createFileRoute, Outlet, redirect } from \"@tanstack/react-router\";\nimport { validatePrefix } from \"intlayer\";\n\nexport const Route = createFileRoute(\"/${localeSegment}\")({\n beforeLoad: ({ params }) => {\n // beforeLoad runs on both client and server, so resolve the locale from the\n // route params rather than from request headers.\n const { isValid, localePrefix } = validatePrefix(params.locale);\n\n // A valid (or absent, i.e. default) locale prefix is fine as-is.\n if (isValid) return;\n\n // Otherwise redirect to the same route with a valid locale prefix.\n throw redirect({\n params: { locale: localePrefix },\n to: \"/${localeSegment}\",\n });\n },\n component: Outlet,\n});\n`;\n\n/**\n * Root route document (`routes/__root.tsx`) providing `<html>` + the Intlayer\n * provider, with the locale read from the locale segment route params.\n *\n * The route is reached through `getRouteApi(<route id>)` rather than by\n * importing `<segment>/route`, so the root never imports a child route module —\n * the recommended way to consume another route's typed hooks.\n */\nexport const buildRootTemplateTs = (localeSegment: string): string => `import {\n createRootRoute,\n getRouteApi,\n HeadContent,\n Scripts,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport type { ReactNode } from \"react\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nconst localeRoute = getRouteApi(\"/${localeSegment}\");\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }: { children: ReactNode }) {\n const { locale = defaultLocale } = localeRoute.useParams();\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n\n/** JavaScript counterpart of {@link buildRootTemplateTs}. */\nexport const buildRootTemplateJs = (localeSegment: string): string => `import {\n createRootRoute,\n getRouteApi,\n HeadContent,\n Scripts,\n} from \"@tanstack/react-router\";\nimport { defaultLocale, getHTMLTextDir } from \"intlayer\";\nimport { IntlayerProvider } from \"react-intlayer\";\n\nconst localeRoute = getRouteApi(\"/${localeSegment}\");\n\nexport const Route = createRootRoute({\n shellComponent: RootDocument,\n});\n\nfunction RootDocument({ children }) {\n const { locale = defaultLocale } = localeRoute.useParams();\n\n return (\n <html dir={getHTMLTextDir(locale)} lang={locale}>\n <head>\n <HeadContent />\n </head>\n <body>\n <IntlayerProvider locale={locale}>{children}</IntlayerProvider>\n <Scripts />\n </body>\n </html>\n );\n}\n`;\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,4BACX,kBACW;;;yCAG4B,cAAc;;;;;;;;;;;;cAYzC,cAAc;;;;;;;;;;;;;;AAe5B,MAAa,uBAAuB,kBAAkC;;;;;;;;;;oCAUlC,cAAc;;;;;;;;;;;;;;;;;;;;;;;AAwBlD,MAAa,uBAAuB,kBAAkC;;;;;;;;;oCASlC,cAAc"}
@@ -1,9 +1,11 @@
1
1
  import { babelTsParser } from "../../../utils/babelParser.mjs";
2
- import { ensureNamedImport } from "../../utils/astImports.mjs";
2
+ import { ensureNamedImport, firstInsertIndex, isModuleScopeBinding } from "../../utils/astImports.mjs";
3
3
  import * as recast from "recast";
4
4
 
5
5
  //#region src/init/frameworkSetup/tanstackStart/transforms.ts
6
6
  const { builders: b, namedTypes: n } = recast.types;
7
+ /** Module-scope identifier bound to the locale route's typed API. */
8
+ const LOCALE_ROUTE_API_VARIABLE = "localeRoute";
7
9
  /** babel-ts parser handles TypeScript *and* JSX. */
8
10
  const parseTsx = (code) => recast.parse(code, { parser: babelTsParser });
9
11
  /**
@@ -48,27 +50,59 @@ const patternBindsName = (node, name) => {
48
50
  }
49
51
  };
50
52
  /**
51
- * Inserts `const locale = …` once, at the top of the function body. A single
52
- * self-contained statement is used (rather than an intermediate `params` const)
53
- * so it can never collide with a `params` variable the document already
54
- * declares. Expression-bodied arrow functions are first converted to a block so
55
- * the declaration has somewhere to live.
53
+ * Inserts `const { locale = defaultLocale } = localeRoute.useParams();` once, at
54
+ * the top of the function body. The destructuring form binds only `locale`, so
55
+ * it can never collide with a `params` variable the document already declares.
56
+ * Expression-bodied arrow functions are first converted to a block so the
57
+ * declaration has somewhere to live.
56
58
  *
57
59
  * No-ops when `locale` is already bound in the function — whether as a simple
58
60
  * identifier, through a destructuring pattern (e.g.
59
- * `const { locale = defaultLocale } = useLoaderData()`), or as a function
61
+ * `const { locale = defaultLocale } = Route.useLoaderData()`), or as a function
60
62
  * parameter — so the injected declaration never collides with one the document
61
63
  * already provides (which would produce an
62
64
  * `Identifier 'locale' has already been declared` parse error).
65
+ *
66
+ * @returns `true` when the declaration was injected, `false` when the document
67
+ * already binds `locale` itself. Callers use this to skip the imports and the
68
+ * route API declaration the injected statement would have needed.
63
69
  */
64
70
  const ensureLocaleFromParams = (funcNode) => {
65
71
  if (funcNode.body?.type !== "BlockStatement") funcNode.body = b.blockStatement([b.returnStatement(funcNode.body)]);
66
72
  const declaredInBody = funcNode.body.body.some((stmt) => stmt.type === "VariableDeclaration" && stmt.declarations.some((d) => patternBindsName(d.id, "locale")));
67
73
  const declaredInParams = (funcNode.params ?? []).some((param) => patternBindsName(param, "locale"));
68
- if (declaredInBody || declaredInParams) return;
69
- const statement = parseTsx("const locale = useParams({ strict: false }).locale ?? defaultLocale;").program.body[0];
74
+ if (declaredInBody || declaredInParams) return false;
75
+ const statement = parseTsx(`const { locale = defaultLocale } = ${LOCALE_ROUTE_API_VARIABLE}.useParams();`).program.body[0];
70
76
  funcNode.body.body.unshift(statement);
77
+ return true;
71
78
  };
79
+ /**
80
+ * Ensures the module-scope `const localeRoute = getRouteApi("/<segment>");`
81
+ * declaration the injected locale statement reads from, inserted right after the
82
+ * import block.
83
+ *
84
+ * Referencing the locale route by id keeps the root document free of an import
85
+ * of `<segment>/route` — importing a child route module from `__root` is
86
+ * discouraged, since the generated route tree already imports the root.
87
+ *
88
+ * No-ops when `localeRoute` is already bound at module scope, reusing that
89
+ * binding rather than redeclaring it (which would be a parse error).
90
+ *
91
+ * @returns `true` when the declaration was inserted, `false` when an existing
92
+ * binding was reused.
93
+ */
94
+ const ensureLocaleRouteApi = (ast, localeSegment) => {
95
+ if (isModuleScopeBinding(ast, LOCALE_ROUTE_API_VARIABLE)) return false;
96
+ const declaration = parseTsx(`const ${LOCALE_ROUTE_API_VARIABLE} = getRouteApi("/${localeSegment}");`).program.body[0];
97
+ ast.program.body.splice(firstInsertIndex(ast), 0, declaration);
98
+ return true;
99
+ };
100
+ /**
101
+ * Puts a blank line between the import block and the inserted route API
102
+ * declaration — recast prints a spliced statement flush against the preceding
103
+ * line, which reads as part of the imports.
104
+ */
105
+ const separateRouteApiDeclaration = (printedCode) => printedCode.replace(new RegExp(`([^\\n])\\n(const ${LOCALE_ROUTE_API_VARIABLE} = getRouteApi\\()`), "$1\n\n$2");
72
106
  /** Sets `lang={locale}` and `dir={getHTMLTextDir(locale)}` on the first `<html>` element. */
73
107
  const setHtmlLangAndDir = (ast) => {
74
108
  recast.visit(ast, { visitJSXOpeningElement(path) {
@@ -89,12 +123,16 @@ const setHtmlLangAndDir = (ast) => {
89
123
  };
90
124
  /**
91
125
  * Wraps the `{children}` of a TanStack Start root document with
92
- * `IntlayerProvider`, deriving the locale from the `{-$locale}` route param.
126
+ * `IntlayerProvider`, deriving the locale from the locale segment route params.
93
127
  * Safe and idempotent: bails (returns the original code) when the provider is
94
128
  * already present, when no `<html>` document function is found, or when there is
95
129
  * no `{children}` placeholder to wrap.
130
+ *
131
+ * @param code Source of the project's `__root` document.
132
+ * @param localeSegment Locale segment directory in use — `{-$locale}` for every
133
+ * routing mode but `prefix-all`, which uses the required `$locale`.
96
134
  */
97
- const wrapRootWithProvider = (code) => {
135
+ const wrapRootWithProvider = (code, localeSegment) => {
98
136
  const ast = parseTsx(code);
99
137
  if (code.includes("IntlayerProvider")) return {
100
138
  code,
@@ -122,14 +160,19 @@ const wrapRootWithProvider = (code) => {
122
160
  code,
123
161
  status: "skipped"
124
162
  };
163
+ const injectedLocaleDeclaration = ensureLocaleFromParams(funcNode);
125
164
  ensureNamedImport(ast, "IntlayerProvider", "react-intlayer");
126
- ensureNamedImport(ast, "useParams", "@tanstack/react-router");
127
- ensureNamedImport(ast, "defaultLocale", "intlayer");
165
+ let insertedRouteApi = false;
166
+ if (injectedLocaleDeclaration) {
167
+ ensureNamedImport(ast, "getRouteApi", "@tanstack/react-router");
168
+ ensureNamedImport(ast, "defaultLocale", "intlayer");
169
+ insertedRouteApi = ensureLocaleRouteApi(ast, localeSegment);
170
+ }
128
171
  ensureNamedImport(ast, "getHTMLTextDir", "intlayer");
129
- ensureLocaleFromParams(funcNode);
130
172
  setHtmlLangAndDir(ast);
173
+ const printedCode = recast.print(ast).code;
131
174
  return {
132
- code: recast.print(ast).code,
175
+ code: insertedRouteApi ? separateRouteApiDeclaration(printedCode) : printedCode,
133
176
  status: "wrapped"
134
177
  };
135
178
  };