@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.
- package/ATOM-PIPELINE.md +144 -0
- package/README.md +88 -40
- package/dist/auth-3UK75242.mjs +17 -0
- package/dist/babel/index.d.mts +2 -2
- package/dist/babel/index.d.ts +2 -2
- package/dist/babel/index.js +2816 -279
- package/dist/babel/index.mjs +2 -2
- package/dist/chunk-3USIFFE4.mjs +2190 -0
- package/dist/chunk-45YMGEVT.mjs +186 -0
- package/dist/chunk-4FN2AISW.mjs +148 -0
- package/dist/chunk-4OPI5L7G.mjs +2593 -0
- package/dist/chunk-4RYTKOOJ.mjs +186 -0
- package/dist/chunk-52XHYD2V.mjs +214 -0
- package/dist/chunk-5FTDWKHH.mjs +244 -0
- package/dist/chunk-5GUFFFGL.mjs +148 -0
- package/dist/chunk-5RKTOVR5.mjs +244 -0
- package/dist/chunk-5YDMOO4X.mjs +214 -0
- package/dist/chunk-64ZWEMLJ.mjs +148 -0
- package/dist/chunk-6XP4KSWQ.mjs +2190 -0
- package/dist/chunk-72QWL54I.mjs +175 -0
- package/dist/chunk-7B4TRI7C.mjs +4835 -0
- package/dist/chunk-7JRAEFRB.mjs +7510 -0
- package/dist/chunk-7T6Q5KAA.mjs +7506 -0
- package/dist/chunk-7ZKGHTNB.mjs +4952 -0
- package/dist/chunk-ABYPKRSB.mjs +215 -0
- package/dist/chunk-BZEXUPDH.mjs +175 -0
- package/dist/chunk-CIESM3BP.mjs +33 -0
- package/dist/chunk-DE3ZGQAC.mjs +148 -0
- package/dist/chunk-DMCY3BBG.mjs +1933 -0
- package/dist/chunk-DPIK3PJS.mjs +244 -0
- package/dist/chunk-E5IVH4RE.mjs +186 -0
- package/dist/chunk-E6FZNUR5.mjs +4953 -0
- package/dist/chunk-EJRBDQDP.mjs +2607 -0
- package/dist/chunk-ELO4TXJL.mjs +186 -0
- package/dist/chunk-EO6SYNCG.mjs +175 -0
- package/dist/chunk-FKRO52XH.mjs +3446 -0
- package/dist/chunk-FL4YAKU6.mjs +4941 -0
- package/dist/chunk-FYT47UBU.mjs +5076 -0
- package/dist/chunk-GCLGPOJZ.mjs +148 -0
- package/dist/chunk-GXB4JOP7.mjs +5072 -0
- package/dist/chunk-HFXOUMTD.mjs +175 -0
- package/dist/chunk-HRYR54PT.mjs +175 -0
- package/dist/chunk-HWIZ47US.mjs +214 -0
- package/dist/chunk-IB7MNPQL.mjs +4953 -0
- package/dist/chunk-ICSIHQCG.mjs +148 -0
- package/dist/chunk-J3M4GUS7.mjs +161 -0
- package/dist/chunk-J7JUAHS4.mjs +186 -0
- package/dist/chunk-JLA5VNQ3.mjs +186 -0
- package/dist/chunk-JQLWFCTM.mjs +214 -0
- package/dist/chunk-JRGFBWTN.mjs +2918 -0
- package/dist/chunk-KFJJCQAL.mjs +148 -0
- package/dist/chunk-KJUIIEQE.mjs +186 -0
- package/dist/chunk-KNWTHRVQ.mjs +175 -0
- package/dist/chunk-KSG4XSZF.mjs +175 -0
- package/dist/chunk-LF5N6DOU.mjs +175 -0
- package/dist/chunk-LJQCM2IM.mjs +214 -0
- package/dist/chunk-NTB7OEX2.mjs +2918 -0
- package/dist/chunk-NW6555WJ.mjs +186 -0
- package/dist/chunk-O4AUS7EU.mjs +148 -0
- package/dist/chunk-OMZE6VLQ.mjs +214 -0
- package/dist/chunk-OPJKP747.mjs +7506 -0
- package/dist/chunk-P4BR7WVO.mjs +2190 -0
- package/dist/chunk-QQHVYH2X.mjs +244 -0
- package/dist/chunk-R2DD5GTY.mjs +186 -0
- package/dist/chunk-S5QLWLLT.mjs +186 -0
- package/dist/chunk-SCWGT2FY.mjs +2190 -0
- package/dist/chunk-SMKJUSB3.mjs +2190 -0
- package/dist/chunk-THFYE5ZX.mjs +244 -0
- package/dist/chunk-UDDTWG5J.mjs +734 -0
- package/dist/chunk-VCAY2KGM.mjs +175 -0
- package/dist/chunk-VLTKQDJ3.mjs +244 -0
- package/dist/chunk-WBYMW4NQ.mjs +3450 -0
- package/dist/chunk-WECAV6QB.mjs +148 -0
- package/dist/chunk-WMKBXUCE.mjs +3228 -0
- package/dist/chunk-WVYY32LD.mjs +939 -0
- package/dist/chunk-XAJ5BKKL.mjs +4947 -0
- package/dist/chunk-XDVM4YHX.mjs +3450 -0
- package/dist/chunk-XG2X7AEA.mjs +175 -0
- package/dist/chunk-XG7Z23NQ.mjs +148 -0
- package/dist/chunk-XWZAOCQ7.mjs +2607 -0
- package/dist/chunk-Y6MA7ULW.mjs +148 -0
- package/dist/chunk-YMS7Q7LG.mjs +214 -0
- package/dist/chunk-Z2G5RZ4H.mjs +186 -0
- package/dist/chunk-ZA37XTGA.mjs +175 -0
- package/dist/chunk-ZE3KCHBM.mjs +2918 -0
- package/dist/cli/index.js +14720 -7199
- package/dist/cli/index.mjs +224 -183
- package/dist/codemod/cli.js +1 -1
- package/dist/codemod/cli.mjs +2 -2
- package/dist/codemod/index.d.mts +3 -3
- package/dist/codemod/index.d.ts +3 -3
- package/dist/codemod/index.js +1 -1
- package/dist/codemod/index.mjs +2 -2
- package/dist/config-PL24KEWL.mjs +219 -0
- package/dist/deploy-YAJGW6II.mjs +9 -0
- package/dist/dev-server-CrQ041KP.d.mts +79 -0
- package/dist/dev-server-CrQ041KP.d.ts +79 -0
- package/dist/dev-server-RmGHIntF.d.mts +113 -0
- package/dist/dev-server-RmGHIntF.d.ts +113 -0
- package/dist/dev-server.d.mts +2 -2
- package/dist/dev-server.d.ts +2 -2
- package/dist/dev-server.js +6424 -1597
- package/dist/dev-server.mjs +5 -5
- package/dist/envelope-ChEkuHij.d.mts +265 -0
- package/dist/envelope-ChEkuHij.d.ts +265 -0
- package/dist/envelope.d.mts +2 -2
- package/dist/envelope.d.ts +2 -2
- package/dist/envelope.js +2814 -277
- package/dist/envelope.mjs +3 -3
- package/dist/index-CEKyyazf.d.mts +104 -0
- package/dist/index-CEKyyazf.d.ts +104 -0
- package/dist/index.d.mts +168 -9
- package/dist/index.d.ts +168 -9
- package/dist/index.js +5606 -681
- package/dist/index.mjs +217 -9
- package/dist/init-7FJENUDK.mjs +407 -0
- package/{src/cli/init.ts → dist/init-7JQMAAXS.mjs} +70 -95
- package/dist/init-DQDX3QK6.mjs +369 -0
- package/dist/init-EHO4VQ22.mjs +369 -0
- package/dist/init-UC3FWPIW.mjs +367 -0
- package/dist/init-UNSMVKIK.mjs +366 -0
- package/dist/init-UNV5XIDE.mjs +367 -0
- package/dist/project-compiler-2P4N4DR7.mjs +10 -0
- package/dist/project-compiler-D2LCC27O.mjs +10 -0
- package/dist/project-compiler-EJ3GANJE.mjs +10 -0
- package/dist/project-compiler-LOQKVRZJ.mjs +10 -0
- package/dist/project-compiler-NNK32MPG.mjs +10 -0
- package/dist/project-compiler-OP2VVGJQ.mjs +10 -0
- package/dist/project-compiler-RQ6OQKRM.mjs +10 -0
- package/dist/project-compiler-VWNNCHGO.mjs +10 -0
- package/dist/project-compiler-XVAAU4C5.mjs +10 -0
- package/dist/project-compiler-YES5FGMD.mjs +10 -0
- package/dist/project-compiler-ZB4RUYVL.mjs +10 -0
- package/dist/project-compiler-ZKMQDLGU.mjs +10 -0
- package/dist/project-decompiler-FLXCEJHS.mjs +7 -0
- package/dist/project-decompiler-U55HQUHW.mjs +7 -0
- package/dist/project-decompiler-US7GAVIC.mjs +7 -0
- package/dist/project-decompiler-VLPR22QF.mjs +7 -0
- package/dist/pull-FUS5QYZS.mjs +109 -0
- package/dist/pull-KOL2QAYQ.mjs +109 -0
- package/dist/pull-LD5ENLGY.mjs +109 -0
- package/dist/pull-P44LDRWB.mjs +109 -0
- package/dist/seed-KOGEPGOJ.mjs +154 -0
- package/dist/server-VW6UPCHO.mjs +277 -0
- package/dist/testing/index.d.mts +8 -8
- package/dist/testing/index.d.ts +8 -8
- package/dist/testing/index.js +2824 -287
- package/dist/testing/index.mjs +2 -2
- package/dist/verify-BYHUKARQ.mjs +1833 -0
- package/dist/verify-OQDEQYMS.mjs +1833 -0
- package/dist/verify-SEIXUGN4.mjs +1833 -0
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +2817 -280
- package/dist/vite/index.mjs +3 -3
- package/examples/authentication/main.workflow.tsx +1 -1
- package/examples/authentication/mm.config.ts +1 -1
- package/examples/authentication/pages/LoginPage.tsx +2 -2
- package/examples/authentication/pages/SignupPage.tsx +2 -2
- package/examples/counter.workflow.tsx +1 -1
- package/examples/dashboard.workflow.tsx +1 -1
- package/examples/invoice-approval/actions/invoice.server.ts +1 -1
- package/examples/invoice-approval/main.workflow.tsx +1 -1
- package/examples/invoice-approval/mm.config.ts +1 -1
- package/examples/invoice-approval/pages/InvoiceDetailPage.tsx +1 -1
- package/examples/invoice-approval/pages/InvoiceFormPage.tsx +1 -1
- package/examples/invoice-approval/pages/InvoiceListPage.tsx +1 -1
- package/examples/todo-app.workflow.tsx +1 -1
- package/examples/uber-app/actions/matching.server.ts +1 -1
- package/examples/uber-app/actions/notifications.server.ts +1 -1
- package/examples/uber-app/actions/payments.server.ts +1 -1
- package/examples/uber-app/actions/pricing.server.ts +1 -1
- package/examples/uber-app/app/admin/analytics.tsx +2 -2
- package/examples/uber-app/app/admin/fleet.tsx +21 -21
- package/examples/uber-app/app/admin/surge-pricing.tsx +2 -2
- package/examples/uber-app/app/driver/dashboard.tsx +2 -2
- package/examples/uber-app/app/driver/earnings.tsx +2 -2
- package/examples/uber-app/app/driver/navigation.tsx +2 -2
- package/examples/uber-app/app/driver/ride-acceptance.tsx +2 -2
- package/examples/uber-app/app/rider/home.tsx +2 -2
- package/examples/uber-app/app/rider/payment-methods.tsx +2 -2
- package/examples/uber-app/app/rider/ride-history.tsx +2 -2
- package/examples/uber-app/app/rider/ride-tracking.tsx +2 -2
- package/examples/uber-app/components/DriverCard.tsx +1 -1
- package/examples/uber-app/components/MapView.tsx +3 -3
- package/examples/uber-app/components/RatingStars.tsx +2 -2
- package/examples/uber-app/components/RideCard.tsx +1 -1
- package/examples/uber-app/mm.config.ts +1 -1
- package/examples/uber-app/workflows/dispute-resolution.workflow.tsx +2 -2
- package/examples/uber-app/workflows/driver-onboarding.workflow.tsx +2 -2
- package/examples/uber-app/workflows/payment-processing.workflow.tsx +2 -2
- package/examples/uber-app/workflows/ride-request.workflow.tsx +2 -2
- package/package.json +10 -4
- package/compile-blueprint-chat.mjs +0 -99
- package/compile-blueprint-glass-console.mjs +0 -98
- package/compile-chat-defs.mjs +0 -92
- package/examples/uber-app/tests/payment.test.tsx +0 -129
- package/examples/uber-app/tests/ride-flow.test.tsx +0 -123
- package/package.json.backup +0 -86
- package/scripts/decompile.ts +0 -226
- package/scripts/seed-auth.ts +0 -267
- package/scripts/seed-uber.ts +0 -248
- package/scripts/validate-uber.ts +0 -119
- package/seed-blueprint-chat.mjs +0 -444
- package/seed-blueprint-glass-console.mjs +0 -445
- package/seed-compiled.mjs +0 -318
- package/src/RoundTripValidator.ts +0 -400
- package/src/__tests__/atom-rendering-coverage.test.ts +0 -680
- package/src/__tests__/auth-module-compilation.test.ts +0 -247
- package/src/__tests__/auth-template-compilation.test.ts +0 -589
- package/src/__tests__/change-extractor.test.ts +0 -142
- package/src/__tests__/cli-pull.test.ts +0 -73
- package/src/__tests__/cli-test.test.ts +0 -72
- package/src/__tests__/component-extractor.test.ts +0 -331
- package/src/__tests__/context-extractor.test.ts +0 -145
- package/src/__tests__/decompiler.test.ts +0 -718
- package/src/__tests__/define-blueprint.test.ts +0 -133
- package/src/__tests__/definition-validator.test.ts +0 -519
- package/src/__tests__/during-extractor.test.ts +0 -152
- package/src/__tests__/effect-extractor.test.ts +0 -107
- package/src/__tests__/event-emission.test.ts +0 -127
- package/src/__tests__/examples.test.ts +0 -236
- package/src/__tests__/full-blueprint-coverage.test.ts +0 -1221
- package/src/__tests__/golden-suite.test.ts +0 -403
- package/src/__tests__/grammar-island-extractor.test.ts +0 -289
- package/src/__tests__/instance-key.test.ts +0 -82
- package/src/__tests__/ir-migration.test.ts +0 -255
- package/src/__tests__/lock-file.test.ts +0 -117
- package/src/__tests__/model-extractor.test.ts +0 -195
- package/src/__tests__/model-field-acl.test.ts +0 -237
- package/src/__tests__/model-hooks.test.ts +0 -130
- package/src/__tests__/model-ref-resolution.test.ts +0 -268
- package/src/__tests__/model-roundtrip.test.ts +0 -502
- package/src/__tests__/model-runtime.test.ts +0 -112
- package/src/__tests__/model-transitions.test.ts +0 -183
- package/src/__tests__/nrt-action-trace.test.ts +0 -391
- package/src/__tests__/pipeline-hardening.test.ts +0 -413
- package/src/__tests__/project-compiler.test.ts +0 -546
- package/src/__tests__/project-decompiler.test.ts +0 -343
- package/src/__tests__/query-compilation.test.ts +0 -145
- package/src/__tests__/round-trip/PLAN.md +0 -158
- package/src/__tests__/round-trip/README.md +0 -52
- package/src/__tests__/round-trip/RESULTS.md +0 -86
- package/src/__tests__/round-trip/fixtures/data-heavy/main.workflow.tsx +0 -55
- package/src/__tests__/round-trip/fixtures/data-heavy/mm.config.ts +0 -11
- package/src/__tests__/round-trip/fixtures/data-heavy/models/contact.ts +0 -54
- package/src/__tests__/round-trip/fixtures/full-workflow/main.workflow.tsx +0 -79
- package/src/__tests__/round-trip/fixtures/full-workflow/mm.config.ts +0 -12
- package/src/__tests__/round-trip/fixtures/full-workflow/models/order.ts +0 -50
- package/src/__tests__/round-trip/fixtures/simple-crud/main.workflow.tsx +0 -25
- package/src/__tests__/round-trip/fixtures/simple-crud/mm.config.ts +0 -11
- package/src/__tests__/round-trip/fixtures/simple-crud/models/task.ts +0 -32
- package/src/__tests__/round-trip/fixtures/view-heavy/main.workflow.tsx +0 -79
- package/src/__tests__/round-trip/fixtures/view-heavy/mm.config.ts +0 -10
- package/src/__tests__/round-trip/round-trip.test.ts +0 -2598
- package/src/__tests__/round-trip-ir.test.ts +0 -300
- package/src/__tests__/round-trip.test.ts +0 -1212
- package/src/__tests__/route-merging.test.ts +0 -372
- package/src/__tests__/router-composition.test.ts +0 -489
- package/src/__tests__/router-extractor.test.ts +0 -176
- package/src/__tests__/server-action-extractor.test.ts +0 -128
- package/src/__tests__/smart-type-inference.test.ts +0 -365
- package/src/__tests__/source-envelope.test.ts +0 -284
- package/src/__tests__/source-fidelity.test.ts +0 -516
- package/src/__tests__/state-extractor.test.ts +0 -115
- package/src/__tests__/strict-mode.test.ts +0 -227
- package/src/__tests__/transition-effect-extractor.test.ts +0 -119
- package/src/__tests__/transition-extractor.test.ts +0 -68
- package/src/__tests__/ts-to-expression.test.ts +0 -462
- package/src/__tests__/type-generator.test.ts +0 -201
- package/src/__tests__/uber-validation.test.ts +0 -502
- package/src/action-compiler.ts +0 -361
- package/src/babel/emitters/experience-transform.ts +0 -199
- package/src/babel/emitters/ir-to-tsx-emitter.ts +0 -110
- package/src/babel/emitters/pure-form-emitter.ts +0 -1023
- package/src/babel/emitters/runtime-glue-emitter.ts +0 -39
- package/src/babel/extractors/change-extractor.ts +0 -199
- package/src/babel/extractors/component-extractor.ts +0 -907
- package/src/babel/extractors/computed-extractor.ts +0 -262
- package/src/babel/extractors/context-extractor.ts +0 -277
- package/src/babel/extractors/during-extractor.ts +0 -295
- package/src/babel/extractors/effect-extractor.ts +0 -340
- package/src/babel/extractors/event-extractor.ts +0 -235
- package/src/babel/extractors/grammar-island-extractor.ts +0 -302
- package/src/babel/extractors/model-extractor.ts +0 -1018
- package/src/babel/extractors/router-extractor.ts +0 -303
- package/src/babel/extractors/server-action-extractor.ts +0 -173
- package/src/babel/extractors/server-action-hook-extractor.ts +0 -72
- package/src/babel/extractors/server-state-extractor.ts +0 -88
- package/src/babel/extractors/state-extractor.ts +0 -214
- package/src/babel/extractors/transition-effect-extractor.ts +0 -176
- package/src/babel/extractors/transition-extractor.ts +0 -143
- package/src/babel/index.ts +0 -24
- package/src/babel/transpilers/ts-to-expression.ts +0 -674
- package/src/babel/visitor.ts +0 -807
- package/src/cli/auth.ts +0 -255
- package/src/cli/build.ts +0 -288
- package/src/cli/deploy.ts +0 -206
- package/src/cli/index.ts +0 -328
- package/src/cli/installer.ts +0 -261
- package/src/cli/lock-file.ts +0 -94
- package/src/cli/mmrc.ts +0 -22
- package/src/cli/pull.ts +0 -172
- package/src/cli/registry-client.ts +0 -175
- package/src/cli/test.ts +0 -397
- package/src/cli/type-generator.ts +0 -243
- package/src/codemod/__tests__/forward.test.ts +0 -239
- package/src/codemod/__tests__/reverse.test.ts +0 -145
- package/src/codemod/__tests__/round-trip.test.ts +0 -137
- package/src/codemod/annotation.ts +0 -97
- package/src/codemod/classify.ts +0 -197
- package/src/codemod/cli.ts +0 -207
- package/src/codemod/control-flow.ts +0 -409
- package/src/codemod/forward.ts +0 -244
- package/src/codemod/import-manager.ts +0 -171
- package/src/codemod/index.ts +0 -120
- package/src/codemod/reverse.ts +0 -197
- package/src/codemod/rules.ts +0 -174
- package/src/codemod/state-transform.ts +0 -126
- package/src/decompiler/ast-builder.ts +0 -538
- package/src/decompiler/config-generator.ts +0 -151
- package/src/decompiler/index.ts +0 -315
- package/src/decompiler/project-decompiler.ts +0 -1776
- package/src/decompiler/project.ts +0 -862
- package/src/decompiler/split-strategy.ts +0 -140
- package/src/decompiler/state-emitter.ts +0 -1053
- package/src/decompiler/sx-emitter.ts +0 -318
- package/src/decompiler/workspace-hydrator.ts +0 -189
- package/src/dev-server.ts +0 -238
- package/src/envelope/fs-tree.ts +0 -217
- package/src/envelope/source-envelope.ts +0 -264
- package/src/envelope.ts +0 -315
- package/src/incremental-compiler.ts +0 -401
- package/src/index.ts +0 -99
- package/src/model-compiler.ts +0 -277
- package/src/project-compiler.ts +0 -1629
- package/src/route-extractor.ts +0 -333
- package/src/testing/index.ts +0 -32
- package/src/testing/snapshot.ts +0 -252
- package/src/testing/test-utils.ts +0 -226
- package/src/types.ts +0 -68
- package/src/vite/index.ts +0 -288
- package/test-compile.mjs +0 -142
- package/tsconfig.json +0 -25
- package/tsup.config.ts +0 -23
- package/vitest.config.ts +0 -9
package/src/babel/visitor.ts
DELETED
|
@@ -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
|
-
}
|