@mmapp/react-compiler 0.1.0-alpha.1 → 0.1.0-alpha.4

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 (346) hide show
  1. package/ATOM-PIPELINE.md +144 -0
  2. package/README.md +88 -40
  3. package/dist/auth-3UK75242.mjs +17 -0
  4. package/dist/babel/index.d.mts +2 -2
  5. package/dist/babel/index.d.ts +2 -2
  6. package/dist/babel/index.js +2816 -279
  7. package/dist/babel/index.mjs +2 -2
  8. package/dist/chunk-3USIFFE4.mjs +2190 -0
  9. package/dist/chunk-45YMGEVT.mjs +186 -0
  10. package/dist/chunk-4FN2AISW.mjs +148 -0
  11. package/dist/chunk-4OPI5L7G.mjs +2593 -0
  12. package/dist/chunk-4RYTKOOJ.mjs +186 -0
  13. package/dist/chunk-52XHYD2V.mjs +214 -0
  14. package/dist/chunk-5FTDWKHH.mjs +244 -0
  15. package/dist/chunk-5GUFFFGL.mjs +148 -0
  16. package/dist/chunk-5RKTOVR5.mjs +244 -0
  17. package/dist/chunk-5YDMOO4X.mjs +214 -0
  18. package/dist/chunk-64ZWEMLJ.mjs +148 -0
  19. package/dist/chunk-6XP4KSWQ.mjs +2190 -0
  20. package/dist/chunk-72QWL54I.mjs +175 -0
  21. package/dist/chunk-7B4TRI7C.mjs +4835 -0
  22. package/dist/chunk-7JRAEFRB.mjs +7510 -0
  23. package/dist/chunk-7T6Q5KAA.mjs +7506 -0
  24. package/dist/chunk-7ZKGHTNB.mjs +4952 -0
  25. package/dist/chunk-ABYPKRSB.mjs +215 -0
  26. package/dist/chunk-BZEXUPDH.mjs +175 -0
  27. package/dist/chunk-CIESM3BP.mjs +33 -0
  28. package/dist/chunk-DE3ZGQAC.mjs +148 -0
  29. package/dist/chunk-DMCY3BBG.mjs +1933 -0
  30. package/dist/chunk-DPIK3PJS.mjs +244 -0
  31. package/dist/chunk-E5IVH4RE.mjs +186 -0
  32. package/dist/chunk-E6FZNUR5.mjs +4953 -0
  33. package/dist/chunk-EJRBDQDP.mjs +2607 -0
  34. package/dist/chunk-ELO4TXJL.mjs +186 -0
  35. package/dist/chunk-EO6SYNCG.mjs +175 -0
  36. package/dist/chunk-FKRO52XH.mjs +3446 -0
  37. package/dist/chunk-FL4YAKU6.mjs +4941 -0
  38. package/dist/chunk-FYT47UBU.mjs +5076 -0
  39. package/dist/chunk-GCLGPOJZ.mjs +148 -0
  40. package/dist/chunk-GXB4JOP7.mjs +5072 -0
  41. package/dist/chunk-HFXOUMTD.mjs +175 -0
  42. package/dist/chunk-HRYR54PT.mjs +175 -0
  43. package/dist/chunk-HWIZ47US.mjs +214 -0
  44. package/dist/chunk-IB7MNPQL.mjs +4953 -0
  45. package/dist/chunk-ICSIHQCG.mjs +148 -0
  46. package/dist/chunk-J3M4GUS7.mjs +161 -0
  47. package/dist/chunk-J7JUAHS4.mjs +186 -0
  48. package/dist/chunk-JLA5VNQ3.mjs +186 -0
  49. package/dist/chunk-JQLWFCTM.mjs +214 -0
  50. package/dist/chunk-JRGFBWTN.mjs +2918 -0
  51. package/dist/chunk-KFJJCQAL.mjs +148 -0
  52. package/dist/chunk-KJUIIEQE.mjs +186 -0
  53. package/dist/chunk-KNWTHRVQ.mjs +175 -0
  54. package/dist/chunk-KSG4XSZF.mjs +175 -0
  55. package/dist/chunk-LF5N6DOU.mjs +175 -0
  56. package/dist/chunk-LJQCM2IM.mjs +214 -0
  57. package/dist/chunk-NTB7OEX2.mjs +2918 -0
  58. package/dist/chunk-NW6555WJ.mjs +186 -0
  59. package/dist/chunk-O4AUS7EU.mjs +148 -0
  60. package/dist/chunk-OMZE6VLQ.mjs +214 -0
  61. package/dist/chunk-OPJKP747.mjs +7506 -0
  62. package/dist/chunk-P4BR7WVO.mjs +2190 -0
  63. package/dist/chunk-QQHVYH2X.mjs +244 -0
  64. package/dist/chunk-R2DD5GTY.mjs +186 -0
  65. package/dist/chunk-S5QLWLLT.mjs +186 -0
  66. package/dist/chunk-SCWGT2FY.mjs +2190 -0
  67. package/dist/chunk-SMKJUSB3.mjs +2190 -0
  68. package/dist/chunk-THFYE5ZX.mjs +244 -0
  69. package/dist/chunk-UDDTWG5J.mjs +734 -0
  70. package/dist/chunk-VCAY2KGM.mjs +175 -0
  71. package/dist/chunk-VLTKQDJ3.mjs +244 -0
  72. package/dist/chunk-WBYMW4NQ.mjs +3450 -0
  73. package/dist/chunk-WECAV6QB.mjs +148 -0
  74. package/dist/chunk-WMKBXUCE.mjs +3228 -0
  75. package/dist/chunk-WVYY32LD.mjs +939 -0
  76. package/dist/chunk-XAJ5BKKL.mjs +4947 -0
  77. package/dist/chunk-XDVM4YHX.mjs +3450 -0
  78. package/dist/chunk-XG2X7AEA.mjs +175 -0
  79. package/dist/chunk-XG7Z23NQ.mjs +148 -0
  80. package/dist/chunk-XWZAOCQ7.mjs +2607 -0
  81. package/dist/chunk-Y6MA7ULW.mjs +148 -0
  82. package/dist/chunk-YMS7Q7LG.mjs +214 -0
  83. package/dist/chunk-Z2G5RZ4H.mjs +186 -0
  84. package/dist/chunk-ZA37XTGA.mjs +175 -0
  85. package/dist/chunk-ZE3KCHBM.mjs +2918 -0
  86. package/dist/cli/index.js +14720 -7199
  87. package/dist/cli/index.mjs +224 -183
  88. package/dist/codemod/cli.js +1 -1
  89. package/dist/codemod/cli.mjs +2 -2
  90. package/dist/codemod/index.d.mts +3 -3
  91. package/dist/codemod/index.d.ts +3 -3
  92. package/dist/codemod/index.js +1 -1
  93. package/dist/codemod/index.mjs +2 -2
  94. package/dist/config-PL24KEWL.mjs +219 -0
  95. package/dist/deploy-YAJGW6II.mjs +9 -0
  96. package/dist/dev-server-CrQ041KP.d.mts +79 -0
  97. package/dist/dev-server-CrQ041KP.d.ts +79 -0
  98. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  99. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  100. package/dist/dev-server.d.mts +2 -2
  101. package/dist/dev-server.d.ts +2 -2
  102. package/dist/dev-server.js +6424 -1597
  103. package/dist/dev-server.mjs +5 -5
  104. package/dist/envelope-ChEkuHij.d.mts +265 -0
  105. package/dist/envelope-ChEkuHij.d.ts +265 -0
  106. package/dist/envelope.d.mts +2 -2
  107. package/dist/envelope.d.ts +2 -2
  108. package/dist/envelope.js +2814 -277
  109. package/dist/envelope.mjs +3 -3
  110. package/dist/index-CEKyyazf.d.mts +104 -0
  111. package/dist/index-CEKyyazf.d.ts +104 -0
  112. package/dist/index.d.mts +168 -9
  113. package/dist/index.d.ts +168 -9
  114. package/dist/index.js +5606 -681
  115. package/dist/index.mjs +217 -9
  116. package/dist/init-7FJENUDK.mjs +407 -0
  117. package/{src/cli/init.ts → dist/init-7JQMAAXS.mjs} +70 -95
  118. package/dist/init-DQDX3QK6.mjs +369 -0
  119. package/dist/init-EHO4VQ22.mjs +369 -0
  120. package/dist/init-UC3FWPIW.mjs +367 -0
  121. package/dist/init-UNSMVKIK.mjs +366 -0
  122. package/dist/init-UNV5XIDE.mjs +367 -0
  123. package/dist/project-compiler-2P4N4DR7.mjs +10 -0
  124. package/dist/project-compiler-D2LCC27O.mjs +10 -0
  125. package/dist/project-compiler-EJ3GANJE.mjs +10 -0
  126. package/dist/project-compiler-LOQKVRZJ.mjs +10 -0
  127. package/dist/project-compiler-NNK32MPG.mjs +10 -0
  128. package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
  129. package/dist/project-compiler-RQ6OQKRM.mjs +10 -0
  130. package/dist/project-compiler-VWNNCHGO.mjs +10 -0
  131. package/dist/project-compiler-XVAAU4C5.mjs +10 -0
  132. package/dist/project-compiler-YES5FGMD.mjs +10 -0
  133. package/dist/project-compiler-ZB4RUYVL.mjs +10 -0
  134. package/dist/project-compiler-ZKMQDLGU.mjs +10 -0
  135. package/dist/project-decompiler-FLXCEJHS.mjs +7 -0
  136. package/dist/project-decompiler-U55HQUHW.mjs +7 -0
  137. package/dist/project-decompiler-US7GAVIC.mjs +7 -0
  138. package/dist/project-decompiler-VLPR22QF.mjs +7 -0
  139. package/dist/pull-FUS5QYZS.mjs +109 -0
  140. package/dist/pull-KOL2QAYQ.mjs +109 -0
  141. package/dist/pull-LD5ENLGY.mjs +109 -0
  142. package/dist/pull-P44LDRWB.mjs +109 -0
  143. package/dist/seed-KOGEPGOJ.mjs +154 -0
  144. package/dist/server-VW6UPCHO.mjs +277 -0
  145. package/dist/testing/index.d.mts +8 -8
  146. package/dist/testing/index.d.ts +8 -8
  147. package/dist/testing/index.js +2824 -287
  148. package/dist/testing/index.mjs +2 -2
  149. package/dist/verify-BYHUKARQ.mjs +1833 -0
  150. package/dist/verify-OQDEQYMS.mjs +1833 -0
  151. package/dist/verify-SEIXUGN4.mjs +1833 -0
  152. package/dist/vite/index.d.mts +1 -1
  153. package/dist/vite/index.d.ts +1 -1
  154. package/dist/vite/index.js +2817 -280
  155. package/dist/vite/index.mjs +3 -3
  156. package/examples/authentication/main.workflow.tsx +1 -1
  157. package/examples/authentication/mm.config.ts +1 -1
  158. package/examples/authentication/pages/LoginPage.tsx +2 -2
  159. package/examples/authentication/pages/SignupPage.tsx +2 -2
  160. package/examples/counter.workflow.tsx +1 -1
  161. package/examples/dashboard.workflow.tsx +1 -1
  162. package/examples/invoice-approval/actions/invoice.server.ts +1 -1
  163. package/examples/invoice-approval/main.workflow.tsx +1 -1
  164. package/examples/invoice-approval/mm.config.ts +1 -1
  165. package/examples/invoice-approval/pages/InvoiceDetailPage.tsx +1 -1
  166. package/examples/invoice-approval/pages/InvoiceFormPage.tsx +1 -1
  167. package/examples/invoice-approval/pages/InvoiceListPage.tsx +1 -1
  168. package/examples/todo-app.workflow.tsx +1 -1
  169. package/examples/uber-app/actions/matching.server.ts +1 -1
  170. package/examples/uber-app/actions/notifications.server.ts +1 -1
  171. package/examples/uber-app/actions/payments.server.ts +1 -1
  172. package/examples/uber-app/actions/pricing.server.ts +1 -1
  173. package/examples/uber-app/app/admin/analytics.tsx +2 -2
  174. package/examples/uber-app/app/admin/fleet.tsx +21 -21
  175. package/examples/uber-app/app/admin/surge-pricing.tsx +2 -2
  176. package/examples/uber-app/app/driver/dashboard.tsx +2 -2
  177. package/examples/uber-app/app/driver/earnings.tsx +2 -2
  178. package/examples/uber-app/app/driver/navigation.tsx +2 -2
  179. package/examples/uber-app/app/driver/ride-acceptance.tsx +2 -2
  180. package/examples/uber-app/app/rider/home.tsx +2 -2
  181. package/examples/uber-app/app/rider/payment-methods.tsx +2 -2
  182. package/examples/uber-app/app/rider/ride-history.tsx +2 -2
  183. package/examples/uber-app/app/rider/ride-tracking.tsx +2 -2
  184. package/examples/uber-app/components/DriverCard.tsx +1 -1
  185. package/examples/uber-app/components/MapView.tsx +3 -3
  186. package/examples/uber-app/components/RatingStars.tsx +2 -2
  187. package/examples/uber-app/components/RideCard.tsx +1 -1
  188. package/examples/uber-app/mm.config.ts +1 -1
  189. package/examples/uber-app/workflows/dispute-resolution.workflow.tsx +2 -2
  190. package/examples/uber-app/workflows/driver-onboarding.workflow.tsx +2 -2
  191. package/examples/uber-app/workflows/payment-processing.workflow.tsx +2 -2
  192. package/examples/uber-app/workflows/ride-request.workflow.tsx +2 -2
  193. package/package.json +10 -4
  194. package/compile-blueprint-chat.mjs +0 -99
  195. package/compile-blueprint-glass-console.mjs +0 -98
  196. package/compile-chat-defs.mjs +0 -92
  197. package/examples/uber-app/tests/payment.test.tsx +0 -129
  198. package/examples/uber-app/tests/ride-flow.test.tsx +0 -123
  199. package/package.json.backup +0 -86
  200. package/scripts/decompile.ts +0 -226
  201. package/scripts/seed-auth.ts +0 -267
  202. package/scripts/seed-uber.ts +0 -248
  203. package/scripts/validate-uber.ts +0 -119
  204. package/seed-blueprint-chat.mjs +0 -444
  205. package/seed-blueprint-glass-console.mjs +0 -445
  206. package/seed-compiled.mjs +0 -318
  207. package/src/RoundTripValidator.ts +0 -400
  208. package/src/__tests__/atom-rendering-coverage.test.ts +0 -680
  209. package/src/__tests__/auth-module-compilation.test.ts +0 -247
  210. package/src/__tests__/auth-template-compilation.test.ts +0 -589
  211. package/src/__tests__/change-extractor.test.ts +0 -142
  212. package/src/__tests__/cli-pull.test.ts +0 -73
  213. package/src/__tests__/cli-test.test.ts +0 -72
  214. package/src/__tests__/component-extractor.test.ts +0 -331
  215. package/src/__tests__/context-extractor.test.ts +0 -145
  216. package/src/__tests__/decompiler.test.ts +0 -718
  217. package/src/__tests__/define-blueprint.test.ts +0 -133
  218. package/src/__tests__/definition-validator.test.ts +0 -519
  219. package/src/__tests__/during-extractor.test.ts +0 -152
  220. package/src/__tests__/effect-extractor.test.ts +0 -107
  221. package/src/__tests__/event-emission.test.ts +0 -127
  222. package/src/__tests__/examples.test.ts +0 -236
  223. package/src/__tests__/full-blueprint-coverage.test.ts +0 -1221
  224. package/src/__tests__/golden-suite.test.ts +0 -403
  225. package/src/__tests__/grammar-island-extractor.test.ts +0 -289
  226. package/src/__tests__/instance-key.test.ts +0 -82
  227. package/src/__tests__/ir-migration.test.ts +0 -255
  228. package/src/__tests__/lock-file.test.ts +0 -117
  229. package/src/__tests__/model-extractor.test.ts +0 -195
  230. package/src/__tests__/model-field-acl.test.ts +0 -237
  231. package/src/__tests__/model-hooks.test.ts +0 -130
  232. package/src/__tests__/model-ref-resolution.test.ts +0 -268
  233. package/src/__tests__/model-roundtrip.test.ts +0 -502
  234. package/src/__tests__/model-runtime.test.ts +0 -112
  235. package/src/__tests__/model-transitions.test.ts +0 -183
  236. package/src/__tests__/nrt-action-trace.test.ts +0 -391
  237. package/src/__tests__/pipeline-hardening.test.ts +0 -413
  238. package/src/__tests__/project-compiler.test.ts +0 -546
  239. package/src/__tests__/project-decompiler.test.ts +0 -343
  240. package/src/__tests__/query-compilation.test.ts +0 -145
  241. package/src/__tests__/round-trip/PLAN.md +0 -158
  242. package/src/__tests__/round-trip/README.md +0 -52
  243. package/src/__tests__/round-trip/RESULTS.md +0 -86
  244. package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +0 -55
  245. package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +0 -11
  246. package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +0 -54
  247. package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +0 -79
  248. package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +0 -12
  249. package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +0 -50
  250. package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +0 -25
  251. package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +0 -11
  252. package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +0 -32
  253. package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +0 -79
  254. package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +0 -10
  255. package/src/__tests__/round-trip/round-trip.test.ts +0 -2598
  256. package/src/__tests__/round-trip-ir.test.ts +0 -300
  257. package/src/__tests__/round-trip.test.ts +0 -1212
  258. package/src/__tests__/route-merging.test.ts +0 -372
  259. package/src/__tests__/router-composition.test.ts +0 -489
  260. package/src/__tests__/router-extractor.test.ts +0 -176
  261. package/src/__tests__/server-action-extractor.test.ts +0 -128
  262. package/src/__tests__/smart-type-inference.test.ts +0 -365
  263. package/src/__tests__/source-envelope.test.ts +0 -284
  264. package/src/__tests__/source-fidelity.test.ts +0 -516
  265. package/src/__tests__/state-extractor.test.ts +0 -115
  266. package/src/__tests__/strict-mode.test.ts +0 -227
  267. package/src/__tests__/transition-effect-extractor.test.ts +0 -119
  268. package/src/__tests__/transition-extractor.test.ts +0 -68
  269. package/src/__tests__/ts-to-expression.test.ts +0 -462
  270. package/src/__tests__/type-generator.test.ts +0 -201
  271. package/src/__tests__/uber-validation.test.ts +0 -502
  272. package/src/action-compiler.ts +0 -361
  273. package/src/babel/emitters/experience-transform.ts +0 -199
  274. package/src/babel/emitters/ir-to-tsx-emitter.ts +0 -110
  275. package/src/babel/emitters/pure-form-emitter.ts +0 -1023
  276. package/src/babel/emitters/runtime-glue-emitter.ts +0 -39
  277. package/src/babel/extractors/change-extractor.ts +0 -199
  278. package/src/babel/extractors/component-extractor.ts +0 -907
  279. package/src/babel/extractors/computed-extractor.ts +0 -262
  280. package/src/babel/extractors/context-extractor.ts +0 -277
  281. package/src/babel/extractors/during-extractor.ts +0 -295
  282. package/src/babel/extractors/effect-extractor.ts +0 -340
  283. package/src/babel/extractors/event-extractor.ts +0 -235
  284. package/src/babel/extractors/grammar-island-extractor.ts +0 -302
  285. package/src/babel/extractors/model-extractor.ts +0 -1018
  286. package/src/babel/extractors/router-extractor.ts +0 -303
  287. package/src/babel/extractors/server-action-extractor.ts +0 -173
  288. package/src/babel/extractors/server-action-hook-extractor.ts +0 -72
  289. package/src/babel/extractors/server-state-extractor.ts +0 -88
  290. package/src/babel/extractors/state-extractor.ts +0 -214
  291. package/src/babel/extractors/transition-effect-extractor.ts +0 -176
  292. package/src/babel/extractors/transition-extractor.ts +0 -143
  293. package/src/babel/index.ts +0 -24
  294. package/src/babel/transpilers/ts-to-expression.ts +0 -674
  295. package/src/babel/visitor.ts +0 -807
  296. package/src/cli/auth.ts +0 -255
  297. package/src/cli/build.ts +0 -288
  298. package/src/cli/deploy.ts +0 -206
  299. package/src/cli/index.ts +0 -328
  300. package/src/cli/installer.ts +0 -261
  301. package/src/cli/lock-file.ts +0 -94
  302. package/src/cli/mmrc.ts +0 -22
  303. package/src/cli/pull.ts +0 -172
  304. package/src/cli/registry-client.ts +0 -175
  305. package/src/cli/test.ts +0 -397
  306. package/src/cli/type-generator.ts +0 -243
  307. package/src/codemod/__tests__/forward.test.ts +0 -239
  308. package/src/codemod/__tests__/reverse.test.ts +0 -145
  309. package/src/codemod/__tests__/round-trip.test.ts +0 -137
  310. package/src/codemod/annotation.ts +0 -97
  311. package/src/codemod/classify.ts +0 -197
  312. package/src/codemod/cli.ts +0 -207
  313. package/src/codemod/control-flow.ts +0 -409
  314. package/src/codemod/forward.ts +0 -244
  315. package/src/codemod/import-manager.ts +0 -171
  316. package/src/codemod/index.ts +0 -120
  317. package/src/codemod/reverse.ts +0 -197
  318. package/src/codemod/rules.ts +0 -174
  319. package/src/codemod/state-transform.ts +0 -126
  320. package/src/decompiler/ast-builder.ts +0 -538
  321. package/src/decompiler/config-generator.ts +0 -151
  322. package/src/decompiler/index.ts +0 -315
  323. package/src/decompiler/project-decompiler.ts +0 -1776
  324. package/src/decompiler/project.ts +0 -862
  325. package/src/decompiler/split-strategy.ts +0 -140
  326. package/src/decompiler/state-emitter.ts +0 -1053
  327. package/src/decompiler/sx-emitter.ts +0 -318
  328. package/src/decompiler/workspace-hydrator.ts +0 -189
  329. package/src/dev-server.ts +0 -238
  330. package/src/envelope/fs-tree.ts +0 -217
  331. package/src/envelope/source-envelope.ts +0 -264
  332. package/src/envelope.ts +0 -315
  333. package/src/incremental-compiler.ts +0 -401
  334. package/src/index.ts +0 -99
  335. package/src/model-compiler.ts +0 -277
  336. package/src/project-compiler.ts +0 -1629
  337. package/src/route-extractor.ts +0 -333
  338. package/src/testing/index.ts +0 -32
  339. package/src/testing/snapshot.ts +0 -252
  340. package/src/testing/test-utils.ts +0 -226
  341. package/src/types.ts +0 -68
  342. package/src/vite/index.ts +0 -288
  343. package/test-compile.mjs +0 -142
  344. package/tsconfig.json +0 -25
  345. package/tsup.config.ts +0 -23
  346. package/vitest.config.ts +0 -9
@@ -1,807 +0,0 @@
1
- /**
2
- * Babel Visitor — main AST traversal logic for extracting workflow definitions.
3
- *
4
- * Handles all @mindmatrix/react hooks:
5
- * - useState → fields
6
- * - useOnEnter / useOnExit → state enter/exit actions
7
- * - useTransition → transitions
8
- * - useOnEvent → event subscriptions
9
- * - useWhileIn → during actions (interval while in state)
10
- * - useOnChange → field watchers
11
- * - useOnTransition → transition effects
12
- *
13
- * Supports strict/infer mode enforcement.
14
- */
15
-
16
- import type { Visitor } from '@babel/traverse';
17
- import * as t from '@babel/types';
18
- import type { CompilerState, CompilerOptions, ReactCompilerError } from '../types';
19
- import { extractStates } from './extractors/state-extractor';
20
- import { extractEffects } from './extractors/effect-extractor';
21
- import { extractTransitions } from './extractors/transition-extractor';
22
- import { extractEvents } from './extractors/event-extractor';
23
- import { extractComponents, resetNodeIdCounter, registerDerivedVar } from './extractors/component-extractor';
24
- import { extractDuring, resetDuringIdCounter } from './extractors/during-extractor';
25
- import { extractChangeWatcher, resetWatcherIdCounter } from './extractors/change-extractor';
26
- import { extractComputed } from './extractors/computed-extractor';
27
- import { extractTransitionEffect, resetTransitionEffectIdCounter } from './extractors/transition-effect-extractor';
28
- import { isModelFile, extractModelFile } from './extractors/model-extractor';
29
- import { isServerActionFile, extractServerActions } from './extractors/server-action-extractor';
30
- import { extractServerActionHook } from './extractors/server-action-hook-extractor';
31
- import { extractServerState } from './extractors/server-state-extractor';
32
- import { extractGrammarIslands } from './extractors/grammar-island-extractor';
33
- import { hasContextCreation, extractContextWorkflows } from './extractors/context-extractor';
34
- import { emitIR, emitCanonical, emitWorkflowDefinition, compilerStateToWorkflow } from './emitters/pure-form-emitter';
35
- import type { IRDataSource, IRWorkflowDataSource } from '@mindmatrix/player-core';
36
- import type { NodePath } from '@babel/traverse';
37
-
38
- /**
39
- * Extracts useQuery() calls into IRDataSource declarations.
40
- * useQuery('channel', { state: 'active' }) → IRDataSource with type 'workflow'
41
- */
42
- /**
43
- * Resolves a useQuery/useMutation slug argument.
44
- * Supports string literals ('channel') and identifier references to imported defineModel results.
45
- *
46
- * When the argument is an identifier (e.g. `useQuery(ChannelModel)`), we:
47
- * 1. Look up the import source in __modelImports
48
- * 2. Derive the slug from the import path (e.g. '../models/channel' → 'channel')
49
- * 3. Also check __modelImportSlugs for slugs resolved from actual file contents
50
- */
51
- function resolveSlugArg(args: t.Node[], state: any): string | null {
52
- if (args.length < 1) return null;
53
- const slugArg = args[0];
54
-
55
- // Direct string literal: useQuery('channel')
56
- if (t.isStringLiteral(slugArg)) return slugArg.value;
57
-
58
- // Identifier reference: useQuery(ChannelModel)
59
- // Resolve via tracked model imports
60
- if (t.isIdentifier(slugArg)) {
61
- const compilerState = state as CompilerState;
62
- const meta = compilerState.metadata as Record<string, unknown>;
63
-
64
- // Check pre-resolved slugs first (from file content analysis)
65
- const resolvedSlugs = meta.__modelImportSlugs as Record<string, string> | undefined;
66
- if (resolvedSlugs?.[slugArg.name]) return resolvedSlugs[slugArg.name];
67
-
68
- // Fall back to slug derived from import path
69
- const importSources = meta.__modelImports as Record<string, string> | undefined;
70
- if (importSources?.[slugArg.name]) {
71
- const importPath = importSources[slugArg.name];
72
- // Derive slug from the import path's filename
73
- // e.g. '../models/channel' → 'channel'
74
- // e.g. '../models/user-profile' → 'user-profile'
75
- // e.g. './models/OrderItem' → 'order-item'
76
- const filename = importPath.split('/').pop() || '';
77
- return filename
78
- .replace(/\.(ts|tsx|js|jsx)$/, '')
79
- .replace(/\.model$/, '')
80
- .replace(/([A-Z])/g, '-$1')
81
- .toLowerCase()
82
- .replace(/^-/, '');
83
- }
84
- }
85
-
86
- return null;
87
- }
88
-
89
- function extractQueryDataSource(path: NodePath<t.CallExpression>, state: any): void {
90
- const args = path.node.arguments;
91
- if (args.length < 1) return;
92
-
93
- const slug = resolveSlugArg(args, state);
94
- if (!slug) return;
95
- const compilerState = state as CompilerState;
96
-
97
- // Build data source from useQuery params
98
- const dataSource: IRWorkflowDataSource = {
99
- type: 'workflow',
100
- name: slug,
101
- slug,
102
- query: 'list',
103
- };
104
-
105
- // Extract options if provided — maps QueryParams → IRWorkflowDataSource fields
106
- if (args.length > 1 && t.isObjectExpression(args[1])) {
107
- for (const prop of args[1].properties) {
108
- if (!t.isObjectProperty(prop) || !t.isIdentifier(prop.key)) continue;
109
- const key = prop.key.name;
110
- const val = prop.value;
111
-
112
- switch (key) {
113
- case 'limit':
114
- if (t.isNumericLiteral(val)) {
115
- dataSource.pageSize = val.value;
116
- dataSource.paginated = true;
117
- }
118
- break;
119
- case 'orderBy':
120
- if (t.isStringLiteral(val)) {
121
- dataSource.sort = val.value;
122
- }
123
- break;
124
- case 'order':
125
- if (t.isStringLiteral(val) && dataSource.sort) {
126
- dataSource.sort = `${dataSource.sort}:${val.value}`;
127
- }
128
- break;
129
- case 'search':
130
- if (t.isStringLiteral(val)) {
131
- dataSource.search = val.value;
132
- }
133
- break;
134
- case 'searchFields':
135
- if (t.isArrayExpression(val)) {
136
- dataSource.searchFields = val.elements
137
- .filter((el): el is t.StringLiteral => t.isStringLiteral(el))
138
- .map(el => el.value);
139
- }
140
- break;
141
- case 'filter':
142
- if (t.isObjectExpression(val)) {
143
- const filter: Record<string, string> = {};
144
- for (const fp of val.properties) {
145
- if (t.isObjectProperty(fp) && t.isIdentifier(fp.key) && t.isStringLiteral(fp.value)) {
146
- filter[fp.key.name] = fp.value.value;
147
- }
148
- }
149
- if (Object.keys(filter).length > 0) {
150
- dataSource.filter = filter;
151
- }
152
- }
153
- break;
154
- case 'state':
155
- // Shorthand: filter by workflow state
156
- if (t.isStringLiteral(val)) {
157
- if (!dataSource.filter) dataSource.filter = {};
158
- dataSource.filter.current_state = val.value;
159
- }
160
- break;
161
- case 'groupBy':
162
- if (t.isStringLiteral(val)) {
163
- dataSource.groupBy = val.value;
164
- }
165
- break;
166
- case 'facets':
167
- if (t.isArrayExpression(val)) {
168
- dataSource.facets = val.elements
169
- .filter((el): el is t.StringLiteral => t.isStringLiteral(el))
170
- .map(el => el.value);
171
- }
172
- break;
173
- }
174
- }
175
- }
176
-
177
- // Store data sources on the metadata for later emission
178
- if (!compilerState.metadata) compilerState.metadata = {};
179
- const meta = compilerState.metadata as Record<string, unknown>;
180
- if (!meta.dataSources) meta.dataSources = [];
181
- (meta.dataSources as IRDataSource[]).push(dataSource);
182
- }
183
-
184
- /**
185
- * Extracts useMutation() calls into mutation action stubs.
186
- * useMutation('channel') → records the slug for transition/create capability
187
- */
188
- function extractMutationDataSource(path: NodePath<t.CallExpression>, state: any): void {
189
- const args = path.node.arguments;
190
- if (args.length < 1) return;
191
-
192
- const slug = resolveSlugArg(args, state);
193
- if (!slug) return;
194
- const compilerState = state as CompilerState;
195
-
196
- // Store mutation targets on metadata
197
- if (!compilerState.metadata) compilerState.metadata = {};
198
- const meta = compilerState.metadata as Record<string, unknown>;
199
- if (!meta.mutationTargets) meta.mutationTargets = [];
200
- (meta.mutationTargets as string[]).push(slug);
201
- }
202
-
203
- /**
204
- * Extracts useDuringAction({ state, action, intervalMs }) into during actions.
205
- * Alternate API for useWhileIn — config object instead of positional args.
206
- */
207
- function extractDuringAction(path: NodePath<t.CallExpression>, state: any): void {
208
- const args = path.node.arguments;
209
- if (args.length < 1 || !t.isObjectExpression(args[0])) return;
210
-
211
- const compilerState = state as CompilerState;
212
- const config = args[0];
213
-
214
- let stateName: string | undefined;
215
- let intervalMs = 1000;
216
-
217
- for (const prop of config.properties) {
218
- if (!t.isObjectProperty(prop) || !t.isIdentifier(prop.key)) continue;
219
- if (prop.key.name === 'state' && t.isStringLiteral(prop.value)) {
220
- stateName = prop.value.value;
221
- }
222
- if (prop.key.name === 'intervalMs' && t.isNumericLiteral(prop.value)) {
223
- intervalMs = prop.value.value;
224
- }
225
- }
226
-
227
- if (!stateName) return;
228
-
229
- // Ensure state exists
230
- if (!compilerState.states.has(stateName)) {
231
- compilerState.states.set(stateName, {
232
- name: stateName,
233
- type: 'REGULAR',
234
- on_enter: [],
235
- during: [],
236
- on_exit: [],
237
- });
238
- }
239
-
240
- const stateEntry = compilerState.states.get(stateName)!;
241
- if (!stateEntry.during) stateEntry.during = [];
242
-
243
- compilerState.actionCounter++;
244
- stateEntry.during.push({
245
- id: `during_${compilerState.actionCounter}`,
246
- type: 'interval',
247
- interval_ms: intervalMs,
248
- actions: [],
249
- });
250
- }
251
-
252
- /**
253
- * Extracts useRole('roleName') into metadata.roleDependencies.
254
- */
255
- function extractRoleDependency(path: NodePath<t.CallExpression>, state: any): void {
256
- const args = path.node.arguments;
257
- if (args.length < 1 || !t.isStringLiteral(args[0])) return;
258
-
259
- const compilerState = state as CompilerState;
260
- if (!compilerState.metadata) compilerState.metadata = {};
261
- const meta = compilerState.metadata as Record<string, unknown>;
262
- if (!meta.roleDependencies) meta.roleDependencies = [];
263
- const roles = meta.roleDependencies as string[];
264
- const role = args[0].value;
265
- if (!roles.includes(role)) roles.push(role);
266
- }
267
-
268
- /**
269
- * Extracts useView('slug') into metadata.viewDependencies.
270
- */
271
- function extractViewDependency(path: NodePath<t.CallExpression>, state: any): void {
272
- const args = path.node.arguments;
273
- if (args.length < 1 || !t.isStringLiteral(args[0])) return;
274
-
275
- const compilerState = state as CompilerState;
276
- if (!compilerState.metadata) compilerState.metadata = {};
277
- const meta = compilerState.metadata as Record<string, unknown>;
278
- if (!meta.viewDependencies) meta.viewDependencies = [];
279
- const views = meta.viewDependencies as string[];
280
- const slug = args[0].value;
281
- if (!views.includes(slug)) views.push(slug);
282
- }
283
-
284
- /**
285
- * Extracts useParams() into metadata.acceptsParams = true.
286
- */
287
- function extractParamsUsage(_path: NodePath<t.CallExpression>, state: any): void {
288
- const compilerState = state as CompilerState;
289
- if (!compilerState.metadata) compilerState.metadata = {};
290
- const meta = compilerState.metadata as Record<string, unknown>;
291
- meta.acceptsParams = true;
292
- }
293
-
294
- /**
295
- * Extracts useExpressionLibrary('slug') into metadata.libraryDependencies.
296
- */
297
- function extractLibraryDependency(path: NodePath<t.CallExpression>, state: any): void {
298
- const args = path.node.arguments;
299
- if (args.length < 1 || !t.isStringLiteral(args[0])) return;
300
-
301
- const compilerState = state as CompilerState;
302
- if (!compilerState.metadata) compilerState.metadata = {};
303
- const meta = compilerState.metadata as Record<string, unknown>;
304
- if (!meta.libraryDependencies) meta.libraryDependencies = [];
305
- const libs = meta.libraryDependencies as string[];
306
- const slug = args[0].value;
307
- if (!libs.includes(slug)) libs.push(slug);
308
- }
309
-
310
- /** Hooks banned in strict mode */
311
- const STRICT_BANNED_HOOKS: Record<string, string> = {
312
- useEffect: 'STRICT_USE_EFFECT',
313
- useLayoutEffect: 'STRICT_USE_LAYOUT_EFFECT',
314
- useRef: 'STRICT_USE_REF',
315
- useMemo: 'STRICT_USE_MEMO',
316
- useCallback: 'STRICT_USE_CALLBACK',
317
- };
318
-
319
- /**
320
- * Extracts metadata from JSDoc comments.
321
- * Shared between Program.enter and FunctionDeclaration.
322
- */
323
- function extractMetadataFromComments(comments: any[], metadata: CompilerState['metadata']): void {
324
- for (const comment of comments) {
325
- if (comment.type !== 'CommentBlock') continue;
326
- const lines = comment.value.split('\n');
327
- for (const line of lines) {
328
- // Match @workflow with inline key=value pairs:
329
- // @workflow slug="mm-chat-app" version="1.0.0" category="blueprint"
330
- const workflowMatch = line.match(/@workflow\s+(.+)/);
331
- if (workflowMatch) {
332
- const rest = workflowMatch[1].trim();
333
- const kvRegex = /(\w+)="([^"]+)"/g;
334
- let kvMatch;
335
- let hasKV = false;
336
- while ((kvMatch = kvRegex.exec(rest)) !== null) {
337
- hasKV = true;
338
- const [, key, val] = kvMatch;
339
- if (key === 'slug') metadata.slug = val;
340
- else if (key === 'version') metadata.version = val;
341
- else if (key === 'category') metadata.category = val;
342
- else if (key === 'description') metadata.description = val;
343
- }
344
- // Fallback: if no key=value pairs, treat whole value as slug
345
- if (!hasKV) {
346
- metadata.slug = rest;
347
- }
348
- continue;
349
- }
350
- // Match standalone @version, @category, @description tags
351
- const match = line.match(/@(\w+)\s+(.+)/);
352
- if (match) {
353
- const [, tag, value] = match;
354
- if (tag === 'version') metadata.version = value.trim();
355
- if (tag === 'category') metadata.category = value.trim();
356
- if (tag === 'description') metadata.description = value.trim();
357
- }
358
- }
359
- }
360
- }
361
-
362
- /**
363
- * Creates the Babel visitor for the plugin.
364
- */
365
- export function createVisitor(options: CompilerOptions = {}): Visitor {
366
- const mode = options.mode;
367
-
368
- return {
369
- Program: {
370
- // Initialize compiler state at program entry
371
- enter(_path, state: any) {
372
- // Reset per-file counters to prevent ID leaks across compilations
373
- resetNodeIdCounter();
374
- resetDuringIdCounter();
375
- resetWatcherIdCounter();
376
- resetTransitionEffectIdCounter();
377
-
378
- const compilerState: CompilerState = {
379
- fields: [],
380
- states: new Map(),
381
- transitions: [],
382
- events: [],
383
- actionCounter: 0,
384
- metadata: {},
385
- errors: [],
386
- warnings: [],
387
- };
388
-
389
- // Extract metadata from JSDoc comments
390
- const program = _path.node;
391
- const leadingComments = program.leadingComments || [];
392
- extractMetadataFromComments(leadingComments, compilerState.metadata);
393
-
394
- // Also check leadingComments on first body statement (Babel attaches
395
- // top-of-file comments to the first statement, not the program node)
396
- if (program.body.length > 0) {
397
- const firstStmt = program.body[0];
398
- if (firstStmt.leadingComments) {
399
- extractMetadataFromComments(firstStmt.leadingComments, compilerState.metadata);
400
- }
401
- }
402
-
403
- // Extract component name from export and check for JSDoc on function
404
- const exportDeclaration = program.body.find((node) =>
405
- t.isExportNamedDeclaration(node) || t.isExportDefaultDeclaration(node)
406
- );
407
-
408
- if (exportDeclaration) {
409
- // Check the export statement's own leading comments (JSDoc may be on the export)
410
- if (exportDeclaration.leadingComments) {
411
- extractMetadataFromComments(exportDeclaration.leadingComments, compilerState.metadata);
412
- }
413
-
414
- if (t.isExportNamedDeclaration(exportDeclaration)) {
415
- const declaration = exportDeclaration.declaration;
416
- if (t.isFunctionDeclaration(declaration)) {
417
- if (declaration.id) {
418
- compilerState.metadata.name = declaration.id.name;
419
- }
420
- if (declaration.leadingComments) {
421
- extractMetadataFromComments(declaration.leadingComments, compilerState.metadata);
422
- }
423
- }
424
- } else if (t.isExportDefaultDeclaration(exportDeclaration)) {
425
- const declaration = exportDeclaration.declaration;
426
- if (t.isFunctionDeclaration(declaration)) {
427
- if (declaration.id) {
428
- compilerState.metadata.name = declaration.id.name;
429
- }
430
- if (declaration.leadingComments) {
431
- extractMetadataFromComments(declaration.leadingComments, compilerState.metadata);
432
- }
433
- } else if (t.isIdentifier(declaration)) {
434
- compilerState.metadata.name = declaration.name;
435
- }
436
- }
437
- }
438
-
439
- // Infer slug from file name if not set by JSDoc
440
- if (!compilerState.metadata.slug && state.filename) {
441
- const fileName = state.filename.split('/').pop() || '';
442
- compilerState.metadata.slug = fileName
443
- .replace(/\.workflow\.(tsx?|jsx?)$/, '')
444
- .replace(/\.(tsx?|jsx?)$/, '')
445
- .replace(/([A-Z])/g, '-$1')
446
- .toLowerCase()
447
- .replace(/^-/, '');
448
- // Track that this was auto-derived (model extractor may override)
449
- (compilerState.metadata as any).__slugAutoFallback = true;
450
- }
451
-
452
- // Store in state for later phases
453
- Object.assign(state, compilerState);
454
-
455
- // Model file detection — extract immediately since all declarations are top-level
456
- if (isModelFile(_path, state.filename)) {
457
- extractModelFile(_path, state);
458
- // Mark as model file so CallExpression/ReturnStatement visitors can skip
459
- (state as any).__isModelFile = true;
460
- // Model files default to category 'data' (creates a data workflow definition)
461
- if (!compilerState.metadata.category) {
462
- compilerState.metadata.category = 'data';
463
- }
464
- }
465
-
466
- // Grammar island extraction — detect tagged templates (cedar, sql, cron, etc.)
467
- // Works in both model files and regular workflow files
468
- extractGrammarIslands(_path, state);
469
-
470
- // Context workflow extraction — detect createContext() calls
471
- if (hasContextCreation(_path)) {
472
- extractContextWorkflows(_path, state);
473
- }
474
-
475
- // Server action file detection — extract exported functions
476
- if (isServerActionFile(state.filename)) {
477
- extractServerActions(_path, state);
478
- (state as any).__isServerActionFile = true;
479
- }
480
- },
481
-
482
- // After all extraction, emit Pure Form JSON
483
- exit(_path, state: any) {
484
- const compilerState = state as CompilerState;
485
-
486
- // Convert to ExtractedWorkflow and emit IR, engine definition, and canonical tree
487
- const extracted = compilerStateToWorkflow(compilerState, compilerState.metadata);
488
- const ir = emitIR(extracted);
489
- const definition = emitWorkflowDefinition(extracted);
490
- const canonical = emitCanonical(extracted, state.filename);
491
-
492
- // Attach errors/warnings to IR
493
- if (compilerState.errors && compilerState.errors.length > 0) {
494
- if (!ir.metadata) ir.metadata = {};
495
- (ir.metadata as any).errors = compilerState.errors;
496
- }
497
- if (compilerState.warnings && compilerState.warnings.length > 0) {
498
- if (!ir.metadata) ir.metadata = {};
499
- (ir.metadata as any).warnings = compilerState.warnings;
500
- }
501
-
502
- // Attach all outputs to file metadata for compile script to consume
503
- if (!state.file.metadata) state.file.metadata = {};
504
- state.file.metadata.mindmatrixIR = ir;
505
- state.file.metadata.mindmatrixDefinition = definition;
506
- state.file.metadata.mindmatrixCanonical = canonical;
507
- },
508
- },
509
-
510
- // Extract metadata from function declarations with JSDoc
511
- FunctionDeclaration(path, state: any) {
512
- const comments = path.node.leadingComments || [];
513
- const compilerState = state as CompilerState;
514
- extractMetadataFromComments(comments, compilerState.metadata);
515
- },
516
-
517
- // Main hook extraction dispatcher
518
- CallExpression(path, state) {
519
- const callee = path.node.callee;
520
- if (!t.isIdentifier(callee)) return;
521
-
522
- const compilerState = state as CompilerState;
523
- const hookName = callee.name;
524
-
525
- // Strict mode enforcement — ban forbidden hooks
526
- if (mode === 'strict' && STRICT_BANNED_HOOKS[hookName]) {
527
- const error: ReactCompilerError = {
528
- code: STRICT_BANNED_HOOKS[hookName] as any,
529
- message: `${hookName}() is not allowed in strict mode. Use @mindmatrix/react effect hooks instead.`,
530
- line: path.node.loc?.start.line,
531
- column: path.node.loc?.start.column,
532
- severity: 'error',
533
- };
534
- if (!compilerState.errors) compilerState.errors = [];
535
- compilerState.errors.push(error);
536
- return;
537
- }
538
-
539
- // Infer mode: warn about useEffect (component becomes opaque)
540
- if (mode === 'infer' && hookName === 'useEffect') {
541
- const warning: ReactCompilerError = {
542
- code: 'INFER_RAW_JSX',
543
- message: `useEffect() found in infer mode — component will be treated as an opaque [raw jsx] grammar island.`,
544
- line: path.node.loc?.start.line,
545
- column: path.node.loc?.start.column,
546
- severity: 'warning',
547
- };
548
- if (!compilerState.warnings) compilerState.warnings = [];
549
- compilerState.warnings.push(warning);
550
- return;
551
- }
552
-
553
- switch (hookName) {
554
- case 'useState':
555
- extractStates(path, state);
556
- break;
557
- case 'useOnEnter':
558
- case 'useOnExit':
559
- extractEffects(path, state);
560
- break;
561
- case 'useTransition':
562
- extractTransitions(path, state);
563
- break;
564
- case 'useOnEvent':
565
- extractEvents(path, state);
566
- break;
567
- case 'useWhileIn':
568
- extractDuring(path, state);
569
- break;
570
- case 'useOnChange':
571
- extractChangeWatcher(path, state);
572
- break;
573
- case 'useComputed':
574
- extractComputed(path, state);
575
- break;
576
- case 'useOnTransition':
577
- extractTransitionEffect(path, state);
578
- break;
579
- case 'useQuery':
580
- extractQueryDataSource(path, state);
581
- break;
582
- case 'useMutation':
583
- extractMutationDataSource(path, state);
584
- break;
585
- case 'useDuringAction':
586
- // useDuringAction({ state, action, intervalMs }) → during action (alias for useWhileIn)
587
- extractDuringAction(path, state);
588
- break;
589
- case 'useRole':
590
- // useRole('admin') → records role dependency in metadata
591
- extractRoleDependency(path, state);
592
- break;
593
- case 'useView':
594
- // useView('my-view') → records view dependency in metadata
595
- extractViewDependency(path, state);
596
- break;
597
- case 'useParams':
598
- // useParams() → records that the workflow accepts invocation params
599
- extractParamsUsage(path, state);
600
- break;
601
- case 'useExpressionLibrary':
602
- // useExpressionLibrary('tax-calc') → records library dependency
603
- extractLibraryDependency(path, state);
604
- break;
605
- case 'useServerAction':
606
- // useServerAction('approve', { instanceId }) → records server action dependency
607
- extractServerActionHook(path, state);
608
- break;
609
- case 'useServerState':
610
- // useServerState(instanceId) → records SSE state subscription
611
- extractServerState(path, state);
612
- break;
613
- }
614
- },
615
-
616
- // Track imports: model imports for useQuery(modelRef) resolution + strict mode validation
617
- ImportDeclaration(path, state) {
618
- const compilerState = state as CompilerState;
619
- const source = path.node.source.value;
620
-
621
- // Track model imports: imports from paths containing /models/ or ending with .model
622
- // e.g. import ChannelModel from '../models/channel'
623
- // e.g. import { Channel } from './models/channel.model'
624
- if (source.match(/\/models\/|\.model(?:\.[tj]sx?)?$/)) {
625
- if (!compilerState.metadata) compilerState.metadata = {};
626
- const meta = compilerState.metadata as Record<string, unknown>;
627
- if (!meta.__modelImports) meta.__modelImports = {};
628
- const imports = meta.__modelImports as Record<string, string>;
629
-
630
- for (const specifier of path.node.specifiers) {
631
- if (t.isImportDefaultSpecifier(specifier) || t.isImportSpecifier(specifier)) {
632
- imports[specifier.local.name] = source;
633
- }
634
- }
635
- }
636
-
637
- // Strict mode: validate imports
638
- if (mode !== 'strict') return;
639
-
640
- // Allow @mindmatrix/* imports and react itself (for types)
641
- if (
642
- source.startsWith('@mindmatrix/') ||
643
- source === 'react' ||
644
- source.startsWith('react/') ||
645
- source.startsWith('.') ||
646
- source.startsWith('/')
647
- ) {
648
- return;
649
- }
650
-
651
- const error: ReactCompilerError = {
652
- code: 'STRICT_FORBIDDEN_IMPORT',
653
- message: `Import from '${source}' is not allowed in strict mode. Only @mindmatrix/* and relative imports are permitted.`,
654
- line: path.node.loc?.start.line,
655
- column: path.node.loc?.start.column,
656
- severity: 'error',
657
- };
658
- if (!compilerState.errors) compilerState.errors = [];
659
- compilerState.errors.push(error);
660
- },
661
-
662
- // Track derived/computed variable declarations for expression inlining.
663
- //
664
- // Three cases:
665
- // 1. `const hasContent = expr` → registerDerivedVar (inline the expression)
666
- // 2. `const users = useQuery(...)` → registerDerivedVar as $instance.{slug}
667
- // (data source results are available on $instance by their slug)
668
- // 3. `const sendMsg = useMutation(...)` → registerDerivedVar as $action.transition
669
- // (mutation results are action callbacks)
670
- // 4. `const memo = useMemo(() => expr, [])` → registerDerivedVar with body expr
671
- VariableDeclarator(path, state) {
672
- const compilerState = state as CompilerState;
673
- if (!compilerState.metadata) return;
674
-
675
- const id = path.node.id;
676
- const init = path.node.init;
677
-
678
- // Skip array destructuring (useState pattern: const [x, setX] = useState(...))
679
- if (t.isArrayPattern(id)) return;
680
- if (!t.isIdentifier(id) || !init || !t.isExpression(init)) return;
681
-
682
- // Only track declarations inside the exported component function body
683
- const parentFn = path.getFunctionParent();
684
- if (!parentFn) return;
685
- const parentNode = parentFn.parentPath;
686
- const isExportedDirectly =
687
- parentNode?.isExportDefaultDeclaration() ||
688
- parentNode?.isExportNamedDeclaration();
689
- let isExportedByName = false;
690
- if (!isExportedDirectly && parentNode?.isVariableDeclarator()) {
691
- const varId = parentNode.node.id;
692
- if (t.isIdentifier(varId)) {
693
- isExportedByName = varId.name === compilerState.metadata?.name;
694
- }
695
- }
696
- if (!isExportedDirectly && !isExportedByName) return;
697
-
698
- // Handle hook call results
699
- if (t.isCallExpression(init) && t.isIdentifier(init.callee)) {
700
- const callee = init.callee.name;
701
-
702
- // useQuery result → reference as $instance.{varName}
703
- // The data source is already extracted by extractQueryDataSource.
704
- // At runtime, the CTR makes query results available on $instance.
705
- if (callee === 'useQuery') {
706
- // Use optional member expression: $instance?.{varName}
707
- // Data sources may not be loaded yet on initial render.
708
- registerDerivedVar(id.name, t.optionalMemberExpression(
709
- t.identifier('$instance'),
710
- t.identifier(id.name),
711
- false, // computed
712
- true, // optional
713
- ));
714
- return;
715
- }
716
-
717
- // useMutation result → reference as $action.transition
718
- if (callee === 'useMutation') {
719
- registerDerivedVar(id.name, t.memberExpression(
720
- t.identifier('$action'),
721
- t.identifier('transition'),
722
- ));
723
- return;
724
- }
725
-
726
- // useServerAction result → reference as $action.serverAction
727
- if (callee === 'useServerAction') {
728
- registerDerivedVar(id.name, t.memberExpression(
729
- t.identifier('$action'),
730
- t.identifier('serverAction'),
731
- ));
732
- return;
733
- }
734
-
735
- // useServerState result → reference as $instance.serverState
736
- if (callee === 'useServerState') {
737
- registerDerivedVar(id.name, t.optionalMemberExpression(
738
- t.identifier('$instance'),
739
- t.identifier('serverState'),
740
- false,
741
- true,
742
- ));
743
- return;
744
- }
745
-
746
- // useMemo → extract the callback body expression
747
- if (callee === 'useMemo' && init.arguments.length >= 1) {
748
- const callback = init.arguments[0];
749
- if (t.isArrowFunctionExpression(callback)) {
750
- if (t.isExpression(callback.body)) {
751
- // () => expression
752
- registerDerivedVar(id.name, callback.body);
753
- } else if (t.isBlockStatement(callback.body)) {
754
- // () => { return expression; }
755
- const retStmt = callback.body.body.find(s => t.isReturnStatement(s)) as t.ReturnStatement | undefined;
756
- if (retStmt?.argument && t.isExpression(retStmt.argument)) {
757
- registerDerivedVar(id.name, retStmt.argument);
758
- }
759
- }
760
- }
761
- return;
762
- }
763
-
764
- // Skip other hooks (useState handled by extractStates, etc.)
765
- if (callee.startsWith('use')) return;
766
- }
767
-
768
- // Regular const declaration: inline the expression
769
- registerDerivedVar(id.name, init);
770
- },
771
-
772
- // Extract JSX from function component return.
773
- // Only extract from the exported component function's direct return,
774
- // not from nested callbacks (Each render functions, helper components, etc.).
775
- ReturnStatement(path, state) {
776
- if (!t.isJSXElement(path.node.argument) && !t.isJSXFragment(path.node.argument)) return;
777
-
778
- // Walk up to find the nearest enclosing function
779
- const parentFn = path.getFunctionParent();
780
- if (!parentFn) return;
781
-
782
- // Check if this function is the exported component:
783
- // export default function Foo() { ... }
784
- // export function Foo() { ... }
785
- // export default Foo (where Foo is defined as function above)
786
- const parentNode = parentFn.parentPath;
787
- const isExportedDirectly =
788
- parentNode?.isExportDefaultDeclaration() ||
789
- parentNode?.isExportNamedDeclaration();
790
-
791
- // Also handle: const Foo = () => { return <JSX/> }; export default Foo;
792
- // In this case parentFn is the arrow/function expression assigned to a variable.
793
- // We check if the variable name matches the exported identifier.
794
- let isExportedByName = false;
795
- if (!isExportedDirectly && parentNode?.isVariableDeclarator()) {
796
- const varId = parentNode.node.id;
797
- if (t.isIdentifier(varId)) {
798
- isExportedByName = varId.name === (state as CompilerState).metadata?.name;
799
- }
800
- }
801
-
802
- if (isExportedDirectly || isExportedByName) {
803
- extractComponents(path, state);
804
- }
805
- },
806
- };
807
- }