@mandujs/core 0.54.32 → 0.55.0-beta.1
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/README.md +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +14 -2
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +16 -15
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/props-serialization.test.ts +37 -37
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +82 -82
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +127 -211
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/spec/schema.ts +35 -35
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { needsHydration, type RouteSpec } from "../spec/schema";
|
|
1
|
+
import { needsHydration, type RouteSpec } from "../spec/schema";
|
|
2
2
|
import { GENERATED_RELATIVE_PATHS } from "../paths";
|
|
3
3
|
|
|
4
4
|
export function generateApiHandler(route: RouteSpec): string {
|
|
@@ -238,43 +238,43 @@ function computeSlotImportPath(slotModule: string, fromDir: string): string {
|
|
|
238
238
|
return result;
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
export function generatePageComponent(route: RouteSpec): string {
|
|
242
|
-
// Island-First: clientModule이 있으면 Island render를 SSR에서 직접 사용
|
|
243
|
-
if (route.clientModule) {
|
|
244
|
-
return generatePageComponentWithIsland(route);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const hasCompilerOwnedBoundaries = (route.boundaries?.length ?? 0) > 0;
|
|
248
|
-
|
|
249
|
-
if (needsHydration(route) && !hasCompilerOwnedBoundaries) {
|
|
250
|
-
throw new Error(
|
|
251
|
-
`[${route.id}] Route has hydration strategy "${route.hydration?.strategy}" but no clientModule. ` +
|
|
252
|
-
"Refusing to generate a placeholder page because it would disagree with runtime hydration state.",
|
|
253
|
-
);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// slotModule이 있으면 PageHandler 형식으로 생성 (filling 포함)
|
|
257
|
-
if (route.slotModule) {
|
|
258
|
-
return generatePageHandlerWithSlot(route);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (route.kind === "page" && route.componentModule) {
|
|
262
|
-
return generatePageComponentFromModule(route);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (needsHydration(route)) {
|
|
266
|
-
throw new Error(
|
|
267
|
-
`[${route.id}] Route has hydration strategy "${route.hydration?.strategy}" and compiler-owned boundaries, ` +
|
|
268
|
-
"but no componentModule. Refusing to generate a placeholder page because it would disagree with runtime hydration state.",
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const pageName = toPascalCase(route.id);
|
|
273
|
-
|
|
274
|
-
// Legacy fallback for malformed historical manifests that lack componentModule.
|
|
275
|
-
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
276
|
-
// Route ID: ${route.id}
|
|
277
|
-
// Pattern: ${route.pattern}
|
|
241
|
+
export function generatePageComponent(route: RouteSpec): string {
|
|
242
|
+
// Island-First: clientModule이 있으면 Island render를 SSR에서 직접 사용
|
|
243
|
+
if (route.clientModule) {
|
|
244
|
+
return generatePageComponentWithIsland(route);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const hasCompilerOwnedBoundaries = (route.boundaries?.length ?? 0) > 0;
|
|
248
|
+
|
|
249
|
+
if (needsHydration(route) && !hasCompilerOwnedBoundaries) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`[${route.id}] Route has hydration strategy "${route.hydration?.strategy}" but no clientModule. ` +
|
|
252
|
+
"Refusing to generate a placeholder page because it would disagree with runtime hydration state.",
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// slotModule이 있으면 PageHandler 형식으로 생성 (filling 포함)
|
|
257
|
+
if (route.slotModule) {
|
|
258
|
+
return generatePageHandlerWithSlot(route);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (route.kind === "page" && route.componentModule) {
|
|
262
|
+
return generatePageComponentFromModule(route);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (needsHydration(route)) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
`[${route.id}] Route has hydration strategy "${route.hydration?.strategy}" and compiler-owned boundaries, ` +
|
|
268
|
+
"but no componentModule. Refusing to generate a placeholder page because it would disagree with runtime hydration state.",
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const pageName = toPascalCase(route.id);
|
|
273
|
+
|
|
274
|
+
// Legacy fallback for malformed historical manifests that lack componentModule.
|
|
275
|
+
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
276
|
+
// Route ID: ${route.id}
|
|
277
|
+
// Pattern: ${route.pattern}
|
|
278
278
|
|
|
279
279
|
import React from "react";
|
|
280
280
|
|
|
@@ -290,31 +290,31 @@ export default function ${pageName}Page({ params }: Props): React.ReactElement {
|
|
|
290
290
|
React.createElement("p", null, "Pattern: ${route.pattern}")
|
|
291
291
|
);
|
|
292
292
|
}
|
|
293
|
-
`;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export function generatePageComponentFromModule(route: RouteSpec): string {
|
|
297
|
-
const pageName = toPascalCase(route.id);
|
|
298
|
-
const pageImportPath = computeSlotImportPath(route.componentModule!, GENERATED_RELATIVE_PATHS.webRoutes);
|
|
299
|
-
|
|
300
|
-
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
301
|
-
// Route ID: ${route.id}
|
|
302
|
-
// Pattern: ${route.pattern}
|
|
303
|
-
// Page Module: ${route.componentModule}
|
|
304
|
-
|
|
305
|
-
import React from "react";
|
|
306
|
-
import pageModule from "${pageImportPath}";
|
|
307
|
-
|
|
308
|
-
interface Props {
|
|
309
|
-
params: Record<string, string>;
|
|
310
|
-
loaderData?: unknown;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
export default function ${pageName}Page(props: Props): React.ReactElement {
|
|
314
|
-
return React.createElement(pageModule as React.ComponentType<Props>, props);
|
|
315
|
-
}
|
|
316
|
-
`;
|
|
317
|
-
}
|
|
293
|
+
`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function generatePageComponentFromModule(route: RouteSpec): string {
|
|
297
|
+
const pageName = toPascalCase(route.id);
|
|
298
|
+
const pageImportPath = computeSlotImportPath(route.componentModule!, GENERATED_RELATIVE_PATHS.webRoutes);
|
|
299
|
+
|
|
300
|
+
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
301
|
+
// Route ID: ${route.id}
|
|
302
|
+
// Pattern: ${route.pattern}
|
|
303
|
+
// Page Module: ${route.componentModule}
|
|
304
|
+
|
|
305
|
+
import React from "react";
|
|
306
|
+
import pageModule from "${pageImportPath}";
|
|
307
|
+
|
|
308
|
+
interface Props {
|
|
309
|
+
params: Record<string, string>;
|
|
310
|
+
loaderData?: unknown;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export default function ${pageName}Page(props: Props): React.ReactElement {
|
|
314
|
+
return React.createElement(pageModule as React.ComponentType<Props>, props);
|
|
315
|
+
}
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
318
|
|
|
319
319
|
/**
|
|
320
320
|
* Island-First Rendering: SSR이 island의 render 함수를 직접 사용
|
|
@@ -322,51 +322,51 @@ export default function ${pageName}Page(props: Props): React.ReactElement {
|
|
|
322
322
|
* - SSR과 클라이언트가 동일한 렌더링 로직 사용 → 불일치 구조적 방지
|
|
323
323
|
* - slotModule 유무에 따라 두 가지 변형 생성
|
|
324
324
|
*/
|
|
325
|
-
export function generatePageComponentWithIsland(route: RouteSpec): string {
|
|
326
|
-
const pageName = toPascalCase(route.id);
|
|
327
|
-
const clientImportPath = computeSlotImportPath(route.clientModule!, GENERATED_RELATIVE_PATHS.webRoutes);
|
|
328
|
-
const pageImportPath = route.kind === "page" && route.componentModule
|
|
329
|
-
? computeSlotImportPath(route.componentModule, GENERATED_RELATIVE_PATHS.webRoutes)
|
|
330
|
-
: null;
|
|
331
|
-
const pageModuleComment = route.kind === "page" && route.componentModule
|
|
332
|
-
? `// Page Module: ${route.componentModule}\n`
|
|
333
|
-
: "";
|
|
334
|
-
const shouldImportPageModule = !!pageImportPath &&
|
|
335
|
-
normalizeRouteModulePath(route.componentModule) !== normalizeRouteModulePath(route.clientModule);
|
|
336
|
-
const pageImport = shouldImportPageModule ? `import pageModule from "${pageImportPath}";\n` : "";
|
|
337
|
-
const pageRenderTarget = shouldImportPageModule ? "pageModule" : "islandModule";
|
|
338
|
-
const renderHelper = generateClientBackedPageRenderHelper(pageRenderTarget, route.id);
|
|
339
|
-
|
|
340
|
-
// clientModule + slotModule → PageRegistration 형식
|
|
341
|
-
if (route.slotModule) {
|
|
342
|
-
const slotImportPath = computeSlotImportPath(route.slotModule!, GENERATED_RELATIVE_PATHS.webRoutes);
|
|
325
|
+
export function generatePageComponentWithIsland(route: RouteSpec): string {
|
|
326
|
+
const pageName = toPascalCase(route.id);
|
|
327
|
+
const clientImportPath = computeSlotImportPath(route.clientModule!, GENERATED_RELATIVE_PATHS.webRoutes);
|
|
328
|
+
const pageImportPath = route.kind === "page" && route.componentModule
|
|
329
|
+
? computeSlotImportPath(route.componentModule, GENERATED_RELATIVE_PATHS.webRoutes)
|
|
330
|
+
: null;
|
|
331
|
+
const pageModuleComment = route.kind === "page" && route.componentModule
|
|
332
|
+
? `// Page Module: ${route.componentModule}\n`
|
|
333
|
+
: "";
|
|
334
|
+
const shouldImportPageModule = !!pageImportPath &&
|
|
335
|
+
normalizeRouteModulePath(route.componentModule) !== normalizeRouteModulePath(route.clientModule);
|
|
336
|
+
const pageImport = shouldImportPageModule ? `import pageModule from "${pageImportPath}";\n` : "";
|
|
337
|
+
const pageRenderTarget = shouldImportPageModule ? "pageModule" : "islandModule";
|
|
338
|
+
const renderHelper = generateClientBackedPageRenderHelper(pageRenderTarget, route.id);
|
|
339
|
+
|
|
340
|
+
// clientModule + slotModule → PageRegistration 형식
|
|
341
|
+
if (route.slotModule) {
|
|
342
|
+
const slotImportPath = computeSlotImportPath(route.slotModule!, GENERATED_RELATIVE_PATHS.webRoutes);
|
|
343
343
|
|
|
344
344
|
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
345
345
|
// Island-First Rendering + Slot Module
|
|
346
346
|
// Route ID: ${route.id}
|
|
347
|
-
// Pattern: ${route.pattern}
|
|
348
|
-
// Client Module: ${route.clientModule}
|
|
349
|
-
// Slot Module: ${route.slotModule}
|
|
350
|
-
${pageModuleComment}
|
|
351
|
-
|
|
352
|
-
import React from "react";
|
|
353
|
-
import filling from "${slotImportPath}";
|
|
354
|
-
import islandModule from "${clientImportPath}";
|
|
355
|
-
${pageImport}
|
|
356
|
-
|
|
357
|
-
interface Props {
|
|
358
|
-
params: Record<string, string>;
|
|
359
|
-
loaderData?: unknown;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
${renderHelper}
|
|
363
|
-
|
|
364
|
-
function ${pageName}Page({ params, loaderData }: Props): React.ReactElement {
|
|
365
|
-
const serverData = (loaderData || {}) as Record<string, unknown>;
|
|
366
|
-
return renderClientBackedPage({ params, loaderData }, serverData);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// PageRegistration 형식으로 export (server.ts의 registerPageHandler용)
|
|
347
|
+
// Pattern: ${route.pattern}
|
|
348
|
+
// Client Module: ${route.clientModule}
|
|
349
|
+
// Slot Module: ${route.slotModule}
|
|
350
|
+
${pageModuleComment}
|
|
351
|
+
|
|
352
|
+
import React from "react";
|
|
353
|
+
import filling from "${slotImportPath}";
|
|
354
|
+
import islandModule from "${clientImportPath}";
|
|
355
|
+
${pageImport}
|
|
356
|
+
|
|
357
|
+
interface Props {
|
|
358
|
+
params: Record<string, string>;
|
|
359
|
+
loaderData?: unknown;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
${renderHelper}
|
|
363
|
+
|
|
364
|
+
function ${pageName}Page({ params, loaderData }: Props): React.ReactElement {
|
|
365
|
+
const serverData = (loaderData || {}) as Record<string, unknown>;
|
|
366
|
+
return renderClientBackedPage({ params, loaderData }, serverData);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// PageRegistration 형식으로 export (server.ts의 registerPageHandler용)
|
|
370
370
|
export default {
|
|
371
371
|
component: ${pageName}Page,
|
|
372
372
|
filling: filling,
|
|
@@ -377,124 +377,124 @@ export default {
|
|
|
377
377
|
// clientModule만 (slotModule 없음) → default export 컴포넌트
|
|
378
378
|
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
379
379
|
// Island-First Rendering: SSR이 island render 직접 사용
|
|
380
|
-
// Route ID: ${route.id}
|
|
381
|
-
// Pattern: ${route.pattern}
|
|
382
|
-
// Client Module: ${route.clientModule}
|
|
383
|
-
${pageModuleComment}
|
|
384
|
-
|
|
385
|
-
import React from "react";
|
|
386
|
-
import islandModule from "${clientImportPath}";
|
|
387
|
-
${pageImport}
|
|
388
|
-
|
|
389
|
-
interface Props {
|
|
390
|
-
params: Record<string, string>;
|
|
391
|
-
loaderData?: unknown;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
${renderHelper}
|
|
395
|
-
|
|
396
|
-
export default function ${pageName}Page({ params, loaderData }: Props): React.ReactElement {
|
|
397
|
-
const serverData = (loaderData || {}) as Record<string, unknown>;
|
|
398
|
-
return renderClientBackedPage({ params, loaderData }, serverData);
|
|
399
|
-
}
|
|
400
|
-
`;
|
|
401
|
-
}
|
|
380
|
+
// Route ID: ${route.id}
|
|
381
|
+
// Pattern: ${route.pattern}
|
|
382
|
+
// Client Module: ${route.clientModule}
|
|
383
|
+
${pageModuleComment}
|
|
384
|
+
|
|
385
|
+
import React from "react";
|
|
386
|
+
import islandModule from "${clientImportPath}";
|
|
387
|
+
${pageImport}
|
|
388
|
+
|
|
389
|
+
interface Props {
|
|
390
|
+
params: Record<string, string>;
|
|
391
|
+
loaderData?: unknown;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
${renderHelper}
|
|
395
|
+
|
|
396
|
+
export default function ${pageName}Page({ params, loaderData }: Props): React.ReactElement {
|
|
397
|
+
const serverData = (loaderData || {}) as Record<string, unknown>;
|
|
398
|
+
return renderClientBackedPage({ params, loaderData }, serverData);
|
|
399
|
+
}
|
|
400
|
+
`;
|
|
401
|
+
}
|
|
402
402
|
|
|
403
403
|
/**
|
|
404
404
|
* slotModule이 있는 Page Route용 Handler 생성
|
|
405
405
|
* - component와 filling을 함께 export
|
|
406
406
|
* - server.ts에서 filling.executeLoader() 호출 가능
|
|
407
407
|
*/
|
|
408
|
-
export function generatePageHandlerWithSlot(route: RouteSpec): string {
|
|
409
|
-
const pageName = toPascalCase(route.id);
|
|
410
|
-
const slotImportPath = computeSlotImportPath(route.slotModule!, GENERATED_RELATIVE_PATHS.serverRoutes);
|
|
411
|
-
const pageImportPath = route.kind === "page" && route.componentModule
|
|
412
|
-
? computeSlotImportPath(route.componentModule, GENERATED_RELATIVE_PATHS.serverRoutes)
|
|
413
|
-
: null;
|
|
414
|
-
const pageImport = pageImportPath ? `import pageModule from "${pageImportPath}";\n` : "";
|
|
415
|
-
const pageRender = pageImportPath
|
|
416
|
-
? ` return React.createElement(pageModule as React.ComponentType<Props>, { params, loaderData });`
|
|
417
|
-
: ` return React.createElement("div", null,
|
|
418
|
-
React.createElement("h1", null, "${pageName} Page"),
|
|
419
|
-
React.createElement("p", null, "Route ID: ${route.id}"),
|
|
420
|
-
React.createElement("p", null, "Pattern: ${route.pattern}"),
|
|
421
|
-
loaderData ? React.createElement("pre", null, JSON.stringify(loaderData, null, 2)) : null
|
|
422
|
-
);`;
|
|
423
|
-
|
|
424
|
-
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
425
|
-
// Route ID: ${route.id}
|
|
426
|
-
// Pattern: ${route.pattern}
|
|
427
|
-
// Slot Module: ${route.slotModule}
|
|
428
|
-
|
|
429
|
-
import React from "react";
|
|
430
|
-
import filling from "${slotImportPath}";
|
|
431
|
-
${pageImport}
|
|
432
|
-
|
|
433
|
-
interface Props {
|
|
434
|
-
params: Record<string, string>;
|
|
435
|
-
loaderData?: unknown;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function ${pageName}Page({ params, loaderData }: Props): React.ReactElement {
|
|
439
|
-
${pageRender}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// PageRegistration 형식으로 export (server.ts의 registerPageHandler용)
|
|
408
|
+
export function generatePageHandlerWithSlot(route: RouteSpec): string {
|
|
409
|
+
const pageName = toPascalCase(route.id);
|
|
410
|
+
const slotImportPath = computeSlotImportPath(route.slotModule!, GENERATED_RELATIVE_PATHS.serverRoutes);
|
|
411
|
+
const pageImportPath = route.kind === "page" && route.componentModule
|
|
412
|
+
? computeSlotImportPath(route.componentModule, GENERATED_RELATIVE_PATHS.serverRoutes)
|
|
413
|
+
: null;
|
|
414
|
+
const pageImport = pageImportPath ? `import pageModule from "${pageImportPath}";\n` : "";
|
|
415
|
+
const pageRender = pageImportPath
|
|
416
|
+
? ` return React.createElement(pageModule as React.ComponentType<Props>, { params, loaderData });`
|
|
417
|
+
: ` return React.createElement("div", null,
|
|
418
|
+
React.createElement("h1", null, "${pageName} Page"),
|
|
419
|
+
React.createElement("p", null, "Route ID: ${route.id}"),
|
|
420
|
+
React.createElement("p", null, "Pattern: ${route.pattern}"),
|
|
421
|
+
loaderData ? React.createElement("pre", null, JSON.stringify(loaderData, null, 2)) : null
|
|
422
|
+
);`;
|
|
423
|
+
|
|
424
|
+
return `// Generated by Mandu - DO NOT EDIT DIRECTLY
|
|
425
|
+
// Route ID: ${route.id}
|
|
426
|
+
// Pattern: ${route.pattern}
|
|
427
|
+
// Slot Module: ${route.slotModule}
|
|
428
|
+
|
|
429
|
+
import React from "react";
|
|
430
|
+
import filling from "${slotImportPath}";
|
|
431
|
+
${pageImport}
|
|
432
|
+
|
|
433
|
+
interface Props {
|
|
434
|
+
params: Record<string, string>;
|
|
435
|
+
loaderData?: unknown;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function ${pageName}Page({ params, loaderData }: Props): React.ReactElement {
|
|
439
|
+
${pageRender}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// PageRegistration 형식으로 export (server.ts의 registerPageHandler용)
|
|
443
443
|
export default {
|
|
444
444
|
component: ${pageName}Page,
|
|
445
445
|
filling: filling,
|
|
446
446
|
};
|
|
447
|
-
`;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function generateClientBackedPageRenderHelper(pageRenderTarget: string, routeId: string): string {
|
|
451
|
-
return `type ManduIslandModule = {
|
|
452
|
-
__mandu_island: true;
|
|
453
|
-
definition: {
|
|
454
|
-
setup: (serverData: Record<string, unknown>) => unknown;
|
|
455
|
-
render: (props: unknown) => React.ReactNode;
|
|
456
|
-
};
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
function isManduIslandModule(value: unknown): value is ManduIslandModule {
|
|
460
|
-
const candidate = value as Partial<ManduIslandModule> | null;
|
|
461
|
-
return !!(
|
|
462
|
-
candidate &&
|
|
463
|
-
typeof candidate === "object" &&
|
|
464
|
-
candidate.__mandu_island === true &&
|
|
465
|
-
typeof candidate.definition?.setup === "function" &&
|
|
466
|
-
typeof candidate.definition?.render === "function"
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function renderClientBackedPage(props: Props, serverData: Record<string, unknown>): React.ReactElement {
|
|
471
|
-
if (isManduIslandModule(islandModule)) {
|
|
472
|
-
const setupResult = islandModule.definition.setup(serverData);
|
|
473
|
-
return islandModule.definition.render(setupResult) as React.ReactElement;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (typeof ${pageRenderTarget} === "function") {
|
|
477
|
-
return React.createElement(${pageRenderTarget} as React.ComponentType<Props>, props);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
if (typeof islandModule === "function") {
|
|
481
|
-
return React.createElement(islandModule as React.ComponentType<Record<string, unknown>>, serverData);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
if (React.isValidElement(${pageRenderTarget})) {
|
|
485
|
-
return ${pageRenderTarget} as React.ReactElement;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (React.isValidElement(islandModule)) {
|
|
489
|
-
return islandModule as React.ReactElement;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
throw new Error("[Mandu] Route ${routeId} clientModule must export a Mandu island or React component.");
|
|
493
|
-
}`;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Convert string to PascalCase (handles kebab-case, snake_case)
|
|
447
|
+
`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function generateClientBackedPageRenderHelper(pageRenderTarget: string, routeId: string): string {
|
|
451
|
+
return `type ManduIslandModule = {
|
|
452
|
+
__mandu_island: true;
|
|
453
|
+
definition: {
|
|
454
|
+
setup: (serverData: Record<string, unknown>) => unknown;
|
|
455
|
+
render: (props: unknown) => React.ReactNode;
|
|
456
|
+
};
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
function isManduIslandModule(value: unknown): value is ManduIslandModule {
|
|
460
|
+
const candidate = value as Partial<ManduIslandModule> | null;
|
|
461
|
+
return !!(
|
|
462
|
+
candidate &&
|
|
463
|
+
typeof candidate === "object" &&
|
|
464
|
+
candidate.__mandu_island === true &&
|
|
465
|
+
typeof candidate.definition?.setup === "function" &&
|
|
466
|
+
typeof candidate.definition?.render === "function"
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function renderClientBackedPage(props: Props, serverData: Record<string, unknown>): React.ReactElement {
|
|
471
|
+
if (isManduIslandModule(islandModule)) {
|
|
472
|
+
const setupResult = islandModule.definition.setup(serverData);
|
|
473
|
+
return islandModule.definition.render(setupResult) as React.ReactElement;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (typeof ${pageRenderTarget} === "function") {
|
|
477
|
+
return React.createElement(${pageRenderTarget} as React.ComponentType<Props>, props);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (typeof islandModule === "function") {
|
|
481
|
+
return React.createElement(islandModule as React.ComponentType<Record<string, unknown>>, serverData);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (React.isValidElement(${pageRenderTarget})) {
|
|
485
|
+
return ${pageRenderTarget} as React.ReactElement;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (React.isValidElement(islandModule)) {
|
|
489
|
+
return islandModule as React.ReactElement;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
throw new Error("[Mandu] Route ${routeId} clientModule must export a Mandu island or React component.");
|
|
493
|
+
}`;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Convert string to PascalCase (handles kebab-case, snake_case)
|
|
498
498
|
* "todo-page" → "TodoPage"
|
|
499
499
|
* "user_profile" → "UserProfile"
|
|
500
500
|
*/
|
|
@@ -505,10 +505,10 @@ function toPascalCase(str: string): string {
|
|
|
505
505
|
.join("");
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
-
function pathDirname(filePath: string): string {
|
|
509
|
-
return filePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/");
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function normalizeRouteModulePath(filePath: string | undefined): string {
|
|
513
|
-
return (filePath ?? "").replace(/\\/g, "/").replace(/^\.\//, "");
|
|
514
|
-
}
|
|
508
|
+
function pathDirname(filePath: string): string {
|
|
509
|
+
return filePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/");
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function normalizeRouteModulePath(filePath: string | undefined): string {
|
|
513
|
+
return (filePath ?? "").replace(/\\/g, "/").replace(/^\.\//, "");
|
|
514
|
+
}
|