rimecms 0.26.6 → 0.26.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/dist/adapter-sqlite/where.js +7 -1
  2. package/dist/core/areas/api/get.server.js +6 -2
  3. package/dist/core/collections/nested/hooks/index.server.js +3 -1
  4. package/dist/core/collections/upload/upload.d.ts +8 -8
  5. package/dist/core/collections/upload/util/path.js +2 -1
  6. package/dist/core/config/types.d.ts +1 -1
  7. package/dist/core/dev/generate/sanitize/index.js +707 -707
  8. package/dist/core/errors/handler.server.js +3 -3
  9. package/dist/core/fields/util.js +2 -1
  10. package/dist/core/i18n/en/common.js +60 -60
  11. package/dist/core/i18n/en/errors.js +13 -12
  12. package/dist/core/i18n/en/fields.js +11 -11
  13. package/dist/core/i18n/en/mail.js +2 -2
  14. package/dist/core/i18n/fr/common.js +64 -64
  15. package/dist/core/i18n/fr/errors.js +11 -11
  16. package/dist/core/i18n/fr/fields.js +11 -11
  17. package/dist/core/i18n/fr/mail.js +2 -2
  18. package/dist/core/logger/index.server.js +136 -136
  19. package/dist/core/operations/hooks/before-upsert/validate-fields.server.js +5 -5
  20. package/dist/core/plugins/cache/HeaderButton.svelte +11 -11
  21. package/dist/fields/blocks/component/AddBlockButton.svelte +161 -161
  22. package/dist/fields/blocks/component/Block.svelte +157 -157
  23. package/dist/fields/blocks/component/BlockActions.svelte +36 -36
  24. package/dist/fields/blocks/component/Blocks.svelte +154 -154
  25. package/dist/fields/blocks/component/Cell.svelte +12 -12
  26. package/dist/fields/blocks/component/ToggleBlockButton.svelte +11 -11
  27. package/dist/fields/checkbox/component/Checkbox.svelte +51 -51
  28. package/dist/fields/combobox/component/ComboBox.svelte +88 -88
  29. package/dist/fields/combobox/component/combobox.css +24 -24
  30. package/dist/fields/date/component/Cell.svelte +5 -5
  31. package/dist/fields/date/component/Date.svelte +92 -92
  32. package/dist/fields/email/component/Email.svelte +58 -58
  33. package/dist/fields/group/component/Group.svelte +131 -131
  34. package/dist/fields/link/component/Cell.svelte +9 -9
  35. package/dist/fields/link/component/Link.svelte +269 -269
  36. package/dist/fields/link/component/RessourceInput.svelte +183 -183
  37. package/dist/fields/number/component/Number.svelte +118 -118
  38. package/dist/fields/number/component/number.css +52 -52
  39. package/dist/fields/radio/component/Radio.svelte +56 -53
  40. package/dist/fields/relation/component/Cell.svelte +51 -51
  41. package/dist/fields/relation/component/Relation.svelte +226 -227
  42. package/dist/fields/relation/component/default/Default.svelte +218 -218
  43. package/dist/fields/relation/component/upload/Browse.svelte +227 -227
  44. package/dist/fields/relation/component/upload/Upload.svelte +112 -112
  45. package/dist/fields/relation/component/upload/upload.css +72 -72
  46. package/dist/fields/rich-text/component/Cell.svelte +5 -5
  47. package/dist/fields/rich-text/component/RichText.svelte +95 -95
  48. package/dist/fields/rich-text/component/bubble-menu/bubble-menu.css +15 -15
  49. package/dist/fields/rich-text/component/bubble-menu/bubble-menu.svelte +148 -148
  50. package/dist/fields/rich-text/component/bubble-menu/icon-button/icon-button.css +10 -10
  51. package/dist/fields/rich-text/component/bubble-menu/icon-button/icon-button.svelte +23 -17
  52. package/dist/fields/rich-text/component/bubble-menu/node-selector/node-selector.css +37 -37
  53. package/dist/fields/rich-text/component/bubble-menu/node-selector/node-selector.svelte +93 -89
  54. package/dist/fields/rich-text/component/drag-handle/drag-handle.css +12 -12
  55. package/dist/fields/rich-text/component/drag-handle/drag-handle.svelte +27 -27
  56. package/dist/fields/rich-text/component/styles/rich-text.css +142 -142
  57. package/dist/fields/rich-text/component/suggestion/suggestion.svelte +79 -79
  58. package/dist/fields/rich-text/core/extensions/current-node/current-node.css +3 -3
  59. package/dist/fields/rich-text/core/extensions/drag-handle/drag-handle.js +10 -7
  60. package/dist/fields/rich-text/core/extensions/drag-handle/helpers/cloneElement.js +4 -1
  61. package/dist/fields/rich-text/core/extensions/drag-handle/helpers/dragHandler.js +4 -4
  62. package/dist/fields/rich-text/core/extensions/drag-handle/helpers/findNextElementFromCursor.js +4 -4
  63. package/dist/fields/rich-text/core/extensions/drag-handle/helpers/getInnerCoords.js +1 -1
  64. package/dist/fields/rich-text/core/features/fields/fields.css +16 -16
  65. package/dist/fields/rich-text/core/features/fields/fields.svelte +50 -50
  66. package/dist/fields/rich-text/core/features/hr/hr.css +15 -15
  67. package/dist/fields/rich-text/core/features/link/component/link-selector.css +2 -2
  68. package/dist/fields/rich-text/core/features/link/component/link-selector.svelte +266 -266
  69. package/dist/fields/rich-text/core/features/resource/resource.svelte +135 -138
  70. package/dist/fields/rich-text/core/features/upload/upload.svelte +231 -235
  71. package/dist/fields/rich-text/core/render-rich-text.svelte +162 -162
  72. package/dist/fields/rich-text/core/svelte/node-view-wrapper.svelte +25 -18
  73. package/dist/fields/rich-text/util.js +2 -1
  74. package/dist/fields/select/component/Select.svelte +194 -194
  75. package/dist/fields/separator/component/Separator.svelte +5 -5
  76. package/dist/fields/slug/component/Cell.svelte +4 -4
  77. package/dist/fields/slug/component/Slug.svelte +99 -99
  78. package/dist/fields/tabs/component/Tabs.svelte +96 -96
  79. package/dist/fields/text/component/Text.svelte +54 -54
  80. package/dist/fields/textarea/component/TextArea.svelte +51 -51
  81. package/dist/fields/time/component/Time.svelte +44 -44
  82. package/dist/fields/toggle/component/Cell.svelte +10 -10
  83. package/dist/fields/toggle/component/Toggle.svelte +36 -36
  84. package/dist/fields/tree/component/AddItemButton.svelte +35 -29
  85. package/dist/fields/tree/component/Cell.svelte +12 -12
  86. package/dist/fields/tree/component/ToggleBlockButton.svelte +11 -11
  87. package/dist/fields/tree/component/Tree.svelte +150 -150
  88. package/dist/fields/tree/component/TreeBlock.svelte +174 -174
  89. package/dist/fields/tree/component/TreeBlockActions.svelte +13 -13
  90. package/dist/fields/tree/index.js +3 -2
  91. package/dist/live.d.ts +2 -2
  92. package/dist/live.js +2 -2
  93. package/dist/panel/components/Root.svelte +70 -70
  94. package/dist/panel/components/fields/Error.svelte +35 -35
  95. package/dist/panel/components/fields/FieldsPreview.svelte +71 -71
  96. package/dist/panel/components/fields/FieldsPreviewTrigger.svelte +11 -11
  97. package/dist/panel/components/fields/Hint.svelte +12 -12
  98. package/dist/panel/components/fields/Label.svelte +31 -26
  99. package/dist/panel/components/fields/LabelFor.svelte +26 -26
  100. package/dist/panel/components/fields/RenderFields.svelte +102 -102
  101. package/dist/panel/components/sections/auth/AuthForm.svelte +102 -102
  102. package/dist/panel/components/sections/collection/Empty.svelte +18 -18
  103. package/dist/panel/components/sections/collection/StatusDot.svelte +13 -13
  104. package/dist/panel/components/sections/collection/bulk-upload/BulkUploadDialog.svelte +30 -28
  105. package/dist/panel/components/sections/collection/bulk-upload/DropZone.svelte +176 -169
  106. package/dist/panel/components/sections/collection/folder/Folder.svelte +38 -38
  107. package/dist/panel/components/sections/collection/folder/FolderEdit.svelte +57 -57
  108. package/dist/panel/components/sections/collection/folder/FolderWithActions.svelte +223 -223
  109. package/dist/panel/components/sections/collection/grid/CollectionGrid.svelte +102 -102
  110. package/dist/panel/components/sections/collection/grid/create-directory-dialog/CreateDirectoryDialog.svelte +50 -50
  111. package/dist/panel/components/sections/collection/grid/grid-item/GridItem.svelte +56 -56
  112. package/dist/panel/components/sections/collection/header/ButtonCreate.svelte +29 -29
  113. package/dist/panel/components/sections/collection/header/DisplayMode.svelte +59 -59
  114. package/dist/panel/components/sections/collection/header/Header.svelte +11 -11
  115. package/dist/panel/components/sections/collection/header/SearchInput.svelte +41 -41
  116. package/dist/panel/components/sections/collection/header/SelectUI.svelte +51 -51
  117. package/dist/panel/components/sections/collection/header/Separator.svelte +4 -4
  118. package/dist/panel/components/sections/collection/list/CollectionList.svelte +46 -46
  119. package/dist/panel/components/sections/collection/list/header/Header.svelte +56 -56
  120. package/dist/panel/components/sections/collection/list/row/Row.svelte +115 -115
  121. package/dist/panel/components/sections/collection/tree/CollectionTree.svelte +139 -139
  122. package/dist/panel/components/sections/collection/tree/CollectionTreeNode.svelte +56 -56
  123. package/dist/panel/components/sections/collection/upload-thumb-cell/UploadThumbCell.svelte +64 -64
  124. package/dist/panel/components/sections/document/AuthAPIKeyDialog.svelte +66 -66
  125. package/dist/panel/components/sections/document/AuthFooter.svelte +81 -81
  126. package/dist/panel/components/sections/document/ButtonSave.svelte +34 -27
  127. package/dist/panel/components/sections/document/ButtonStatus.svelte +79 -79
  128. package/dist/panel/components/sections/document/CurrentlyEdited.svelte +36 -36
  129. package/dist/panel/components/sections/document/Document.svelte +223 -223
  130. package/dist/panel/components/sections/document/FloatingUI.svelte +69 -69
  131. package/dist/panel/components/sections/document/Header.svelte +105 -105
  132. package/dist/panel/components/sections/document/Settings.svelte +167 -167
  133. package/dist/panel/components/sections/document/Versions.svelte +89 -89
  134. package/dist/panel/components/sections/document/upload-header/UploadHeader.svelte +205 -205
  135. package/dist/panel/components/sections/document/upload-header/drop-zone/DropZone.svelte +141 -141
  136. package/dist/panel/components/sections/live/Consumer.svelte +14 -14
  137. package/dist/panel/components/sections/live/Provider.svelte +10 -10
  138. package/dist/panel/components/sections/live/SidePanel.svelte +56 -56
  139. package/dist/panel/components/sections/live/side-panel.css +10 -8
  140. package/dist/panel/components/sections/page-layout/Page.svelte +23 -23
  141. package/dist/panel/components/sections/unauthorized/Unauthorized.svelte +22 -22
  142. package/dist/panel/components/ui/breadcrumb/BreadCrumb.svelte +40 -40
  143. package/dist/panel/components/ui/button/button.svelte +226 -226
  144. package/dist/panel/components/ui/calendar/calendar-cell.svelte +6 -2
  145. package/dist/panel/components/ui/calendar/calendar-day.svelte +6 -2
  146. package/dist/panel/components/ui/calendar/calendar-grid-body.svelte +6 -2
  147. package/dist/panel/components/ui/calendar/calendar-grid-head.svelte +6 -2
  148. package/dist/panel/components/ui/calendar/calendar-grid-row.svelte +6 -2
  149. package/dist/panel/components/ui/calendar/calendar-grid.svelte +6 -2
  150. package/dist/panel/components/ui/calendar/calendar-head-cell.svelte +6 -2
  151. package/dist/panel/components/ui/calendar/calendar-header.svelte +6 -2
  152. package/dist/panel/components/ui/calendar/calendar-heading.svelte +6 -2
  153. package/dist/panel/components/ui/calendar/calendar-months.svelte +21 -16
  154. package/dist/panel/components/ui/calendar/calendar-next-button.svelte +13 -8
  155. package/dist/panel/components/ui/calendar/calendar-prev-button.svelte +13 -8
  156. package/dist/panel/components/ui/calendar/calendar.css +88 -88
  157. package/dist/panel/components/ui/calendar/calendar.svelte +50 -50
  158. package/dist/panel/components/ui/card/card-content.svelte +17 -12
  159. package/dist/panel/components/ui/card/card-description.svelte +14 -9
  160. package/dist/panel/components/ui/card/card-footer.svelte +17 -12
  161. package/dist/panel/components/ui/card/card-header.svelte +15 -10
  162. package/dist/panel/components/ui/card/card-title.svelte +25 -19
  163. package/dist/panel/components/ui/card/card.svelte +19 -14
  164. package/dist/panel/components/ui/card-document/card-document.svelte +73 -73
  165. package/dist/panel/components/ui/card-resource/card-resource.svelte +108 -108
  166. package/dist/panel/components/ui/checkbox/checkbox.css +28 -28
  167. package/dist/panel/components/ui/checkbox/checkbox.svelte +13 -13
  168. package/dist/panel/components/ui/command/command-dialog.css +24 -24
  169. package/dist/panel/components/ui/command/command-dialog.svelte +28 -18
  170. package/dist/panel/components/ui/command/command-empty.css +2 -2
  171. package/dist/panel/components/ui/command/command-empty.svelte +7 -3
  172. package/dist/panel/components/ui/command/command-group.css +20 -20
  173. package/dist/panel/components/ui/command/command-group.svelte +17 -17
  174. package/dist/panel/components/ui/command/command-input-select.svelte +50 -45
  175. package/dist/panel/components/ui/command/command-input.svelte +57 -57
  176. package/dist/panel/components/ui/command/command-item.css +18 -18
  177. package/dist/panel/components/ui/command/command-item.svelte +7 -3
  178. package/dist/panel/components/ui/command/command-list.css +4 -4
  179. package/dist/panel/components/ui/command/command-list.svelte +7 -3
  180. package/dist/panel/components/ui/command/command-separator.css +4 -4
  181. package/dist/panel/components/ui/command/command-separator.svelte +7 -3
  182. package/dist/panel/components/ui/command/command.css +5 -5
  183. package/dist/panel/components/ui/command/command.svelte +8 -8
  184. package/dist/panel/components/ui/context-menu/ContextMenu.svelte +10 -5
  185. package/dist/panel/components/ui/context-menu/ContextMenu.svelte.d.ts +2 -2
  186. package/dist/panel/components/ui/context-menu/ContextMenuItem.svelte +8 -8
  187. package/dist/panel/components/ui/context-menu/context-menu-item.css +20 -20
  188. package/dist/panel/components/ui/context-menu/context-menu.css +12 -12
  189. package/dist/panel/components/ui/dialog/dialog-content.css +45 -45
  190. package/dist/panel/components/ui/dialog/dialog-content.svelte +33 -33
  191. package/dist/panel/components/ui/dialog/dialog-description.css +2 -2
  192. package/dist/panel/components/ui/dialog/dialog-description.svelte +7 -3
  193. package/dist/panel/components/ui/dialog/dialog-footer.svelte +20 -20
  194. package/dist/panel/components/ui/dialog/dialog-header.svelte +22 -22
  195. package/dist/panel/components/ui/dialog/dialog-overlay.css +6 -6
  196. package/dist/panel/components/ui/dialog/dialog-overlay.svelte +7 -3
  197. package/dist/panel/components/ui/dialog/dialog-title.css +3 -3
  198. package/dist/panel/components/ui/dialog/dialog-title.svelte +7 -3
  199. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +55 -55
  200. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-content.css +8 -8
  201. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-content.svelte +14 -9
  202. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-group-heading.css +5 -5
  203. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +10 -10
  204. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-item.css +19 -19
  205. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-item.svelte +9 -9
  206. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-radio-item.css +17 -17
  207. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +17 -17
  208. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-separator.css +3 -3
  209. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-separator.svelte +12 -4
  210. package/dist/panel/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +6 -2
  211. package/dist/panel/components/ui/input/input.svelte +93 -93
  212. package/dist/panel/components/ui/label/label.css +7 -7
  213. package/dist/panel/components/ui/label/label.svelte +8 -8
  214. package/dist/panel/components/ui/language-switcher/LanguageSwitcher.svelte +48 -48
  215. package/dist/panel/components/ui/nav/Nav.svelte +151 -151
  216. package/dist/panel/components/ui/nav/NavGroup.svelte +85 -85
  217. package/dist/panel/components/ui/nav/NavItem.svelte +42 -42
  218. package/dist/panel/components/ui/nav/NavItemButton.svelte +39 -38
  219. package/dist/panel/components/ui/nav/NavItemButtonCaret.svelte +8 -8
  220. package/dist/panel/components/ui/nav/UserButton.svelte +90 -90
  221. package/dist/panel/components/ui/page-header/PageHeader.svelte +80 -80
  222. package/dist/panel/components/ui/pane/pane-resizer.svelte +12 -12
  223. package/dist/panel/components/ui/popover/popover-content.css +7 -7
  224. package/dist/panel/components/ui/popover/popover-content.svelte +16 -10
  225. package/dist/panel/components/ui/radio-group/radio-group-item.svelte +8 -7
  226. package/dist/panel/components/ui/radio-group/radio-group.css +23 -23
  227. package/dist/panel/components/ui/radio-group/radio-group.svelte +8 -8
  228. package/dist/panel/components/ui/radio-row-group/radio-row-group-item.svelte +9 -9
  229. package/dist/panel/components/ui/radio-row-group/radio-row-group.css +27 -27
  230. package/dist/panel/components/ui/radio-row-group/radio-row-group.svelte +14 -9
  231. package/dist/panel/components/ui/scroll-area/scroll-area-scrollbar.svelte +14 -14
  232. package/dist/panel/components/ui/scroll-area/scroll-area.css +21 -21
  233. package/dist/panel/components/ui/scroll-area/scroll-area.svelte +26 -26
  234. package/dist/panel/components/ui/separator/separator.svelte +25 -25
  235. package/dist/panel/components/ui/sheet/sheet-content.svelte +33 -33
  236. package/dist/panel/components/ui/sheet/sheet-description.svelte +6 -2
  237. package/dist/panel/components/ui/sheet/sheet-footer.svelte +10 -5
  238. package/dist/panel/components/ui/sheet/sheet-header.svelte +10 -5
  239. package/dist/panel/components/ui/sheet/sheet-overlay.svelte +7 -3
  240. package/dist/panel/components/ui/sheet/sheet-title.svelte +6 -2
  241. package/dist/panel/components/ui/sheet/sheet.css +81 -81
  242. package/dist/panel/components/ui/sheet/sheet.svelte +5 -5
  243. package/dist/panel/components/ui/sonner/sonner.css +12 -12
  244. package/dist/panel/components/ui/sonner/sonner.svelte +15 -15
  245. package/dist/panel/components/ui/spin-loader/SpinLoader.svelte +2 -2
  246. package/dist/panel/components/ui/spin-loader/spin-loader.css +10 -10
  247. package/dist/panel/components/ui/switch/switch.css +36 -36
  248. package/dist/panel/components/ui/switch/switch.svelte +9 -9
  249. package/dist/panel/components/ui/tabs/tabs-content.svelte +6 -2
  250. package/dist/panel/components/ui/tabs/tabs-list.svelte +2 -2
  251. package/dist/panel/components/ui/tabs/tabs-trigger.svelte +6 -2
  252. package/dist/panel/components/ui/tabs/tabs.css +47 -49
  253. package/dist/panel/components/ui/tabs/tabs.svelte +5 -5
  254. package/dist/panel/components/ui/tag/tag.svelte +49 -45
  255. package/dist/panel/components/ui/tooltip/tooltip-content.svelte +31 -31
  256. package/dist/panel/context/api-proxy.svelte.d.ts +12 -11
  257. package/dist/panel/context/api-proxy.svelte.js +6 -5
  258. package/dist/panel/pages/area/Area.svelte +17 -17
  259. package/dist/panel/pages/area/AreaVersionsDoc.svelte +19 -19
  260. package/dist/panel/pages/area/load.server.js +10 -2
  261. package/dist/panel/pages/auth/forgot-password/ForgotPassword.svelte +58 -54
  262. package/dist/panel/pages/auth/reset-password/ResetPassword.svelte +54 -54
  263. package/dist/panel/pages/auth/sign-in/SignIn.svelte +41 -41
  264. package/dist/panel/pages/collection/Collection.svelte +121 -121
  265. package/dist/panel/pages/collection/load.server.js +8 -2
  266. package/dist/panel/pages/collection-document/CollectionDocVersions.svelte +26 -26
  267. package/dist/panel/pages/collection-document/CollectionDocument.svelte +20 -20
  268. package/dist/panel/pages/collection-document/actions.server.js +3 -1
  269. package/dist/panel/pages/collection-document/load.server.js +4 -1
  270. package/dist/panel/pages/dashboard/Dashboard.svelte +109 -109
  271. package/dist/panel/pages/index.actions.server.js +1 -1
  272. package/dist/panel/pages/index.load.server.js +1 -1
  273. package/dist/panel/pages/live/Live.svelte +199 -192
  274. package/dist/panel/style/font.css +32 -32
  275. package/dist/panel/style/index.css +81 -81
  276. package/dist/panel/style/palette.css +27 -28
  277. package/dist/panel/style/radius.css +8 -8
  278. package/dist/panel/style/shadow.css +8 -8
  279. package/dist/panel/style/size.css +41 -41
  280. package/dist/panel/style/util.css +1 -1
  281. package/dist/util/cases.d.ts +1 -1
  282. package/package.json +5 -5
@@ -16,767 +16,767 @@ import { INPUT_DIR, OUTPUT_DIR } from '../../constants.js';
16
16
  */
17
17
 
18
18
  export async function sanitize() {
19
- const root = process.cwd();
20
- const configDir = path.resolve(root, 'src/lib/', INPUT_DIR);
21
- const outputDir = path.resolve(root, 'src/lib/', OUTPUT_DIR);
22
-
23
- logger.info('Sanitizing config...');
24
-
25
- // List all existing files recursively in the output directory
26
- const existingFiles = new Set();
27
- if (fs.existsSync(outputDir)) {
28
- const scanExistingFiles = (dir) => {
29
- const entries = fs.readdirSync(dir, { withFileTypes: true });
30
- for (const entry of entries) {
31
- const fullPath = path.join(dir, entry.name);
32
- if (entry.isDirectory()) {
33
- scanExistingFiles(fullPath);
34
- } else {
35
- existingFiles.add(path.relative(outputDir, fullPath));
36
- }
37
- }
38
- };
39
- scanExistingFiles(outputDir);
40
- } else {
41
- fs.mkdirSync(outputDir, { recursive: true });
42
- }
43
-
44
- // Track files that will be written during sanitization
45
- const outputFiles = new Set();
46
-
47
- // Track files that get split into both client and server versions
48
- const splitFiles = new Set();
49
-
50
- // Scan all TypeScript files (excluding .server.ts)
51
- const allFiles = await scanConfigFiles(configDir);
52
-
53
- // Pre-scan files to determine which ones will be split (contain server content)
54
- for (const filePath of allFiles) {
55
- const relativePath = path.relative(configDir, filePath);
56
- try {
57
- const content = fs.readFileSync(filePath, 'utf-8');
58
- const ast = babelParse(content, 'ts', { sourceType: 'module', attachComment: false });
59
- const analysis = analyzeFile(ast);
60
- if (analysis.hasServerContent) {
61
- // Normalize to forward slashes to match how imports are compared later
62
- splitFiles.add(relativePath.split(path.sep).join('/'));
63
- }
64
- } catch (err) {
65
- logger.warn(`Failed to analyze ${relativePath} during pre-scan: ${err.message}`);
66
- }
67
- }
68
-
69
- // Also scan and copy all existing .server.ts files
70
- const serverFiles = await scanServerFiles(configDir);
71
-
72
- // Scan and copy all other files (non-.ts files like .svelte, .json, etc.)
73
- const otherFiles = await scanOtherFiles(configDir);
74
-
75
- for (const filePath of allFiles) {
76
- const relativePath = path.relative(configDir, filePath);
77
- logger.debug(` Processing: ${relativePath}`);
78
-
79
- // Check if corresponding .server.ts already exists
80
- const originalServerPath = filePath.replace('.ts', '.server.ts');
81
- if (fs.existsSync(originalServerPath)) {
82
- throw new Error(
83
- `Error: ${originalServerPath} already exists. Remove it before running sanitization.`
84
- );
85
- }
86
-
87
- // Process the file and output to .generated
88
- await processConfigFile(filePath, configDir, outputDir, outputFiles, splitFiles);
89
- }
90
-
91
- // Copy existing .server.ts files
92
- for (const serverFilePath of serverFiles) {
93
- const relativePath = path.relative(configDir, serverFilePath);
94
- const outputPath = path.join(outputDir, relativePath);
95
- const outputDirPath = path.dirname(outputPath);
96
-
97
- if (!fs.existsSync(outputDirPath)) {
98
- fs.mkdirSync(outputDirPath, { recursive: true });
99
- }
100
-
101
- const content = fs.readFileSync(serverFilePath, 'utf-8');
102
- const relativeOutputPath = path.relative(outputDir, outputPath);
103
- outputFiles.add(relativeOutputPath);
104
-
105
- // Only write if content is different or file doesn't exist
106
- if (shouldWriteFile(outputPath, content)) {
107
- fs.writeFileSync(outputPath, content);
108
- logger.debug(` Copied: ${relativePath}`);
109
- }
110
- }
111
-
112
- // Copy other files (.svelte, .json, etc.)
113
- for (const otherFilePath of otherFiles) {
114
- const relativePath = path.relative(configDir, otherFilePath);
115
- const outputPath = path.join(outputDir, relativePath);
116
- const outputDirPath = path.dirname(outputPath);
117
-
118
- if (!fs.existsSync(outputDirPath)) {
119
- fs.mkdirSync(outputDirPath, { recursive: true });
120
- }
121
-
122
- const content = fs.readFileSync(otherFilePath, 'utf-8');
123
- const relativeOutputPath = path.relative(outputDir, outputPath);
124
- outputFiles.add(relativeOutputPath);
125
-
126
- // Only write if content is different or file doesn't exist
127
- if (shouldWriteFile(outputPath, content)) {
128
- fs.writeFileSync(outputPath, content);
129
- logger.debug(` Copied: ${relativePath}`);
130
- }
131
- }
132
-
133
- // Delete files that exist in current list but not in output list
134
- for (const existingFile of existingFiles) {
135
- // Do not delete "schema.server.ts"
136
- if (!outputFiles.has(existingFile) && existingFile !== 'schema.server.ts') {
137
- const fileToDelete = path.join(outputDir, existingFile);
138
- fs.unlinkSync(fileToDelete);
139
- logger.debug(` Deleted: ${existingFile}`);
140
- }
141
- }
142
-
143
- logger.info('Sanitization complete');
19
+ const root = process.cwd();
20
+ const configDir = path.resolve(root, 'src/lib/', INPUT_DIR);
21
+ const outputDir = path.resolve(root, 'src/lib/', OUTPUT_DIR);
22
+
23
+ logger.info('Sanitizing config...');
24
+
25
+ // List all existing files recursively in the output directory
26
+ const existingFiles = new Set();
27
+ if (fs.existsSync(outputDir)) {
28
+ const scanExistingFiles = (dir) => {
29
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
30
+ for (const entry of entries) {
31
+ const fullPath = path.join(dir, entry.name);
32
+ if (entry.isDirectory()) {
33
+ scanExistingFiles(fullPath);
34
+ } else {
35
+ existingFiles.add(path.relative(outputDir, fullPath));
36
+ }
37
+ }
38
+ };
39
+ scanExistingFiles(outputDir);
40
+ } else {
41
+ fs.mkdirSync(outputDir, { recursive: true });
42
+ }
43
+
44
+ // Track files that will be written during sanitization
45
+ const outputFiles = new Set();
46
+
47
+ // Track files that get split into both client and server versions
48
+ const splitFiles = new Set();
49
+
50
+ // Scan all TypeScript files (excluding .server.ts)
51
+ const allFiles = await scanConfigFiles(configDir);
52
+
53
+ // Pre-scan files to determine which ones will be split (contain server content)
54
+ for (const filePath of allFiles) {
55
+ const relativePath = path.relative(configDir, filePath);
56
+ try {
57
+ const content = fs.readFileSync(filePath, 'utf-8');
58
+ const ast = babelParse(content, 'ts', { sourceType: 'module', attachComment: false });
59
+ const analysis = analyzeFile(ast);
60
+ if (analysis.hasServerContent) {
61
+ // Normalize to forward slashes to match how imports are compared later
62
+ splitFiles.add(relativePath.split(path.sep).join('/'));
63
+ }
64
+ } catch (err) {
65
+ logger.warn(`Failed to analyze ${relativePath} during pre-scan: ${err.message}`);
66
+ }
67
+ }
68
+
69
+ // Also scan and copy all existing .server.ts files
70
+ const serverFiles = await scanServerFiles(configDir);
71
+
72
+ // Scan and copy all other files (non-.ts files like .svelte, .json, etc.)
73
+ const otherFiles = await scanOtherFiles(configDir);
74
+
75
+ for (const filePath of allFiles) {
76
+ const relativePath = path.relative(configDir, filePath);
77
+ logger.debug(` Processing: ${relativePath}`);
78
+
79
+ // Check if corresponding .server.ts already exists
80
+ const originalServerPath = filePath.replace('.ts', '.server.ts');
81
+ if (fs.existsSync(originalServerPath)) {
82
+ throw new Error(
83
+ `Error: ${originalServerPath} already exists. Remove it before running sanitization.`
84
+ );
85
+ }
86
+
87
+ // Process the file and output to .generated
88
+ await processConfigFile(filePath, configDir, outputDir, outputFiles, splitFiles);
89
+ }
90
+
91
+ // Copy existing .server.ts files
92
+ for (const serverFilePath of serverFiles) {
93
+ const relativePath = path.relative(configDir, serverFilePath);
94
+ const outputPath = path.join(outputDir, relativePath);
95
+ const outputDirPath = path.dirname(outputPath);
96
+
97
+ if (!fs.existsSync(outputDirPath)) {
98
+ fs.mkdirSync(outputDirPath, { recursive: true });
99
+ }
100
+
101
+ const content = fs.readFileSync(serverFilePath, 'utf-8');
102
+ const relativeOutputPath = path.relative(outputDir, outputPath);
103
+ outputFiles.add(relativeOutputPath);
104
+
105
+ // Only write if content is different or file doesn't exist
106
+ if (shouldWriteFile(outputPath, content)) {
107
+ fs.writeFileSync(outputPath, content);
108
+ logger.debug(` Copied: ${relativePath}`);
109
+ }
110
+ }
111
+
112
+ // Copy other files (.svelte, .json, etc.)
113
+ for (const otherFilePath of otherFiles) {
114
+ const relativePath = path.relative(configDir, otherFilePath);
115
+ const outputPath = path.join(outputDir, relativePath);
116
+ const outputDirPath = path.dirname(outputPath);
117
+
118
+ if (!fs.existsSync(outputDirPath)) {
119
+ fs.mkdirSync(outputDirPath, { recursive: true });
120
+ }
121
+
122
+ const content = fs.readFileSync(otherFilePath, 'utf-8');
123
+ const relativeOutputPath = path.relative(outputDir, outputPath);
124
+ outputFiles.add(relativeOutputPath);
125
+
126
+ // Only write if content is different or file doesn't exist
127
+ if (shouldWriteFile(outputPath, content)) {
128
+ fs.writeFileSync(outputPath, content);
129
+ logger.debug(` Copied: ${relativePath}`);
130
+ }
131
+ }
132
+
133
+ // Delete files that exist in current list but not in output list
134
+ for (const existingFile of existingFiles) {
135
+ // Do not delete "schema.server.ts"
136
+ if (!outputFiles.has(existingFile) && existingFile !== 'schema.server.ts') {
137
+ const fileToDelete = path.join(outputDir, existingFile);
138
+ fs.unlinkSync(fileToDelete);
139
+ logger.debug(` Deleted: ${existingFile}`);
140
+ }
141
+ }
142
+
143
+ logger.info('Sanitization complete');
144
144
  }
145
145
 
146
146
  async function scanConfigFiles(configDir) {
147
- const files = [];
147
+ const files = [];
148
148
 
149
- function scanDir(currentDir) {
150
- const items = fs.readdirSync(currentDir, { withFileTypes: true });
149
+ function scanDir(currentDir) {
150
+ const items = fs.readdirSync(currentDir, { withFileTypes: true });
151
151
 
152
- for (const item of items) {
153
- const fullPath = path.join(currentDir, item.name);
152
+ for (const item of items) {
153
+ const fullPath = path.join(currentDir, item.name);
154
154
 
155
- if (item.isDirectory() && item.name !== OUTPUT_DIR) {
156
- scanDir(fullPath);
157
- } else if (item.isFile() && item.name.endsWith('.ts') && !item.name.endsWith('.server.ts')) {
158
- files.push(fullPath);
159
- }
160
- }
161
- }
155
+ if (item.isDirectory() && item.name !== OUTPUT_DIR) {
156
+ scanDir(fullPath);
157
+ } else if (item.isFile() && item.name.endsWith('.ts') && !item.name.endsWith('.server.ts')) {
158
+ files.push(fullPath);
159
+ }
160
+ }
161
+ }
162
162
 
163
- scanDir(configDir);
164
- return files;
163
+ scanDir(configDir);
164
+ return files;
165
165
  }
166
166
 
167
167
  async function scanServerFiles(configDir) {
168
- const files = [];
168
+ const files = [];
169
169
 
170
- function scanDir(currentDir) {
171
- const items = fs.readdirSync(currentDir, { withFileTypes: true });
170
+ function scanDir(currentDir) {
171
+ const items = fs.readdirSync(currentDir, { withFileTypes: true });
172
172
 
173
- for (const item of items) {
174
- const fullPath = path.join(currentDir, item.name);
173
+ for (const item of items) {
174
+ const fullPath = path.join(currentDir, item.name);
175
175
 
176
- if (item.isDirectory() && item.name !== OUTPUT_DIR) {
177
- scanDir(fullPath);
178
- } else if (item.isFile() && item.name.endsWith('.server.ts')) {
179
- files.push(fullPath);
180
- }
181
- }
182
- }
176
+ if (item.isDirectory() && item.name !== OUTPUT_DIR) {
177
+ scanDir(fullPath);
178
+ } else if (item.isFile() && item.name.endsWith('.server.ts')) {
179
+ files.push(fullPath);
180
+ }
181
+ }
182
+ }
183
183
 
184
- scanDir(configDir);
185
- return files;
184
+ scanDir(configDir);
185
+ return files;
186
186
  }
187
187
 
188
188
  async function scanOtherFiles(configDir) {
189
- const files = [];
189
+ const files = [];
190
190
 
191
- function scanDir(currentDir) {
192
- const items = fs.readdirSync(currentDir, { withFileTypes: true });
191
+ function scanDir(currentDir) {
192
+ const items = fs.readdirSync(currentDir, { withFileTypes: true });
193
193
 
194
- for (const item of items) {
195
- const fullPath = path.join(currentDir, item.name);
194
+ for (const item of items) {
195
+ const fullPath = path.join(currentDir, item.name);
196
196
 
197
- if (item.isDirectory() && item.name !== OUTPUT_DIR) {
198
- scanDir(fullPath);
199
- } else if (item.isFile() && !item.name.endsWith('.ts')) {
200
- files.push(fullPath);
201
- }
202
- }
203
- }
197
+ if (item.isDirectory() && item.name !== OUTPUT_DIR) {
198
+ scanDir(fullPath);
199
+ } else if (item.isFile() && !item.name.endsWith('.ts')) {
200
+ files.push(fullPath);
201
+ }
202
+ }
203
+ }
204
204
 
205
- scanDir(configDir);
206
- return files;
205
+ scanDir(configDir);
206
+ return files;
207
207
  }
208
208
 
209
209
  async function processConfigFile(originalPath, configDir, outputDir, outputFiles, splitFiles) {
210
- const content = fs.readFileSync(originalPath, 'utf-8');
211
- const relativePath = path.relative(configDir, originalPath);
212
-
213
- try {
214
- const ast = babelParse(content, 'ts', { sourceType: 'module', attachComment: false });
215
-
216
- // Analyze the file to find $ patterns
217
- const analysis = analyzeFile(ast);
218
-
219
- if (!analysis.hasServerContent) {
220
- // Just copy the original file to .generated
221
- const outputPath = path.join(outputDir, relativePath);
222
- const outputDirPath = path.dirname(outputPath);
223
- if (!fs.existsSync(outputDirPath)) {
224
- fs.mkdirSync(outputDirPath, { recursive: true });
225
- }
226
- const relativeOutputPath = path.relative(outputDir, outputPath);
227
- outputFiles.add(relativeOutputPath);
228
-
229
- // Only write if content is different or file doesn't exist
230
- if (shouldWriteFile(outputPath, content)) {
231
- fs.writeFileSync(outputPath, content);
232
- }
233
- return;
234
- }
235
-
236
- // Calculate output paths
237
- const clientPath = path.join(outputDir, relativePath);
238
- const serverPath = path.join(outputDir, relativePath.replace('.ts', '.server.ts'));
239
-
240
- // Ensure output directories exist
241
- const clientDir = path.dirname(clientPath);
242
- const serverDir = path.dirname(serverPath);
243
- if (!fs.existsSync(clientDir)) {
244
- fs.mkdirSync(clientDir, { recursive: true });
245
- }
246
- if (!fs.existsSync(serverDir)) {
247
- fs.mkdirSync(serverDir, { recursive: true });
248
- }
249
-
250
- // Track output files
251
- const relativeServerPath = path.relative(outputDir, serverPath);
252
- const relativeClientPath = path.relative(outputDir, clientPath);
253
- outputFiles.add(relativeServerPath);
254
- outputFiles.add(relativeClientPath);
255
-
256
- // Track this as a split file (has both client and server versions)
257
- const relativeOriginalPath = path.relative(configDir, originalPath);
258
- splitFiles.add(relativeOriginalPath);
259
-
260
- // Create server version (full content with updated imports)
261
- const serverContent = updateServerImports(content, splitFiles, path.dirname(relativePath));
262
- if (shouldWriteFile(serverPath, serverContent)) {
263
- fs.writeFileSync(serverPath, serverContent);
264
- logger.debug(` Created: ${relativeServerPath}`);
265
- }
266
-
267
- // Create sanitized client version
268
- const clientAst = sanitizeClientAst(ast, analysis);
269
- // Clean up unused imports
270
- const cleanedAst = removeUnusedImports(clientAst);
271
- const clientCode = generate(cleanedAst, { compact: false, comments: true }).code;
272
- if (shouldWriteFile(clientPath, clientCode)) {
273
- fs.writeFileSync(clientPath, clientCode);
274
- logger.debug(` Sanitized: ${relativeClientPath}`);
275
- }
276
- } catch (error) {
277
- logger.error(` Failed to process ${originalPath}:`, error.message);
278
- throw error;
279
- }
210
+ const content = fs.readFileSync(originalPath, 'utf-8');
211
+ const relativePath = path.relative(configDir, originalPath);
212
+
213
+ try {
214
+ const ast = babelParse(content, 'ts', { sourceType: 'module', attachComment: false });
215
+
216
+ // Analyze the file to find $ patterns
217
+ const analysis = analyzeFile(ast);
218
+
219
+ if (!analysis.hasServerContent) {
220
+ // Just copy the original file to .generated
221
+ const outputPath = path.join(outputDir, relativePath);
222
+ const outputDirPath = path.dirname(outputPath);
223
+ if (!fs.existsSync(outputDirPath)) {
224
+ fs.mkdirSync(outputDirPath, { recursive: true });
225
+ }
226
+ const relativeOutputPath = path.relative(outputDir, outputPath);
227
+ outputFiles.add(relativeOutputPath);
228
+
229
+ // Only write if content is different or file doesn't exist
230
+ if (shouldWriteFile(outputPath, content)) {
231
+ fs.writeFileSync(outputPath, content);
232
+ }
233
+ return;
234
+ }
235
+
236
+ // Calculate output paths
237
+ const clientPath = path.join(outputDir, relativePath);
238
+ const serverPath = path.join(outputDir, relativePath.replace('.ts', '.server.ts'));
239
+
240
+ // Ensure output directories exist
241
+ const clientDir = path.dirname(clientPath);
242
+ const serverDir = path.dirname(serverPath);
243
+ if (!fs.existsSync(clientDir)) {
244
+ fs.mkdirSync(clientDir, { recursive: true });
245
+ }
246
+ if (!fs.existsSync(serverDir)) {
247
+ fs.mkdirSync(serverDir, { recursive: true });
248
+ }
249
+
250
+ // Track output files
251
+ const relativeServerPath = path.relative(outputDir, serverPath);
252
+ const relativeClientPath = path.relative(outputDir, clientPath);
253
+ outputFiles.add(relativeServerPath);
254
+ outputFiles.add(relativeClientPath);
255
+
256
+ // Track this as a split file (has both client and server versions)
257
+ const relativeOriginalPath = path.relative(configDir, originalPath);
258
+ splitFiles.add(relativeOriginalPath);
259
+
260
+ // Create server version (full content with updated imports)
261
+ const serverContent = updateServerImports(content, splitFiles, path.dirname(relativePath));
262
+ if (shouldWriteFile(serverPath, serverContent)) {
263
+ fs.writeFileSync(serverPath, serverContent);
264
+ logger.debug(` Created: ${relativeServerPath}`);
265
+ }
266
+
267
+ // Create sanitized client version
268
+ const clientAst = sanitizeClientAst(ast, analysis);
269
+ // Clean up unused imports
270
+ const cleanedAst = removeUnusedImports(clientAst);
271
+ const clientCode = generate(cleanedAst, { compact: false, comments: true }).code;
272
+ if (shouldWriteFile(clientPath, clientCode)) {
273
+ fs.writeFileSync(clientPath, clientCode);
274
+ logger.debug(` Sanitized: ${relativeClientPath}`);
275
+ }
276
+ } catch (error) {
277
+ logger.error(` Failed to process ${originalPath}:`, error.message);
278
+ throw error;
279
+ }
280
280
  }
281
281
 
282
282
  function analyzeFile(ast) {
283
- const analysis = {
284
- hasServerContent: false,
285
- serverImports: new Set(),
286
- serverFunctions: new Set(),
287
- dollarProperties: new Set(),
288
- dollarMethods: new Set(),
289
- functionsUsedInDollarProps: new Set()
290
- };
291
-
292
- // Walk the AST to find $ patterns
293
- function walkNode(node) {
294
- if (!node || typeof node !== 'object') return;
295
-
296
- // Find imports from .server.ts files
297
- if (t.isImportDeclaration(node)) {
298
- const source = node.source.value;
299
- if (source.includes('.server')) {
300
- analysis.hasServerContent = true;
301
- // Mark all imported identifiers as server
302
- node.specifiers.forEach((spec) => {
303
- if (t.isImportSpecifier(spec) || t.isImportDefaultSpecifier(spec)) {
304
- analysis.serverImports.add(spec.local.name);
305
- }
306
- });
307
- }
308
- }
309
-
310
- // Find $ prefixed object properties
311
- if (t.isObjectProperty(node) && t.isIdentifier(node.key) && node.key.name.startsWith('$')) {
312
- analysis.hasServerContent = true;
313
- analysis.dollarProperties.add(node.key.name);
314
- // Track functions used within $ properties
315
- findFunctionsInNode(node.value, analysis.functionsUsedInDollarProps);
316
- }
317
-
318
- // Find $ prefixed method calls (e.g., .$beforeSave())
319
- if (
320
- t.isCallExpression(node) &&
321
- t.isMemberExpression(node.callee) &&
322
- t.isIdentifier(node.callee.property) &&
323
- node.callee.property.name.startsWith('$')
324
- ) {
325
- analysis.hasServerContent = true;
326
- analysis.dollarMethods.add(node.callee.property.name);
327
- }
328
-
329
- // Find local functions that might be used by $ methods
330
- if (
331
- t.isVariableDeclarator(node) &&
332
- t.isIdentifier(node.id) &&
333
- (t.isArrowFunctionExpression(node.init) || t.isFunctionExpression(node.init))
334
- ) {
335
- // Check if this function is used by server content
336
- if (isFunctionUsedByServerContent(node.id.name, ast, analysis)) {
337
- analysis.serverFunctions.add(node.id.name);
338
- }
339
- }
340
-
341
- if (t.isFunctionDeclaration(node) && node.id) {
342
- if (isFunctionUsedByServerContent(node.id.name, ast, analysis)) {
343
- analysis.serverFunctions.add(node.id.name);
344
- }
345
- }
346
-
347
- // Recursively walk child nodes
348
- for (const key in node) {
349
- if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
350
- const child = node[key];
351
- if (Array.isArray(child)) {
352
- child.forEach(walkNode);
353
- } else if (child && typeof child === 'object') {
354
- walkNode(child);
355
- }
356
- }
357
- }
358
- }
359
-
360
- walkNode(ast);
361
- return analysis;
283
+ const analysis = {
284
+ hasServerContent: false,
285
+ serverImports: new Set(),
286
+ serverFunctions: new Set(),
287
+ dollarProperties: new Set(),
288
+ dollarMethods: new Set(),
289
+ functionsUsedInDollarProps: new Set()
290
+ };
291
+
292
+ // Walk the AST to find $ patterns
293
+ function walkNode(node) {
294
+ if (!node || typeof node !== 'object') return;
295
+
296
+ // Find imports from .server.ts files
297
+ if (t.isImportDeclaration(node)) {
298
+ const source = node.source.value;
299
+ if (source.includes('.server')) {
300
+ analysis.hasServerContent = true;
301
+ // Mark all imported identifiers as server
302
+ node.specifiers.forEach((spec) => {
303
+ if (t.isImportSpecifier(spec) || t.isImportDefaultSpecifier(spec)) {
304
+ analysis.serverImports.add(spec.local.name);
305
+ }
306
+ });
307
+ }
308
+ }
309
+
310
+ // Find $ prefixed object properties
311
+ if (t.isObjectProperty(node) && t.isIdentifier(node.key) && node.key.name.startsWith('$')) {
312
+ analysis.hasServerContent = true;
313
+ analysis.dollarProperties.add(node.key.name);
314
+ // Track functions used within $ properties
315
+ findFunctionsInNode(node.value, analysis.functionsUsedInDollarProps);
316
+ }
317
+
318
+ // Find $ prefixed method calls (e.g., .$beforeSave())
319
+ if (
320
+ t.isCallExpression(node) &&
321
+ t.isMemberExpression(node.callee) &&
322
+ t.isIdentifier(node.callee.property) &&
323
+ node.callee.property.name.startsWith('$')
324
+ ) {
325
+ analysis.hasServerContent = true;
326
+ analysis.dollarMethods.add(node.callee.property.name);
327
+ }
328
+
329
+ // Find local functions that might be used by $ methods
330
+ if (
331
+ t.isVariableDeclarator(node) &&
332
+ t.isIdentifier(node.id) &&
333
+ (t.isArrowFunctionExpression(node.init) || t.isFunctionExpression(node.init))
334
+ ) {
335
+ // Check if this function is used by server content
336
+ if (isFunctionUsedByServerContent(node.id.name, ast, analysis)) {
337
+ analysis.serverFunctions.add(node.id.name);
338
+ }
339
+ }
340
+
341
+ if (t.isFunctionDeclaration(node) && node.id) {
342
+ if (isFunctionUsedByServerContent(node.id.name, ast, analysis)) {
343
+ analysis.serverFunctions.add(node.id.name);
344
+ }
345
+ }
346
+
347
+ // Recursively walk child nodes
348
+ for (const key in node) {
349
+ if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
350
+ const child = node[key];
351
+ if (Array.isArray(child)) {
352
+ child.forEach(walkNode);
353
+ } else if (child && typeof child === 'object') {
354
+ walkNode(child);
355
+ }
356
+ }
357
+ }
358
+ }
359
+
360
+ walkNode(ast);
361
+ return analysis;
362
362
  }
363
363
 
364
364
  function isFunctionUsedByServerContent(functionName, ast) {
365
- // Simple heuristic: if function is used as argument to $ methods or imported from server
366
- let isUsedByServer = false;
367
-
368
- function checkUsage(node) {
369
- if (!node || typeof node !== 'object') return;
370
-
371
- // Check if function is used as argument to $ methods
372
- if (
373
- t.isCallExpression(node) &&
374
- t.isMemberExpression(node.callee) &&
375
- t.isIdentifier(node.callee.property) &&
376
- node.callee.property.name.startsWith('$')
377
- ) {
378
- // Check if our function is used as an argument
379
- node.arguments.forEach((arg) => {
380
- if (t.isIdentifier(arg) && arg.name === functionName) {
381
- isUsedByServer = true;
382
- }
383
- });
384
- }
385
-
386
- // Check if function is used in $ properties
387
- if (t.isObjectProperty(node) && t.isIdentifier(node.key) && node.key.name.startsWith('$')) {
388
- function checkValue(value) {
389
- if (t.isIdentifier(value) && value.name === functionName) {
390
- isUsedByServer = true;
391
- } else if (t.isObjectExpression(value)) {
392
- value.properties.forEach((prop) => {
393
- if (t.isObjectProperty(prop)) {
394
- checkValue(prop.value);
395
- }
396
- });
397
- } else if (t.isArrayExpression(value)) {
398
- value.elements.forEach((el) => {
399
- if (el) checkValue(el);
400
- });
401
- }
402
- }
403
-
404
- checkValue(node.value);
405
- }
406
-
407
- // Recursively check child nodes
408
- for (const key in node) {
409
- if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
410
- const child = node[key];
411
- if (Array.isArray(child)) {
412
- child.forEach(checkUsage);
413
- } else if (child && typeof child === 'object') {
414
- checkUsage(child);
415
- }
416
- }
417
- }
418
- }
419
-
420
- checkUsage(ast);
421
- return isUsedByServer;
365
+ // Simple heuristic: if function is used as argument to $ methods or imported from server
366
+ let isUsedByServer = false;
367
+
368
+ function checkUsage(node) {
369
+ if (!node || typeof node !== 'object') return;
370
+
371
+ // Check if function is used as argument to $ methods
372
+ if (
373
+ t.isCallExpression(node) &&
374
+ t.isMemberExpression(node.callee) &&
375
+ t.isIdentifier(node.callee.property) &&
376
+ node.callee.property.name.startsWith('$')
377
+ ) {
378
+ // Check if our function is used as an argument
379
+ node.arguments.forEach((arg) => {
380
+ if (t.isIdentifier(arg) && arg.name === functionName) {
381
+ isUsedByServer = true;
382
+ }
383
+ });
384
+ }
385
+
386
+ // Check if function is used in $ properties
387
+ if (t.isObjectProperty(node) && t.isIdentifier(node.key) && node.key.name.startsWith('$')) {
388
+ function checkValue(value) {
389
+ if (t.isIdentifier(value) && value.name === functionName) {
390
+ isUsedByServer = true;
391
+ } else if (t.isObjectExpression(value)) {
392
+ value.properties.forEach((prop) => {
393
+ if (t.isObjectProperty(prop)) {
394
+ checkValue(prop.value);
395
+ }
396
+ });
397
+ } else if (t.isArrayExpression(value)) {
398
+ value.elements.forEach((el) => {
399
+ if (el) checkValue(el);
400
+ });
401
+ }
402
+ }
403
+
404
+ checkValue(node.value);
405
+ }
406
+
407
+ // Recursively check child nodes
408
+ for (const key in node) {
409
+ if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
410
+ const child = node[key];
411
+ if (Array.isArray(child)) {
412
+ child.forEach(checkUsage);
413
+ } else if (child && typeof child === 'object') {
414
+ checkUsage(child);
415
+ }
416
+ }
417
+ }
418
+ }
419
+
420
+ checkUsage(ast);
421
+ return isUsedByServer;
422
422
  }
423
423
 
424
424
  function findFunctionsInNode(node, functionsSet) {
425
- if (!node || typeof node !== 'object') return;
426
-
427
- if (t.isIdentifier(node)) {
428
- functionsSet.add(node.name);
429
- } else if (t.isArrayExpression(node)) {
430
- node.elements.forEach((element) => {
431
- if (element) findFunctionsInNode(element, functionsSet);
432
- });
433
- } else if (t.isObjectExpression(node)) {
434
- node.properties.forEach((prop) => {
435
- if (t.isObjectProperty(prop)) {
436
- findFunctionsInNode(prop.value, functionsSet);
437
- }
438
- });
439
- }
440
-
441
- // Recursively check child nodes
442
- for (const key in node) {
443
- if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
444
- const child = node[key];
445
- if (Array.isArray(child)) {
446
- child.forEach((item) => findFunctionsInNode(item, functionsSet));
447
- } else if (child && typeof child === 'object') {
448
- findFunctionsInNode(child, functionsSet);
449
- }
450
- }
451
- }
425
+ if (!node || typeof node !== 'object') return;
426
+
427
+ if (t.isIdentifier(node)) {
428
+ functionsSet.add(node.name);
429
+ } else if (t.isArrayExpression(node)) {
430
+ node.elements.forEach((element) => {
431
+ if (element) findFunctionsInNode(element, functionsSet);
432
+ });
433
+ } else if (t.isObjectExpression(node)) {
434
+ node.properties.forEach((prop) => {
435
+ if (t.isObjectProperty(prop)) {
436
+ findFunctionsInNode(prop.value, functionsSet);
437
+ }
438
+ });
439
+ }
440
+
441
+ // Recursively check child nodes
442
+ for (const key in node) {
443
+ if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
444
+ const child = node[key];
445
+ if (Array.isArray(child)) {
446
+ child.forEach((item) => findFunctionsInNode(item, functionsSet));
447
+ } else if (child && typeof child === 'object') {
448
+ findFunctionsInNode(child, functionsSet);
449
+ }
450
+ }
451
+ }
452
452
  }
453
453
 
454
454
  function sanitizeClientAst(ast, analysis) {
455
- const clonedAst = JSON.parse(JSON.stringify(ast));
456
-
457
- function sanitizeNode(node) {
458
- if (!node || typeof node !== 'object') return node;
459
-
460
- // Remove imports from .server.ts files
461
- if (t.isImportDeclaration(node)) {
462
- const source = node.source.value;
463
- if (source.includes('.server')) {
464
- return null; // Mark for removal
465
- }
466
- }
467
-
468
- // Replace rime with rimeClient in imports
469
- if (t.isImportDeclaration(node)) {
470
- node.specifiers.forEach((spec) => {
471
- if (
472
- t.isImportSpecifier(spec) &&
473
- t.isIdentifier(spec.imported) &&
474
- spec.imported.name === 'rime'
475
- ) {
476
- spec.imported.name = 'rimeClient';
477
- if (t.isIdentifier(spec.local)) {
478
- spec.local.name = 'rimeClient';
479
- }
480
- }
481
- });
482
- }
483
-
484
- // Replace rime function calls with rimeClient
485
- if (t.isCallExpression(node) && t.isIdentifier(node.callee) && node.callee.name === 'rime') {
486
- node.callee.name = 'rimeClient';
487
- }
488
-
489
- // Remove $ prefixed object properties
490
- if (t.isObjectExpression(node)) {
491
- node.properties = node.properties.filter((prop) => {
492
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name.startsWith('$')) {
493
- return false; // Remove $ properties
494
- }
495
- return true;
496
- });
497
- }
498
-
499
- // Remove $ prefixed method calls from call chains
500
- if (
501
- t.isCallExpression(node) &&
502
- t.isMemberExpression(node.callee) &&
503
- t.isIdentifier(node.callee.property) &&
504
- node.callee.property.name.startsWith('$')
505
- ) {
506
- // Return the object the method was called on (strip the method call)
507
- return sanitizeNode(node.callee.object);
508
- }
509
-
510
- // Remove server function declarations and functions only used in $ properties
511
- if (t.isVariableDeclaration(node)) {
512
- node.declarations = node.declarations.filter((declarator) => {
513
- if (t.isIdentifier(declarator.id)) {
514
- const name = declarator.id.name;
515
- // Remove if it's a server function or only used in $ properties
516
- if (analysis.serverFunctions.has(name) || analysis.functionsUsedInDollarProps.has(name)) {
517
- return false;
518
- }
519
- }
520
- return true;
521
- });
522
- // If no declarations left, mark for removal
523
- if (node.declarations.length === 0) {
524
- return null;
525
- }
526
- }
527
-
528
- if (t.isFunctionDeclaration(node) && node.id) {
529
- const name = node.id.name;
530
- // Remove if it's a server function or only used in $ properties
531
- if (analysis.serverFunctions.has(name) || analysis.functionsUsedInDollarProps.has(name)) {
532
- return null;
533
- }
534
- }
535
-
536
- // Recursively sanitize child nodes
537
- for (const key in node) {
538
- if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
539
- const child = node[key];
540
- if (Array.isArray(child)) {
541
- node[key] = child.map(sanitizeNode).filter((item) => item !== null);
542
- } else if (child && typeof child === 'object') {
543
- const sanitized = sanitizeNode(child);
544
- if (sanitized === null) {
545
- delete node[key];
546
- } else {
547
- node[key] = sanitized;
548
- }
549
- }
550
- }
551
- }
552
-
553
- return node;
554
- }
555
-
556
- // Apply sanitization
557
- sanitizeNode(clonedAst);
558
-
559
- // Remove null body items (removed imports/declarations)
560
- if (clonedAst.body) {
561
- clonedAst.body = clonedAst.body.filter((item) => item !== null);
562
- }
563
-
564
- // Final cleanup: remove orphaned expression statements (standalone identifiers)
565
- if (clonedAst.body) {
566
- clonedAst.body = clonedAst.body.filter((node) => {
567
- // Remove expression statements that are just standalone identifiers
568
- if (t.isExpressionStatement(node) && t.isIdentifier(node.expression)) {
569
- return false; // Remove orphaned references like "legendField;"
570
- }
571
- return true;
572
- });
573
- }
574
-
575
- return clonedAst;
455
+ const clonedAst = JSON.parse(JSON.stringify(ast));
456
+
457
+ function sanitizeNode(node) {
458
+ if (!node || typeof node !== 'object') return node;
459
+
460
+ // Remove imports from .server.ts files
461
+ if (t.isImportDeclaration(node)) {
462
+ const source = node.source.value;
463
+ if (source.includes('.server')) {
464
+ return null; // Mark for removal
465
+ }
466
+ }
467
+
468
+ // Replace rime with rimeClient in imports
469
+ if (t.isImportDeclaration(node)) {
470
+ node.specifiers.forEach((spec) => {
471
+ if (
472
+ t.isImportSpecifier(spec) &&
473
+ t.isIdentifier(spec.imported) &&
474
+ spec.imported.name === 'rime'
475
+ ) {
476
+ spec.imported.name = 'rimeClient';
477
+ if (t.isIdentifier(spec.local)) {
478
+ spec.local.name = 'rimeClient';
479
+ }
480
+ }
481
+ });
482
+ }
483
+
484
+ // Replace rime function calls with rimeClient
485
+ if (t.isCallExpression(node) && t.isIdentifier(node.callee) && node.callee.name === 'rime') {
486
+ node.callee.name = 'rimeClient';
487
+ }
488
+
489
+ // Remove $ prefixed object properties
490
+ if (t.isObjectExpression(node)) {
491
+ node.properties = node.properties.filter((prop) => {
492
+ if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name.startsWith('$')) {
493
+ return false; // Remove $ properties
494
+ }
495
+ return true;
496
+ });
497
+ }
498
+
499
+ // Remove $ prefixed method calls from call chains
500
+ if (
501
+ t.isCallExpression(node) &&
502
+ t.isMemberExpression(node.callee) &&
503
+ t.isIdentifier(node.callee.property) &&
504
+ node.callee.property.name.startsWith('$')
505
+ ) {
506
+ // Return the object the method was called on (strip the method call)
507
+ return sanitizeNode(node.callee.object);
508
+ }
509
+
510
+ // Remove server function declarations and functions only used in $ properties
511
+ if (t.isVariableDeclaration(node)) {
512
+ node.declarations = node.declarations.filter((declarator) => {
513
+ if (t.isIdentifier(declarator.id)) {
514
+ const name = declarator.id.name;
515
+ // Remove if it's a server function or only used in $ properties
516
+ if (analysis.serverFunctions.has(name) || analysis.functionsUsedInDollarProps.has(name)) {
517
+ return false;
518
+ }
519
+ }
520
+ return true;
521
+ });
522
+ // If no declarations left, mark for removal
523
+ if (node.declarations.length === 0) {
524
+ return null;
525
+ }
526
+ }
527
+
528
+ if (t.isFunctionDeclaration(node) && node.id) {
529
+ const name = node.id.name;
530
+ // Remove if it's a server function or only used in $ properties
531
+ if (analysis.serverFunctions.has(name) || analysis.functionsUsedInDollarProps.has(name)) {
532
+ return null;
533
+ }
534
+ }
535
+
536
+ // Recursively sanitize child nodes
537
+ for (const key in node) {
538
+ if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
539
+ const child = node[key];
540
+ if (Array.isArray(child)) {
541
+ node[key] = child.map(sanitizeNode).filter((item) => item !== null);
542
+ } else if (child && typeof child === 'object') {
543
+ const sanitized = sanitizeNode(child);
544
+ if (sanitized === null) {
545
+ delete node[key];
546
+ } else {
547
+ node[key] = sanitized;
548
+ }
549
+ }
550
+ }
551
+ }
552
+
553
+ return node;
554
+ }
555
+
556
+ // Apply sanitization
557
+ sanitizeNode(clonedAst);
558
+
559
+ // Remove null body items (removed imports/declarations)
560
+ if (clonedAst.body) {
561
+ clonedAst.body = clonedAst.body.filter((item) => item !== null);
562
+ }
563
+
564
+ // Final cleanup: remove orphaned expression statements (standalone identifiers)
565
+ if (clonedAst.body) {
566
+ clonedAst.body = clonedAst.body.filter((node) => {
567
+ // Remove expression statements that are just standalone identifiers
568
+ if (t.isExpressionStatement(node) && t.isIdentifier(node.expression)) {
569
+ return false; // Remove orphaned references like "legendField;"
570
+ }
571
+ return true;
572
+ });
573
+ }
574
+
575
+ return clonedAst;
576
576
  }
577
577
 
578
578
  function removeUnusedImports(ast) {
579
- const clonedAst = JSON.parse(JSON.stringify(ast));
580
-
581
- // Collect all imported identifiers
582
- const importedIdentifiers = new Map(); // identifier -> import declaration
583
- const usedIdentifiers = new Set();
584
-
585
- // First pass: collect all imports
586
- function collectImports(node) {
587
- if (t.isImportDeclaration(node)) {
588
- node.specifiers.forEach((spec) => {
589
- let localName;
590
- if (t.isImportDefaultSpecifier(spec)) {
591
- localName = spec.local.name;
592
- } else if (t.isImportSpecifier(spec)) {
593
- localName = spec.local.name;
594
- } else if (t.isImportNamespaceSpecifier(spec)) {
595
- localName = spec.local.name;
596
- }
597
-
598
- if (localName) {
599
- importedIdentifiers.set(localName, node);
600
- }
601
- });
602
- }
603
- }
604
-
605
- // Second pass: find used identifiers
606
- function findUsedIdentifiers(node) {
607
- if (!node || typeof node !== 'object') return;
608
-
609
- // Skip import declarations and variable declarators' ids to avoid marking declarations as usage
610
- if (t.isImportDeclaration(node)) {
611
- return;
612
- }
613
-
614
- if (t.isVariableDeclarator(node)) {
615
- // Only check the init, not the id
616
- if (node.init) {
617
- findUsedIdentifiers(node.init);
618
- }
619
- return;
620
- }
621
-
622
- if (t.isFunctionDeclaration(node)) {
623
- // Only check the body, not the id and params
624
- if (node.body) {
625
- findUsedIdentifiers(node.body);
626
- }
627
- return;
628
- }
629
-
630
- // Skip $ prefixed object properties to avoid marking their content as used
631
- if (t.isObjectProperty(node) && t.isIdentifier(node.key) && node.key.name.startsWith('$')) {
632
- return;
633
- }
634
-
635
- if (t.isIdentifier(node)) {
636
- usedIdentifiers.add(node.name);
637
- }
638
-
639
- // Recursively check child nodes
640
- for (const key in node) {
641
- if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
642
- const child = node[key];
643
- if (Array.isArray(child)) {
644
- child.forEach(findUsedIdentifiers);
645
- } else if (child && typeof child === 'object') {
646
- findUsedIdentifiers(child);
647
- }
648
- }
649
- }
650
- }
651
-
652
- // Collect imports and find usage
653
- clonedAst.body.forEach(collectImports);
654
- clonedAst.body.forEach(findUsedIdentifiers);
655
-
656
- // Remove unused imports
657
- clonedAst.body = clonedAst.body.filter((node) => {
658
- if (t.isImportDeclaration(node)) {
659
- // Filter out unused specifiers
660
- node.specifiers = node.specifiers.filter((spec) => {
661
- let localName;
662
- if (t.isImportDefaultSpecifier(spec)) {
663
- localName = spec.local.name;
664
- } else if (t.isImportSpecifier(spec)) {
665
- localName = spec.local.name;
666
- } else if (t.isImportNamespaceSpecifier(spec)) {
667
- localName = spec.local.name;
668
- }
669
-
670
- return localName && usedIdentifiers.has(localName);
671
- });
672
-
673
- // Keep the import declaration only if it has remaining specifiers
674
- return node.specifiers.length > 0;
675
- }
676
- return true;
677
- });
678
-
679
- return clonedAst;
579
+ const clonedAst = JSON.parse(JSON.stringify(ast));
580
+
581
+ // Collect all imported identifiers
582
+ const importedIdentifiers = new Map(); // identifier -> import declaration
583
+ const usedIdentifiers = new Set();
584
+
585
+ // First pass: collect all imports
586
+ function collectImports(node) {
587
+ if (t.isImportDeclaration(node)) {
588
+ node.specifiers.forEach((spec) => {
589
+ let localName;
590
+ if (t.isImportDefaultSpecifier(spec)) {
591
+ localName = spec.local.name;
592
+ } else if (t.isImportSpecifier(spec)) {
593
+ localName = spec.local.name;
594
+ } else if (t.isImportNamespaceSpecifier(spec)) {
595
+ localName = spec.local.name;
596
+ }
597
+
598
+ if (localName) {
599
+ importedIdentifiers.set(localName, node);
600
+ }
601
+ });
602
+ }
603
+ }
604
+
605
+ // Second pass: find used identifiers
606
+ function findUsedIdentifiers(node) {
607
+ if (!node || typeof node !== 'object') return;
608
+
609
+ // Skip import declarations and variable declarators' ids to avoid marking declarations as usage
610
+ if (t.isImportDeclaration(node)) {
611
+ return;
612
+ }
613
+
614
+ if (t.isVariableDeclarator(node)) {
615
+ // Only check the init, not the id
616
+ if (node.init) {
617
+ findUsedIdentifiers(node.init);
618
+ }
619
+ return;
620
+ }
621
+
622
+ if (t.isFunctionDeclaration(node)) {
623
+ // Only check the body, not the id and params
624
+ if (node.body) {
625
+ findUsedIdentifiers(node.body);
626
+ }
627
+ return;
628
+ }
629
+
630
+ // Skip $ prefixed object properties to avoid marking their content as used
631
+ if (t.isObjectProperty(node) && t.isIdentifier(node.key) && node.key.name.startsWith('$')) {
632
+ return;
633
+ }
634
+
635
+ if (t.isIdentifier(node)) {
636
+ usedIdentifiers.add(node.name);
637
+ }
638
+
639
+ // Recursively check child nodes
640
+ for (const key in node) {
641
+ if (Object.prototype.hasOwnProperty.call(node, key) && key !== 'parent') {
642
+ const child = node[key];
643
+ if (Array.isArray(child)) {
644
+ child.forEach(findUsedIdentifiers);
645
+ } else if (child && typeof child === 'object') {
646
+ findUsedIdentifiers(child);
647
+ }
648
+ }
649
+ }
650
+ }
651
+
652
+ // Collect imports and find usage
653
+ clonedAst.body.forEach(collectImports);
654
+ clonedAst.body.forEach(findUsedIdentifiers);
655
+
656
+ // Remove unused imports
657
+ clonedAst.body = clonedAst.body.filter((node) => {
658
+ if (t.isImportDeclaration(node)) {
659
+ // Filter out unused specifiers
660
+ node.specifiers = node.specifiers.filter((spec) => {
661
+ let localName;
662
+ if (t.isImportDefaultSpecifier(spec)) {
663
+ localName = spec.local.name;
664
+ } else if (t.isImportSpecifier(spec)) {
665
+ localName = spec.local.name;
666
+ } else if (t.isImportNamespaceSpecifier(spec)) {
667
+ localName = spec.local.name;
668
+ }
669
+
670
+ return localName && usedIdentifiers.has(localName);
671
+ });
672
+
673
+ // Keep the import declaration only if it has remaining specifiers
674
+ return node.specifiers.length > 0;
675
+ }
676
+ return true;
677
+ });
678
+
679
+ return clonedAst;
680
680
  }
681
681
 
682
682
  /**
683
683
  * Updates imports in server files to use server versions of split modules
684
684
  */
685
685
  export function updateServerImports(content, splitFiles, currentDir) {
686
- try {
687
- const ast = babelParse(content, 'ts', { sourceType: 'module', attachComment: false });
688
-
689
- let hasChanges = false;
690
-
691
- // Helper to normalize paths for comparison with splitFiles entries
692
- const normalizeForCompare = (p) =>
693
- p
694
- .replace(/^\.\/?/, '')
695
- .split(path.sep)
696
- .join('/');
697
- const splitFilesSet = new Set(Array.from(splitFiles).map((s) => normalizeForCompare(s)));
698
-
699
- // Walk through all import declarations
700
- for (const node of ast.body) {
701
- if (t.isImportDeclaration(node) && t.isStringLiteral(node.source)) {
702
- const importPath = node.source.value;
703
-
704
- // Skip absolute imports and package imports
705
- if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
706
- continue;
707
- }
708
-
709
- // Skip already server imports
710
- if (importPath.includes('.server')) continue;
711
-
712
- // Normalize import path: remove trailing slashes and explicit extensions
713
- const cleanedImport = importPath.replace(/\/+$/, '');
714
- const importNoExt = cleanedImport.replace(/(\.js|\.ts)$/, '');
715
-
716
- // Build candidate relative paths (relative to config dir)
717
- const candidateFile = path.normalize(path.join(currentDir, importNoExt + '.ts'));
718
- const candidateIndex = path.normalize(path.join(currentDir, importNoExt, 'index.ts'));
719
-
720
- const candidateFileNorm = normalizeForCompare(candidateFile);
721
- const candidateIndexNorm = normalizeForCompare(candidateIndex);
722
-
723
- let matched = null;
724
- let matchedIsIndex = false;
725
-
726
- if (splitFilesSet.has(candidateFileNorm)) {
727
- matched = candidateFileNorm;
728
- matchedIsIndex = false;
729
- } else if (splitFilesSet.has(candidateIndexNorm)) {
730
- matched = candidateIndexNorm;
731
- matchedIsIndex = true;
732
- }
733
-
734
- if (matched) {
735
- // Build new import path: prefer .server.js so runtime imports point to JS server files
736
- let newImportPath;
737
- if (matchedIsIndex) {
738
- newImportPath = importNoExt + '/index.server.js';
739
- } else {
740
- newImportPath = importNoExt + '.server.js';
741
- }
742
-
743
- // Set the node's source value and ensure it renders with single quotes
744
- node.source.value = newImportPath;
745
- if (!node.source.extra || typeof node.source.extra !== 'object') {
746
- node.source.extra = {};
747
- }
748
- node.source.extra.raw = `'${newImportPath}'`;
749
- node.source.extra.rawValue = newImportPath;
750
- hasChanges = true;
751
- }
752
- }
753
- }
754
-
755
- if (hasChanges) {
756
- return generate(ast, { compact: false, comments: true }).code;
757
- }
758
-
759
- return content;
760
- } catch (error) {
761
- // If parsing fails, return original content
762
- logger.warn(`Failed to update imports in server file: ${error.message}`);
763
- return content;
764
- }
686
+ try {
687
+ const ast = babelParse(content, 'ts', { sourceType: 'module', attachComment: false });
688
+
689
+ let hasChanges = false;
690
+
691
+ // Helper to normalize paths for comparison with splitFiles entries
692
+ const normalizeForCompare = (p) =>
693
+ p
694
+ .replace(/^\.\/?/, '')
695
+ .split(path.sep)
696
+ .join('/');
697
+ const splitFilesSet = new Set(Array.from(splitFiles).map((s) => normalizeForCompare(s)));
698
+
699
+ // Walk through all import declarations
700
+ for (const node of ast.body) {
701
+ if (t.isImportDeclaration(node) && t.isStringLiteral(node.source)) {
702
+ const importPath = node.source.value;
703
+
704
+ // Skip absolute imports and package imports
705
+ if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
706
+ continue;
707
+ }
708
+
709
+ // Skip already server imports
710
+ if (importPath.includes('.server')) continue;
711
+
712
+ // Normalize import path: remove trailing slashes and explicit extensions
713
+ const cleanedImport = importPath.replace(/\/+$/, '');
714
+ const importNoExt = cleanedImport.replace(/(\.js|\.ts)$/, '');
715
+
716
+ // Build candidate relative paths (relative to config dir)
717
+ const candidateFile = path.normalize(path.join(currentDir, importNoExt + '.ts'));
718
+ const candidateIndex = path.normalize(path.join(currentDir, importNoExt, 'index.ts'));
719
+
720
+ const candidateFileNorm = normalizeForCompare(candidateFile);
721
+ const candidateIndexNorm = normalizeForCompare(candidateIndex);
722
+
723
+ let matched = null;
724
+ let matchedIsIndex = false;
725
+
726
+ if (splitFilesSet.has(candidateFileNorm)) {
727
+ matched = candidateFileNorm;
728
+ matchedIsIndex = false;
729
+ } else if (splitFilesSet.has(candidateIndexNorm)) {
730
+ matched = candidateIndexNorm;
731
+ matchedIsIndex = true;
732
+ }
733
+
734
+ if (matched) {
735
+ // Build new import path: prefer .server.js so runtime imports point to JS server files
736
+ let newImportPath;
737
+ if (matchedIsIndex) {
738
+ newImportPath = importNoExt + '/index.server.js';
739
+ } else {
740
+ newImportPath = importNoExt + '.server.js';
741
+ }
742
+
743
+ // Set the node's source value and ensure it renders with single quotes
744
+ node.source.value = newImportPath;
745
+ if (!node.source.extra || typeof node.source.extra !== 'object') {
746
+ node.source.extra = {};
747
+ }
748
+ node.source.extra.raw = `'${newImportPath}'`;
749
+ node.source.extra.rawValue = newImportPath;
750
+ hasChanges = true;
751
+ }
752
+ }
753
+ }
754
+
755
+ if (hasChanges) {
756
+ return generate(ast, { compact: false, comments: true }).code;
757
+ }
758
+
759
+ return content;
760
+ } catch (error) {
761
+ // If parsing fails, return original content
762
+ logger.warn(`Failed to update imports in server file: ${error.message}`);
763
+ return content;
764
+ }
765
765
  }
766
766
 
767
767
  /**
768
768
  * Checks if a file should be written by comparing content with existing file
769
769
  */
770
770
  function shouldWriteFile(filePath, newContent) {
771
- if (!fs.existsSync(filePath)) {
772
- return true;
773
- }
771
+ if (!fs.existsSync(filePath)) {
772
+ return true;
773
+ }
774
774
 
775
- const existingContent = fs.readFileSync(filePath, 'utf-8');
776
- return existingContent !== newContent;
775
+ const existingContent = fs.readFileSync(filePath, 'utf-8');
776
+ return existingContent !== newContent;
777
777
  }
778
778
 
779
779
  // Run the sanitizer if this script is executed directly
780
780
  if (import.meta.url === `file://${process.argv[1]}`) {
781
- sanitize().catch(console.error);
781
+ sanitize().catch(console.error);
782
782
  }