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

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 (265) hide show
  1. package/ATOM-PIPELINE.md +144 -0
  2. package/README.md +88 -40
  3. package/dist/babel/index.js +2814 -277
  4. package/dist/babel/index.mjs +2 -2
  5. package/dist/chunk-3USIFFE4.mjs +2190 -0
  6. package/dist/chunk-45YMGEVT.mjs +186 -0
  7. package/dist/chunk-4FN2AISW.mjs +148 -0
  8. package/dist/chunk-4OPI5L7G.mjs +2593 -0
  9. package/dist/chunk-4RYTKOOJ.mjs +186 -0
  10. package/dist/chunk-52XHYD2V.mjs +214 -0
  11. package/dist/chunk-5GUFFFGL.mjs +148 -0
  12. package/dist/chunk-5RKTOVR5.mjs +244 -0
  13. package/dist/chunk-5YDMOO4X.mjs +214 -0
  14. package/dist/chunk-64ZWEMLJ.mjs +148 -0
  15. package/dist/chunk-6XP4KSWQ.mjs +2190 -0
  16. package/dist/chunk-72QWL54I.mjs +175 -0
  17. package/dist/chunk-7B4TRI7C.mjs +4835 -0
  18. package/dist/chunk-7ZKGHTNB.mjs +4952 -0
  19. package/dist/chunk-CIESM3BP.mjs +33 -0
  20. package/dist/chunk-DE3ZGQAC.mjs +148 -0
  21. package/dist/chunk-DMCY3BBG.mjs +1933 -0
  22. package/dist/chunk-DPIK3PJS.mjs +244 -0
  23. package/dist/chunk-E5IVH4RE.mjs +186 -0
  24. package/dist/chunk-E6FZNUR5.mjs +4953 -0
  25. package/dist/chunk-EJRBDQDP.mjs +2607 -0
  26. package/dist/chunk-ELO4TXJL.mjs +186 -0
  27. package/dist/chunk-EO6SYNCG.mjs +175 -0
  28. package/dist/chunk-FKRO52XH.mjs +3446 -0
  29. package/dist/chunk-FL4YAKU6.mjs +4941 -0
  30. package/dist/chunk-FYT47UBU.mjs +5076 -0
  31. package/dist/chunk-GCLGPOJZ.mjs +148 -0
  32. package/dist/chunk-GXB4JOP7.mjs +5072 -0
  33. package/dist/chunk-HFXOUMTD.mjs +175 -0
  34. package/dist/chunk-HWIZ47US.mjs +214 -0
  35. package/dist/chunk-IB7MNPQL.mjs +4953 -0
  36. package/dist/chunk-ICSIHQCG.mjs +148 -0
  37. package/dist/chunk-J7JUAHS4.mjs +186 -0
  38. package/dist/chunk-JLA5VNQ3.mjs +186 -0
  39. package/dist/chunk-JQLWFCTM.mjs +214 -0
  40. package/dist/chunk-KFJJCQAL.mjs +148 -0
  41. package/dist/chunk-KJUIIEQE.mjs +186 -0
  42. package/dist/chunk-KNWTHRVQ.mjs +175 -0
  43. package/dist/chunk-KSG4XSZF.mjs +175 -0
  44. package/dist/chunk-LF5N6DOU.mjs +175 -0
  45. package/dist/chunk-LJQCM2IM.mjs +214 -0
  46. package/dist/chunk-NTB7OEX2.mjs +2918 -0
  47. package/dist/chunk-NW6555WJ.mjs +186 -0
  48. package/dist/chunk-OMZE6VLQ.mjs +214 -0
  49. package/dist/chunk-OPJKP747.mjs +7506 -0
  50. package/dist/chunk-P4BR7WVO.mjs +2190 -0
  51. package/dist/chunk-QQHVYH2X.mjs +244 -0
  52. package/dist/chunk-S5QLWLLT.mjs +186 -0
  53. package/dist/chunk-SCWGT2FY.mjs +2190 -0
  54. package/dist/chunk-SMKJUSB3.mjs +2190 -0
  55. package/dist/chunk-THFYE5ZX.mjs +244 -0
  56. package/dist/chunk-VCAY2KGM.mjs +175 -0
  57. package/dist/chunk-WBYMW4NQ.mjs +3450 -0
  58. package/dist/chunk-WECAV6QB.mjs +148 -0
  59. package/dist/chunk-WMKBXUCE.mjs +3228 -0
  60. package/dist/chunk-XAJ5BKKL.mjs +4947 -0
  61. package/dist/chunk-XG2X7AEA.mjs +175 -0
  62. package/dist/chunk-XG7Z23NQ.mjs +148 -0
  63. package/dist/chunk-XWZAOCQ7.mjs +2607 -0
  64. package/dist/chunk-Y6MA7ULW.mjs +148 -0
  65. package/dist/chunk-YMS7Q7LG.mjs +214 -0
  66. package/dist/chunk-ZA37XTGA.mjs +175 -0
  67. package/dist/cli/index.js +13189 -6838
  68. package/dist/cli/index.mjs +140 -22
  69. package/dist/codemod/cli.mjs +1 -1
  70. package/dist/codemod/index.mjs +1 -1
  71. package/dist/config-PL24KEWL.mjs +219 -0
  72. package/dist/dev-server-RmGHIntF.d.mts +113 -0
  73. package/dist/dev-server-RmGHIntF.d.ts +113 -0
  74. package/dist/dev-server.d.mts +1 -1
  75. package/dist/dev-server.d.ts +1 -1
  76. package/dist/dev-server.js +4135 -440
  77. package/dist/dev-server.mjs +5 -5
  78. package/dist/envelope.js +2812 -275
  79. package/dist/envelope.mjs +3 -3
  80. package/dist/index.d.mts +161 -2
  81. package/dist/index.d.ts +161 -2
  82. package/dist/index.js +4429 -428
  83. package/dist/index.mjs +217 -9
  84. package/{src/cli/init.ts → dist/init-7JQMAAXS.mjs} +70 -95
  85. package/dist/init-DQDX3QK6.mjs +369 -0
  86. package/dist/init-EHO4VQ22.mjs +369 -0
  87. package/dist/init-UC3FWPIW.mjs +367 -0
  88. package/dist/init-UNSMVKIK.mjs +366 -0
  89. package/dist/init-UNV5XIDE.mjs +367 -0
  90. package/dist/project-compiler-2P4N4DR7.mjs +10 -0
  91. package/dist/project-compiler-D2LCC27O.mjs +10 -0
  92. package/dist/project-compiler-EJ3GANJE.mjs +10 -0
  93. package/dist/project-compiler-LOQKVRZJ.mjs +10 -0
  94. package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
  95. package/dist/project-compiler-RQ6OQKRM.mjs +10 -0
  96. package/dist/project-compiler-VWNNCHGO.mjs +10 -0
  97. package/dist/project-compiler-XVAAU4C5.mjs +10 -0
  98. package/dist/project-compiler-YES5FGMD.mjs +10 -0
  99. package/dist/project-compiler-ZKMQDLGU.mjs +10 -0
  100. package/dist/project-decompiler-FLXCEJHS.mjs +7 -0
  101. package/dist/project-decompiler-US7GAVIC.mjs +7 -0
  102. package/dist/project-decompiler-VLPR22QF.mjs +7 -0
  103. package/dist/pull-FUS5QYZS.mjs +109 -0
  104. package/dist/pull-LD5ENLGY.mjs +109 -0
  105. package/dist/pull-P44LDRWB.mjs +109 -0
  106. package/dist/testing/index.js +2822 -285
  107. package/dist/testing/index.mjs +2 -2
  108. package/dist/verify-SEIXUGN4.mjs +1833 -0
  109. package/dist/vite/index.js +2815 -278
  110. package/dist/vite/index.mjs +3 -3
  111. package/examples/uber-app/app/admin/fleet.tsx +19 -19
  112. package/package.json +16 -6
  113. package/compile-blueprint-chat.mjs +0 -99
  114. package/compile-blueprint-glass-console.mjs +0 -98
  115. package/compile-chat-defs.mjs +0 -92
  116. package/examples/uber-app/tests/payment.test.tsx +0 -129
  117. package/examples/uber-app/tests/ride-flow.test.tsx +0 -123
  118. package/package.json.backup +0 -86
  119. package/scripts/decompile.ts +0 -226
  120. package/scripts/seed-auth.ts +0 -267
  121. package/scripts/seed-uber.ts +0 -248
  122. package/scripts/validate-uber.ts +0 -119
  123. package/seed-blueprint-chat.mjs +0 -444
  124. package/seed-blueprint-glass-console.mjs +0 -445
  125. package/seed-compiled.mjs +0 -318
  126. package/src/RoundTripValidator.ts +0 -400
  127. package/src/__tests__/atom-rendering-coverage.test.ts +0 -680
  128. package/src/__tests__/auth-module-compilation.test.ts +0 -247
  129. package/src/__tests__/auth-template-compilation.test.ts +0 -589
  130. package/src/__tests__/change-extractor.test.ts +0 -142
  131. package/src/__tests__/cli-pull.test.ts +0 -73
  132. package/src/__tests__/cli-test.test.ts +0 -72
  133. package/src/__tests__/component-extractor.test.ts +0 -331
  134. package/src/__tests__/context-extractor.test.ts +0 -145
  135. package/src/__tests__/decompiler.test.ts +0 -718
  136. package/src/__tests__/define-blueprint.test.ts +0 -133
  137. package/src/__tests__/definition-validator.test.ts +0 -519
  138. package/src/__tests__/during-extractor.test.ts +0 -152
  139. package/src/__tests__/effect-extractor.test.ts +0 -107
  140. package/src/__tests__/event-emission.test.ts +0 -127
  141. package/src/__tests__/examples.test.ts +0 -236
  142. package/src/__tests__/full-blueprint-coverage.test.ts +0 -1221
  143. package/src/__tests__/golden-suite.test.ts +0 -403
  144. package/src/__tests__/grammar-island-extractor.test.ts +0 -289
  145. package/src/__tests__/instance-key.test.ts +0 -82
  146. package/src/__tests__/ir-migration.test.ts +0 -255
  147. package/src/__tests__/lock-file.test.ts +0 -117
  148. package/src/__tests__/model-extractor.test.ts +0 -195
  149. package/src/__tests__/model-field-acl.test.ts +0 -237
  150. package/src/__tests__/model-hooks.test.ts +0 -130
  151. package/src/__tests__/model-ref-resolution.test.ts +0 -268
  152. package/src/__tests__/model-roundtrip.test.ts +0 -502
  153. package/src/__tests__/model-runtime.test.ts +0 -112
  154. package/src/__tests__/model-transitions.test.ts +0 -183
  155. package/src/__tests__/nrt-action-trace.test.ts +0 -391
  156. package/src/__tests__/pipeline-hardening.test.ts +0 -413
  157. package/src/__tests__/project-compiler.test.ts +0 -546
  158. package/src/__tests__/project-decompiler.test.ts +0 -343
  159. package/src/__tests__/query-compilation.test.ts +0 -145
  160. package/src/__tests__/round-trip/PLAN.md +0 -158
  161. package/src/__tests__/round-trip/README.md +0 -52
  162. package/src/__tests__/round-trip/RESULTS.md +0 -86
  163. package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +0 -55
  164. package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +0 -11
  165. package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +0 -54
  166. package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +0 -79
  167. package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +0 -12
  168. package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +0 -50
  169. package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +0 -25
  170. package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +0 -11
  171. package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +0 -32
  172. package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +0 -79
  173. package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +0 -10
  174. package/src/__tests__/round-trip/round-trip.test.ts +0 -2598
  175. package/src/__tests__/round-trip-ir.test.ts +0 -300
  176. package/src/__tests__/round-trip.test.ts +0 -1212
  177. package/src/__tests__/route-merging.test.ts +0 -372
  178. package/src/__tests__/router-composition.test.ts +0 -489
  179. package/src/__tests__/router-extractor.test.ts +0 -176
  180. package/src/__tests__/server-action-extractor.test.ts +0 -128
  181. package/src/__tests__/smart-type-inference.test.ts +0 -365
  182. package/src/__tests__/source-envelope.test.ts +0 -284
  183. package/src/__tests__/source-fidelity.test.ts +0 -516
  184. package/src/__tests__/state-extractor.test.ts +0 -115
  185. package/src/__tests__/strict-mode.test.ts +0 -227
  186. package/src/__tests__/transition-effect-extractor.test.ts +0 -119
  187. package/src/__tests__/transition-extractor.test.ts +0 -68
  188. package/src/__tests__/ts-to-expression.test.ts +0 -462
  189. package/src/__tests__/type-generator.test.ts +0 -201
  190. package/src/__tests__/uber-validation.test.ts +0 -502
  191. package/src/action-compiler.ts +0 -361
  192. package/src/babel/emitters/experience-transform.ts +0 -199
  193. package/src/babel/emitters/ir-to-tsx-emitter.ts +0 -110
  194. package/src/babel/emitters/pure-form-emitter.ts +0 -1023
  195. package/src/babel/emitters/runtime-glue-emitter.ts +0 -39
  196. package/src/babel/extractors/change-extractor.ts +0 -199
  197. package/src/babel/extractors/component-extractor.ts +0 -907
  198. package/src/babel/extractors/computed-extractor.ts +0 -262
  199. package/src/babel/extractors/context-extractor.ts +0 -277
  200. package/src/babel/extractors/during-extractor.ts +0 -295
  201. package/src/babel/extractors/effect-extractor.ts +0 -340
  202. package/src/babel/extractors/event-extractor.ts +0 -235
  203. package/src/babel/extractors/grammar-island-extractor.ts +0 -302
  204. package/src/babel/extractors/model-extractor.ts +0 -1018
  205. package/src/babel/extractors/router-extractor.ts +0 -303
  206. package/src/babel/extractors/server-action-extractor.ts +0 -173
  207. package/src/babel/extractors/server-action-hook-extractor.ts +0 -72
  208. package/src/babel/extractors/server-state-extractor.ts +0 -88
  209. package/src/babel/extractors/state-extractor.ts +0 -214
  210. package/src/babel/extractors/transition-effect-extractor.ts +0 -176
  211. package/src/babel/extractors/transition-extractor.ts +0 -143
  212. package/src/babel/index.ts +0 -24
  213. package/src/babel/transpilers/ts-to-expression.ts +0 -674
  214. package/src/babel/visitor.ts +0 -807
  215. package/src/cli/auth.ts +0 -255
  216. package/src/cli/build.ts +0 -288
  217. package/src/cli/deploy.ts +0 -206
  218. package/src/cli/index.ts +0 -328
  219. package/src/cli/installer.ts +0 -261
  220. package/src/cli/lock-file.ts +0 -94
  221. package/src/cli/mmrc.ts +0 -22
  222. package/src/cli/pull.ts +0 -172
  223. package/src/cli/registry-client.ts +0 -175
  224. package/src/cli/test.ts +0 -397
  225. package/src/cli/type-generator.ts +0 -243
  226. package/src/codemod/__tests__/forward.test.ts +0 -239
  227. package/src/codemod/__tests__/reverse.test.ts +0 -145
  228. package/src/codemod/__tests__/round-trip.test.ts +0 -137
  229. package/src/codemod/annotation.ts +0 -97
  230. package/src/codemod/classify.ts +0 -197
  231. package/src/codemod/cli.ts +0 -207
  232. package/src/codemod/control-flow.ts +0 -409
  233. package/src/codemod/forward.ts +0 -244
  234. package/src/codemod/import-manager.ts +0 -171
  235. package/src/codemod/index.ts +0 -120
  236. package/src/codemod/reverse.ts +0 -197
  237. package/src/codemod/rules.ts +0 -174
  238. package/src/codemod/state-transform.ts +0 -126
  239. package/src/decompiler/ast-builder.ts +0 -538
  240. package/src/decompiler/config-generator.ts +0 -151
  241. package/src/decompiler/index.ts +0 -315
  242. package/src/decompiler/project-decompiler.ts +0 -1776
  243. package/src/decompiler/project.ts +0 -862
  244. package/src/decompiler/split-strategy.ts +0 -140
  245. package/src/decompiler/state-emitter.ts +0 -1053
  246. package/src/decompiler/sx-emitter.ts +0 -318
  247. package/src/decompiler/workspace-hydrator.ts +0 -189
  248. package/src/dev-server.ts +0 -238
  249. package/src/envelope/fs-tree.ts +0 -217
  250. package/src/envelope/source-envelope.ts +0 -264
  251. package/src/envelope.ts +0 -315
  252. package/src/incremental-compiler.ts +0 -401
  253. package/src/index.ts +0 -99
  254. package/src/model-compiler.ts +0 -277
  255. package/src/project-compiler.ts +0 -1629
  256. package/src/route-extractor.ts +0 -333
  257. package/src/testing/index.ts +0 -32
  258. package/src/testing/snapshot.ts +0 -252
  259. package/src/testing/test-utils.ts +0 -226
  260. package/src/types.ts +0 -68
  261. package/src/vite/index.ts +0 -288
  262. package/test-compile.mjs +0 -142
  263. package/tsconfig.json +0 -25
  264. package/tsup.config.ts +0 -23
  265. package/vitest.config.ts +0 -9
@@ -1,489 +0,0 @@
1
- /**
2
- * Router/Route Experience Composition Tests
3
- *
4
- * Validates that compileProject() correctly composes Router/Route atoms
5
- * from app/ page files, including nav links, index routes, blueprint_manifest,
6
- * and componentDefinitions metadata.
7
- */
8
-
9
- import { describe, it, expect } from 'vitest';
10
- import { compileProject } from '../project-compiler';
11
- import type { IRExperienceNode } from '@mindmatrix/player-core';
12
-
13
- // =============================================================================
14
- // Helpers
15
- // =============================================================================
16
-
17
- /** Minimal page TSX that the babel plugin can compile (returns JSX). */
18
- function page(title: string): string {
19
- return `
20
- export default function ${title.replace(/[^a-zA-Z]/g, '')}Page() {
21
- return <div><h1>${title}</h1></div>;
22
- }
23
- `;
24
- }
25
-
26
- /** Extract the composed experience from the parent IR. */
27
- function getExperience(ir: any): IRExperienceNode | undefined {
28
- return ir.experience as IRExperienceNode | undefined;
29
- }
30
-
31
- /** Find a child node by component type (recursive). */
32
- function findByComponent(node: IRExperienceNode, component: string): IRExperienceNode | undefined {
33
- if (node.component === component) return node;
34
- for (const child of node.children ?? []) {
35
- const found = findByComponent(child, component);
36
- if (found) return found;
37
- }
38
- return undefined;
39
- }
40
-
41
- /** Collect all nodes matching a component type (recursive). */
42
- function findAllByComponent(node: IRExperienceNode, component: string): IRExperienceNode[] {
43
- const results: IRExperienceNode[] = [];
44
- if (node.component === component) results.push(node);
45
- for (const child of node.children ?? []) {
46
- results.push(...findAllByComponent(child, component));
47
- }
48
- return results;
49
- }
50
-
51
- // =============================================================================
52
- // Tests
53
- // =============================================================================
54
-
55
- describe('Router/Route Experience Composition', () => {
56
- // -------------------------------------------------------------------------
57
- // 1. Single page file → Router with one route + index route
58
- // -------------------------------------------------------------------------
59
- it('should compose a Router with one route + index route from a single page', () => {
60
- const result = compileProject({
61
- 'mm.config.ts': `
62
- import { defineBlueprint } from '@mindmatrix/react';
63
- export default defineBlueprint({ slug: 'single-page', name: 'Single Page App', version: '1.0.0' });
64
- `,
65
- 'app/home.tsx': page('Home'),
66
- });
67
-
68
- expect(result.errors).toHaveLength(0);
69
-
70
- const exp = getExperience(result.ir);
71
- expect(exp).toBeDefined();
72
-
73
- // Root should be a Stack
74
- expect(exp!.component).toBe('Stack');
75
-
76
- // Should have a Router child
77
- const router = findByComponent(exp!, 'Router');
78
- expect(router).toBeDefined();
79
-
80
- // Router should contain Route children
81
- const routes = findAllByComponent(router!, 'Route');
82
- expect(routes.length).toBeGreaterThanOrEqual(2); // index + /home
83
-
84
- // One route should be the index with exact:true
85
- const indexRoute = routes.find(r => r.config?.path === '/' && r.config?.exact === true);
86
- expect(indexRoute).toBeDefined();
87
-
88
- // One route should be /home
89
- const homeRoute = routes.find(r => r.config?.path === '/home');
90
- expect(homeRoute).toBeDefined();
91
- });
92
-
93
- // -------------------------------------------------------------------------
94
- // 2. Multiple pages → Router with correct routes + nav links
95
- // -------------------------------------------------------------------------
96
- it('should compose Router with multiple routes and nav links', () => {
97
- const result = compileProject({
98
- 'mm.config.ts': `
99
- import { defineBlueprint } from '@mindmatrix/react';
100
- export default defineBlueprint({ slug: 'multi-page', name: 'Multi Page', version: '1.0.0' });
101
- `,
102
- 'app/home.tsx': page('Home'),
103
- 'app/settings.tsx': page('Settings'),
104
- });
105
-
106
- expect(result.errors).toHaveLength(0);
107
-
108
- const exp = getExperience(result.ir);
109
- expect(exp).toBeDefined();
110
-
111
- const router = findByComponent(exp!, 'Router');
112
- expect(router).toBeDefined();
113
-
114
- // Should have routes for /, /home, /settings
115
- const routes = findAllByComponent(router!, 'Route');
116
- const routePaths = routes.map(r => r.config?.path);
117
- expect(routePaths).toContain('/home');
118
- expect(routePaths).toContain('/settings');
119
- expect(routePaths).toContain('/');
120
-
121
- // Should have NavLink children in a nav bar
122
- const navLinks = findAllByComponent(exp!, 'NavLink');
123
- expect(navLinks.length).toBeGreaterThanOrEqual(2);
124
-
125
- // NavLinks should have 'to' config pointing to routes
126
- const navTargets = navLinks.map(n => n.config?.to);
127
- expect(navTargets).toContain('/home');
128
- expect(navTargets).toContain('/settings');
129
- });
130
-
131
- // -------------------------------------------------------------------------
132
- // 3. Nested pages → correct route paths
133
- // -------------------------------------------------------------------------
134
- it('should generate correct paths for nested pages', () => {
135
- const result = compileProject({
136
- 'mm.config.ts': `
137
- import { defineBlueprint } from '@mindmatrix/react';
138
- export default defineBlueprint({ slug: 'nested-app', name: 'Nested', version: '1.0.0' });
139
- `,
140
- 'app/admin/dashboard.tsx': page('Dashboard'),
141
- 'app/admin/users.tsx': page('Users'),
142
- });
143
-
144
- expect(result.errors).toHaveLength(0);
145
-
146
- const exp = getExperience(result.ir);
147
- expect(exp).toBeDefined();
148
-
149
- const router = findByComponent(exp!, 'Router');
150
- expect(router).toBeDefined();
151
-
152
- const routes = findAllByComponent(router!, 'Route');
153
- const routePaths = routes.map(r => r.config?.path);
154
- expect(routePaths).toContain('/admin/dashboard');
155
- expect(routePaths).toContain('/admin/users');
156
- });
157
-
158
- // -------------------------------------------------------------------------
159
- // 4. Route path generation: index.tsx → /, page.tsx → /, projects.tsx → /projects
160
- // -------------------------------------------------------------------------
161
- it('should map app/index.tsx to / route path', () => {
162
- const result = compileProject({
163
- 'app/index.tsx': page('Index'),
164
- });
165
-
166
- expect(result.errors).toHaveLength(0);
167
-
168
- const exp = getExperience(result.ir);
169
- expect(exp).toBeDefined();
170
-
171
- const routes = findAllByComponent(exp!, 'Route');
172
- // index.tsx strips to '/' → route path should be '/'
173
- const indexPaths = routes.filter(r => r.config?.path === '/');
174
- expect(indexPaths.length).toBeGreaterThanOrEqual(1);
175
- });
176
-
177
- it('should map app/page.tsx to / route path', () => {
178
- const result = compileProject({
179
- 'app/page.tsx': page('Page'),
180
- });
181
-
182
- expect(result.errors).toHaveLength(0);
183
-
184
- const exp = getExperience(result.ir);
185
- expect(exp).toBeDefined();
186
-
187
- const routes = findAllByComponent(exp!, 'Route');
188
- const indexPaths = routes.filter(r => r.config?.path === '/');
189
- expect(indexPaths.length).toBeGreaterThanOrEqual(1);
190
- });
191
-
192
- it('should map app/projects.tsx to /projects route path', () => {
193
- const result = compileProject({
194
- 'app/projects.tsx': page('Projects'),
195
- });
196
-
197
- expect(result.errors).toHaveLength(0);
198
-
199
- const exp = getExperience(result.ir);
200
- expect(exp).toBeDefined();
201
-
202
- const routes = findAllByComponent(exp!, 'Route');
203
- const routePaths = routes.map(r => r.config?.path);
204
- expect(routePaths).toContain('/projects');
205
- });
206
-
207
- // -------------------------------------------------------------------------
208
- // 5. Nav link labels: kebab-case → Title Case
209
- // -------------------------------------------------------------------------
210
- it('should convert kebab-case filenames to Title Case nav labels', () => {
211
- const result = compileProject({
212
- 'app/surge-pricing.tsx': page('SurgePricing'),
213
- 'app/ride-history.tsx': page('RideHistory'),
214
- });
215
-
216
- expect(result.errors).toHaveLength(0);
217
-
218
- const exp = getExperience(result.ir);
219
- expect(exp).toBeDefined();
220
-
221
- const navLinks = findAllByComponent(exp!, 'NavLink');
222
- const labels = navLinks.map(n => n.config?.label);
223
- expect(labels).toContain('Surge Pricing');
224
- expect(labels).toContain('Ride History');
225
- });
226
-
227
- // -------------------------------------------------------------------------
228
- // 6. Index route has exact:true
229
- // -------------------------------------------------------------------------
230
- it('should set exact:true on the index route', () => {
231
- const result = compileProject({
232
- 'app/home.tsx': page('Home'),
233
- 'app/about.tsx': page('About'),
234
- });
235
-
236
- expect(result.errors).toHaveLength(0);
237
-
238
- const exp = getExperience(result.ir);
239
- expect(exp).toBeDefined();
240
-
241
- const routes = findAllByComponent(exp!, 'Route');
242
- const indexRoute = routes.find(r => r.config?.path === '/' && r.config?.exact === true);
243
- expect(indexRoute).toBeDefined();
244
-
245
- // Non-index routes should NOT have exact:true
246
- const homeRoute = routes.find(r => r.config?.path === '/home');
247
- expect(homeRoute).toBeDefined();
248
- expect(homeRoute!.config?.exact).toBeUndefined();
249
- });
250
-
251
- // -------------------------------------------------------------------------
252
- // 7. blueprint_manifest added to metadata with correct routes
253
- // -------------------------------------------------------------------------
254
- it('should add blueprint_manifest to metadata', () => {
255
- const result = compileProject({
256
- 'mm.config.ts': `
257
- import { defineBlueprint } from '@mindmatrix/react';
258
- export default defineBlueprint({ slug: 'uber-clone', name: 'Uber Clone', version: '1.0.0' });
259
- `,
260
- 'app/home.tsx': page('Home'),
261
- 'app/rides.tsx': page('Rides'),
262
- });
263
-
264
- expect(result.errors).toHaveLength(0);
265
-
266
- const meta = result.ir.metadata as Record<string, any>;
267
- expect(meta.blueprint_manifest).toBeDefined();
268
- expect(meta.blueprint_manifest.routes).toBeDefined();
269
- expect(meta.blueprint_manifest.routes).toHaveLength(1);
270
-
271
- const manifestRoute = meta.blueprint_manifest.routes[0];
272
- expect(manifestRoute.path).toBe('uber-clone/*');
273
- expect(manifestRoute.node).toBe('uber-clone');
274
- expect(manifestRoute.label).toBe('Uber Clone');
275
- });
276
-
277
- // -------------------------------------------------------------------------
278
- // 8. full_bleed: true in manifest config
279
- // -------------------------------------------------------------------------
280
- it('should set full_bleed: true in blueprint_manifest config', () => {
281
- const result = compileProject({
282
- 'mm.config.ts': `
283
- import { defineBlueprint } from '@mindmatrix/react';
284
- export default defineBlueprint({ slug: 'my-app', name: 'My App', version: '1.0.0' });
285
- `,
286
- 'app/dashboard.tsx': page('Dashboard'),
287
- });
288
-
289
- expect(result.errors).toHaveLength(0);
290
-
291
- const meta = result.ir.metadata as Record<string, any>;
292
- expect(meta.blueprint_manifest).toBeDefined();
293
- expect(meta.blueprint_manifest.config).toBeDefined();
294
- expect(meta.blueprint_manifest.config.full_bleed).toBe(true);
295
- });
296
-
297
- // -------------------------------------------------------------------------
298
- // 9. componentDefinitions included in metadata when components/*.tsx exist
299
- // -------------------------------------------------------------------------
300
- it('should include componentDefinitions in metadata for components/*.tsx files', () => {
301
- const result = compileProject({
302
- 'mm.config.ts': `
303
- import { defineBlueprint } from '@mindmatrix/react';
304
- export default defineBlueprint({ slug: 'comp-app', name: 'Comp App', version: '1.0.0' });
305
- `,
306
- 'app/home.tsx': page('Home'),
307
- 'components/MapView.tsx': `
308
- export function MapView({ latitude, longitude }: { latitude: number; longitude: number }) {
309
- return <div>Map at {latitude}, {longitude}</div>;
310
- }
311
- `,
312
- });
313
-
314
- expect(result.errors).toHaveLength(0);
315
-
316
- // componentDefinitions should be in the result
317
- expect(result.componentDefinitions).toBeDefined();
318
- expect(result.componentDefinitions['MapView']).toBeDefined();
319
- expect(result.componentDefinitions['MapView'].props).toContain('latitude');
320
- expect(result.componentDefinitions['MapView'].props).toContain('longitude');
321
-
322
- // componentDefinitions should also be in metadata
323
- const meta = result.ir.metadata as Record<string, any>;
324
- expect(meta.componentDefinitions).toBeDefined();
325
- expect(meta.componentDefinitions['MapView']).toBeDefined();
326
- });
327
-
328
- // -------------------------------------------------------------------------
329
- // 10. Page experiences NOT flattened into a single Stack (the old bug)
330
- // -------------------------------------------------------------------------
331
- it('should NOT flatten page experiences into a single Stack', () => {
332
- const result = compileProject({
333
- 'mm.config.ts': `
334
- import { defineBlueprint } from '@mindmatrix/react';
335
- export default defineBlueprint({ slug: 'no-flatten', name: 'No Flatten', version: '1.0.0' });
336
- `,
337
- 'app/page-one.tsx': page('PageOne'),
338
- 'app/page-two.tsx': page('PageTwo'),
339
- 'app/page-three.tsx': page('PageThree'),
340
- });
341
-
342
- expect(result.errors).toHaveLength(0);
343
-
344
- const exp = getExperience(result.ir);
345
- expect(exp).toBeDefined();
346
-
347
- // The root experience should be a Stack containing a nav-bar (Row) and a Router.
348
- // It should NOT be a bare Stack where all pages are direct children
349
- // (that would be the old bug — flattening pages).
350
- expect(exp!.component).toBe('Stack');
351
- expect(exp!.id).toBe('blueprint-root');
352
-
353
- // Root children: nav-bar (Row) + page-router (Router)
354
- const children = exp!.children ?? [];
355
- expect(children.length).toBe(2);
356
-
357
- const navBar = children.find((c: any) => c.component === 'Row');
358
- expect(navBar).toBeDefined();
359
-
360
- const router = children.find((c: any) => c.component === 'Router');
361
- expect(router).toBeDefined();
362
-
363
- // Each page should be inside a Route node, not directly in the Stack
364
- const routes = findAllByComponent(router!, 'Route');
365
- expect(routes.length).toBeGreaterThanOrEqual(3); // 3 pages + 1 index
366
-
367
- // Verify page content is nested inside Route children, not at the top-level
368
- for (const route of routes) {
369
- expect(route.children).toBeDefined();
370
- expect(route.children!.length).toBeGreaterThanOrEqual(1);
371
- }
372
- });
373
-
374
- // -------------------------------------------------------------------------
375
- // Additional: pageExperiences returned correctly
376
- // -------------------------------------------------------------------------
377
- it('should return per-page experience trees in pageExperiences', () => {
378
- const result = compileProject({
379
- 'app/home.tsx': page('Home'),
380
- 'app/settings.tsx': page('Settings'),
381
- });
382
-
383
- expect(result.errors).toHaveLength(0);
384
- expect(result.pageExperiences).toBeDefined();
385
- expect(Object.keys(result.pageExperiences)).toHaveLength(2);
386
- expect(result.pageExperiences['app/home.tsx']).toBeDefined();
387
- expect(result.pageExperiences['app/settings.tsx']).toBeDefined();
388
- });
389
-
390
- // -------------------------------------------------------------------------
391
- // Additional: routeTable populated for page files
392
- // -------------------------------------------------------------------------
393
- it('should populate routeTable with entries for each page file', () => {
394
- const result = compileProject({
395
- 'mm.config.ts': `
396
- import { defineBlueprint } from '@mindmatrix/react';
397
- export default defineBlueprint({ slug: 'route-test', name: 'Route Test', version: '1.0.0' });
398
- `,
399
- 'app/home.tsx': page('Home'),
400
- 'app/settings.tsx': page('Settings'),
401
- 'app/admin/users.tsx': page('Users'),
402
- });
403
-
404
- expect(result.errors).toHaveLength(0);
405
- expect(result.routeTable.length).toBeGreaterThanOrEqual(3);
406
-
407
- const paths = result.routeTable.map(r => r.path);
408
- // Verify that route paths are generated (exact format depends on Phase 1 vs Phase 2)
409
- expect(paths.length).toBeGreaterThanOrEqual(3);
410
- });
411
-
412
- // -------------------------------------------------------------------------
413
- // Additional: Router child definition created
414
- // -------------------------------------------------------------------------
415
- it('should create a router child definition from page files', () => {
416
- const result = compileProject({
417
- 'mm.config.ts': `
418
- import { defineBlueprint } from '@mindmatrix/react';
419
- export default defineBlueprint({ slug: 'child-test', name: 'Child Test', version: '1.0.0' });
420
- `,
421
- 'app/home.tsx': page('Home'),
422
- 'app/about.tsx': page('About'),
423
- });
424
-
425
- expect(result.errors).toHaveLength(0);
426
-
427
- // A router child definition should exist
428
- const routerChild = result.childDefinitions.find(
429
- cd => cd.category === 'router' || cd.slug?.includes('router')
430
- );
431
- expect(routerChild).toBeDefined();
432
- });
433
-
434
- // -------------------------------------------------------------------------
435
- // Additional: nav bar has Row component with correct structure
436
- // -------------------------------------------------------------------------
437
- it('should render nav bar as a Row with NavLink children', () => {
438
- const result = compileProject({
439
- 'app/home.tsx': page('Home'),
440
- 'app/profile.tsx': page('Profile'),
441
- 'app/settings.tsx': page('Settings'),
442
- });
443
-
444
- expect(result.errors).toHaveLength(0);
445
-
446
- const exp = getExperience(result.ir);
447
- expect(exp).toBeDefined();
448
-
449
- // Find the nav bar (Row)
450
- const navBar = (exp!.children ?? []).find((c: any) => c.component === 'Row');
451
- expect(navBar).toBeDefined();
452
- expect(navBar!.id).toBe('nav-bar');
453
- expect(navBar!.config?.gap).toBe(1);
454
-
455
- // Nav bar should contain NavLinks for each page
456
- const navLinks = findAllByComponent(navBar!, 'NavLink');
457
- expect(navLinks).toHaveLength(3);
458
- });
459
-
460
- // -------------------------------------------------------------------------
461
- // Additional: No experience when no page files exist
462
- // -------------------------------------------------------------------------
463
- it('should not set Router experience when no app/ page files exist', () => {
464
- const result = compileProject({
465
- 'mm.config.ts': `
466
- import { defineBlueprint } from '@mindmatrix/react';
467
- export default defineBlueprint({ slug: 'no-pages', name: 'No Pages', version: '1.0.0' });
468
- `,
469
- 'main.workflow.tsx': `
470
- export function Main() {
471
- const [x, setX] = useState(0);
472
- return <div>{x}</div>;
473
- }
474
- `,
475
- });
476
-
477
- expect(result.errors).toHaveLength(0);
478
-
479
- // No page files → no Router composition
480
- const exp = getExperience(result.ir);
481
- // Experience may exist from the workflow view, but should NOT be a Router
482
- if (exp) {
483
- expect(exp.id).not.toBe('blueprint-root');
484
- }
485
-
486
- const meta = result.ir.metadata as Record<string, any>;
487
- expect(meta.blueprint_manifest).toBeUndefined();
488
- });
489
- });
@@ -1,176 +0,0 @@
1
- /**
2
- * Router Extractor Tests — validates file-based route → state derivation.
3
- */
4
-
5
- import { describe, it, expect } from 'vitest';
6
- import {
7
- extractRouterWorkflow,
8
- pathToStateName,
9
- pathToUrlPattern,
10
- extractParams,
11
- } from '../babel/extractors/router-extractor';
12
- import type { PageFile } from '../babel/extractors/router-extractor';
13
-
14
- describe('Router Extractor', () => {
15
- describe('pathToStateName', () => {
16
- it('should convert root path to HOME', () => {
17
- expect(pathToStateName('')).toBe('HOME');
18
- expect(pathToStateName('/')).toBe('HOME');
19
- });
20
-
21
- it('should convert simple paths to UPPER_CASE', () => {
22
- expect(pathToStateName('chat')).toBe('CHAT');
23
- expect(pathToStateName('settings')).toBe('SETTINGS');
24
- });
25
-
26
- it('should handle nested paths', () => {
27
- expect(pathToStateName('chat/messages')).toBe('CHAT_MESSAGES');
28
- });
29
-
30
- it('should handle parameterized segments', () => {
31
- expect(pathToStateName('chat/[channelId]')).toBe('CHAT_CHANNEL_ID');
32
- });
33
-
34
- it('should handle camelCase segments', () => {
35
- expect(pathToStateName('userProfile')).toBe('USER_PROFILE');
36
- });
37
-
38
- it('should handle kebab-case segments', () => {
39
- expect(pathToStateName('user-settings')).toBe('USER_SETTINGS');
40
- });
41
- });
42
-
43
- describe('pathToUrlPattern', () => {
44
- it('should convert simple page path', () => {
45
- expect(pathToUrlPattern('chat/page.tsx')).toBe('/chat');
46
- });
47
-
48
- it('should convert root page path', () => {
49
- expect(pathToUrlPattern('page.tsx')).toBe('/');
50
- });
51
-
52
- it('should convert parameterized paths', () => {
53
- expect(pathToUrlPattern('chat/[channelId]/page.tsx')).toBe('/chat/:channelId');
54
- });
55
-
56
- it('should handle nested params', () => {
57
- expect(pathToUrlPattern('users/[userId]/posts/[postId]/page.tsx')).toBe('/users/:userId/posts/:postId');
58
- });
59
- });
60
-
61
- describe('extractParams', () => {
62
- it('should extract no params from static paths', () => {
63
- expect(extractParams('chat/page.tsx')).toEqual([]);
64
- });
65
-
66
- it('should extract single param', () => {
67
- expect(extractParams('chat/[channelId]/page.tsx')).toEqual(['channelId']);
68
- });
69
-
70
- it('should extract multiple params', () => {
71
- expect(extractParams('users/[userId]/posts/[postId]/page.tsx')).toEqual(['userId', 'postId']);
72
- });
73
- });
74
-
75
- describe('extractRouterWorkflow', () => {
76
- it('should create states from page files', () => {
77
- const pages: PageFile[] = [
78
- { relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
79
- { relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
80
- { relativePath: 'settings/page.tsx', absolutePath: '/app/settings/page.tsx' },
81
- ];
82
-
83
- const ir = extractRouterWorkflow(pages);
84
-
85
- expect(ir.states).toHaveLength(3);
86
- expect(ir.states.map((s) => s.name)).toContain('HOME');
87
- expect(ir.states.map((s) => s.name)).toContain('CHAT');
88
- expect(ir.states.map((s) => s.name)).toContain('SETTINGS');
89
- });
90
-
91
- it('should mark root as START state', () => {
92
- const pages: PageFile[] = [
93
- { relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
94
- { relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
95
- ];
96
-
97
- const ir = extractRouterWorkflow(pages);
98
-
99
- const homeState = ir.states.find((s) => s.name === 'HOME');
100
- expect(homeState?.type).toBe('START');
101
- });
102
-
103
- it('should create fields for route params', () => {
104
- const pages: PageFile[] = [
105
- { relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
106
- { relativePath: 'chat/[channelId]/page.tsx', absolutePath: '/app/chat/[channelId]/page.tsx' },
107
- ];
108
-
109
- const ir = extractRouterWorkflow(pages);
110
-
111
- const channelIdField = ir.fields.find((f) => f.name === 'channelId');
112
- expect(channelIdField).toBeDefined();
113
- expect(channelIdField!.type).toBe('text');
114
- });
115
-
116
- it('should deduplicate param fields', () => {
117
- const pages: PageFile[] = [
118
- { relativePath: 'users/[userId]/page.tsx', absolutePath: '/app/users/[userId]/page.tsx' },
119
- { relativePath: 'users/[userId]/posts/page.tsx', absolutePath: '/app/users/[userId]/posts/page.tsx' },
120
- ];
121
-
122
- const ir = extractRouterWorkflow(pages);
123
-
124
- const userIdFields = ir.fields.filter((f) => f.name === 'userId');
125
- expect(userIdFields).toHaveLength(1);
126
- });
127
-
128
- it('should create navigate_home transitions', () => {
129
- const pages: PageFile[] = [
130
- { relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
131
- { relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
132
- { relativePath: 'settings/page.tsx', absolutePath: '/app/settings/page.tsx' },
133
- ];
134
-
135
- const ir = extractRouterWorkflow(pages);
136
-
137
- const homeTransitions = ir.transitions.filter((t) => t.to === 'HOME');
138
- expect(homeTransitions.length).toBeGreaterThanOrEqual(2);
139
- });
140
-
141
- it('should set router category and metadata', () => {
142
- const pages: PageFile[] = [
143
- { relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
144
- ];
145
-
146
- const ir = extractRouterWorkflow(pages);
147
-
148
- expect(ir.category).toBe('router');
149
- expect(ir.tags).toContainEqual({ tag_name: 'auto-derived' });
150
- expect((ir.metadata as any).routes).toBeDefined();
151
- });
152
-
153
- it('should detect layout boundaries', () => {
154
- const pages: PageFile[] = [
155
- { relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
156
- { relativePath: 'chat/page.tsx', absolutePath: '/app/chat/page.tsx' },
157
- { relativePath: 'layout.tsx', absolutePath: '/app/layout.tsx', isLayout: true },
158
- { relativePath: 'chat/layout.tsx', absolutePath: '/app/chat/layout.tsx', isLayout: true },
159
- ];
160
-
161
- const ir = extractRouterWorkflow(pages);
162
-
163
- const chatRoute = (ir.metadata as any).routes.find((r: any) => r.state === 'CHAT');
164
- expect(chatRoute.layoutBoundary).toBe('chat/layout.tsx');
165
- });
166
-
167
- it('should use custom slug', () => {
168
- const pages: PageFile[] = [
169
- { relativePath: 'page.tsx', absolutePath: '/app/page.tsx' },
170
- ];
171
-
172
- const ir = extractRouterWorkflow(pages, { slug: 'mm-chat-router' });
173
- expect(ir.slug).toBe('mm-chat-router');
174
- });
175
- });
176
- });