@mandujs/core 0.53.3 → 0.54.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 +1 -1
- 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 +394 -394
- package/src/a11y/types.ts +125 -125
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +122 -122
- 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 +120 -120
- 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/bundler/__tests__/build-runner.ts +113 -113
- 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 +10 -10
- package/src/bundler/__tests__/hdr.test.ts +353 -353
- package/src/bundler/__tests__/hmr-client.test.ts +532 -532
- package/src/bundler/__tests__/manifest-schema.test.ts +266 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -519
- 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/budget.ts +404 -404
- package/src/bundler/build.test.ts +179 -179
- package/src/bundler/build.ts +55 -55
- package/src/bundler/dev.ts +42 -42
- package/src/bundler/manifest-schema.ts +301 -301
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +263 -263
- 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 +155 -155
- 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/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +201 -201
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +10 -10
- 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__/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 +340 -340
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/router.ts +11 -11
- package/src/client/runtime.ts +47 -47
- package/src/client/serialize.ts +404 -404
- 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.tsx +162 -162
- package/src/config/validate.ts +3 -3
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +8 -8
- 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/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/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +482 -482
- package/src/db/index.ts +138 -138
- 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 +182 -182
- 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__/webview-fallback.test.ts +254 -254
- package/src/desktop/__tests__/window.test.ts +248 -248
- 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/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/overlay.tsx +368 -368
- 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 +320 -320
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +500 -500
- 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/client/persistence.ts +335 -335
- 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/server/source-context.ts +450 -450
- 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/devtools/worker/worker-manager.ts +410 -410
- package/src/diagnose/__tests__/checks.test.ts +451 -451
- package/src/diagnose/checks.ts +832 -832
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +93 -93
- 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/result.ts +60 -60
- 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 +454 -454
- package/src/filling/auth.ts +308 -308
- package/src/filling/cookie-codec.ts +299 -299
- package/src/filling/deps.ts +265 -265
- 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/index.ts +3 -3
- 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/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/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- 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/index.ts +105 -105
- package/src/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -544
- package/src/kitchen/api/errors-grouping.ts +126 -0
- package/src/kitchen/kitchen-handler.ts +192 -63
- package/src/kitchen/kitchen-ui.ts +842 -464
- 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/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 +32 -32
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +823 -823
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +559 -559
- 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/schema.ts +1 -1
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.ts +6 -6
- 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__/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__/not-found.test.ts +152 -152
- 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/fast-refresh-runtime.ts +322 -322
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +300 -300
- 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/openapi-endpoint.ts +236 -236
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/router.ts +105 -105
- package/src/runtime/server.ts +67 -67
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/trace.ts +144 -144
- 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/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/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/watcher.ts +18 -18
|
@@ -1,179 +1,179 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
-
import { mkdtemp, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { pathToFileURL } from "url";
|
|
5
|
-
|
|
6
|
-
// 모든 테스트가 하나의 빌드 결과를 공유 — 병렬 Bun.build 충돌 방지
|
|
7
|
-
let rootDir: string;
|
|
8
|
-
let result: { success: boolean; errors: string[] };
|
|
9
|
-
|
|
10
|
-
async function importBuiltModule(relativePath: string): Promise<Record<string, unknown>> {
|
|
11
|
-
const fileUrl = pathToFileURL(path.join(rootDir, relativePath)).href;
|
|
12
|
-
return import(`${fileUrl}?t=${Date.now()}`);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Run `buildClientBundles` in an isolated `bun` subprocess.
|
|
17
|
-
*
|
|
18
|
-
* Bun 1.3.x exhibits a deterministic `AggregateError: Bundle failed` when
|
|
19
|
-
* `buildClientBundles` is called from a test file AND the same `bun test`
|
|
20
|
-
* process has previously imported `react` / `react-dom` through any sibling
|
|
21
|
-
* test file (happens transitively through almost every `src/testing/*` or
|
|
22
|
-
* `src/runtime/*` consumer). Retrying in-process does not recover — the
|
|
23
|
-
* resolver state is sticky. A fresh subprocess has a clean module graph.
|
|
24
|
-
* See `__tests__/build-runner.ts` for the subprocess entrypoint and more
|
|
25
|
-
* background.
|
|
26
|
-
*/
|
|
27
|
-
async function runBuildInSubprocess(root: string): Promise<{
|
|
28
|
-
success: boolean;
|
|
29
|
-
errors: string[];
|
|
30
|
-
}> {
|
|
31
|
-
const runner = path.join(
|
|
32
|
-
import.meta.dir,
|
|
33
|
-
"__tests__",
|
|
34
|
-
"build-runner.ts",
|
|
35
|
-
);
|
|
36
|
-
try {
|
|
37
|
-
const proc = Bun.spawn([process.execPath, "run", runner, root], {
|
|
38
|
-
cwd: path.resolve(import.meta.dir, "..", ".."),
|
|
39
|
-
stdin: "ignore",
|
|
40
|
-
stdout: "pipe",
|
|
41
|
-
stderr: "inherit",
|
|
42
|
-
});
|
|
43
|
-
const out = await new Response(proc.stdout).text();
|
|
44
|
-
await proc.exited;
|
|
45
|
-
|
|
46
|
-
// Find the final JSON line — the runner may log Mandu dev banners
|
|
47
|
-
// (e.g. "[Mandu] DevTools …") before emitting the payload. The
|
|
48
|
-
// contract is: last non-empty line is the JSON blob.
|
|
49
|
-
const lines = out.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
50
|
-
const last = lines[lines.length - 1] ?? "";
|
|
51
|
-
try {
|
|
52
|
-
const parsed = JSON.parse(last);
|
|
53
|
-
return {
|
|
54
|
-
success: parsed.success === true,
|
|
55
|
-
errors: Array.isArray(parsed.errors) ? parsed.errors : [],
|
|
56
|
-
};
|
|
57
|
-
} catch (e) {
|
|
58
|
-
return {
|
|
59
|
-
success: false,
|
|
60
|
-
errors: [
|
|
61
|
-
`build-runner output could not be parsed as JSON: ${String(e)}\nLast stdout line: ${last}`,
|
|
62
|
-
],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
} catch (err) {
|
|
66
|
-
return { success: false, errors: [`spawn failed: ${String(err)}`] };
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
beforeAll(async () => {
|
|
71
|
-
rootDir = await mkdtemp(path.join(import.meta.dir, ".tmp-bundler-"));
|
|
72
|
-
|
|
73
|
-
await mkdir(path.join(rootDir, "app"), { recursive: true });
|
|
74
|
-
await writeFile(
|
|
75
|
-
path.join(rootDir, "package.json"),
|
|
76
|
-
JSON.stringify({ name: "mandu-build-test", type: "module" }, null, 2),
|
|
77
|
-
"utf-8",
|
|
78
|
-
);
|
|
79
|
-
await writeFile(
|
|
80
|
-
path.join(rootDir, "app", "demo.client.tsx"),
|
|
81
|
-
"export default function DemoIsland() { return null; }\n",
|
|
82
|
-
"utf-8",
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
result = await runBuildInSubprocess(rootDir);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
afterAll(async () => {
|
|
89
|
-
if (rootDir) {
|
|
90
|
-
await rm(rootDir, { recursive: true, force: true });
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// Historical note — `MANDU_SKIP_BUNDLER_TESTS` gate REMOVED.
|
|
95
|
-
//
|
|
96
|
-
// A previous revision gated this describe block behind
|
|
97
|
-
// `describe.skipIf(MANDU_SKIP_BUNDLER_TESTS === "1")` because running
|
|
98
|
-
// `bun test src/bundler/` without the gate hung indefinitely on Windows
|
|
99
|
-
// (see Phase 0.6 and `docs/qa/wave-R2-integration-report.md`). Root cause
|
|
100
|
-
// was NOT actually in THIS file — it was a deadlock in `safe-build.test.ts`'s
|
|
101
|
-
// "slot handoff" regression test, which drove Bun's microtask queue with a
|
|
102
|
-
// `while (!stop) { await Promise.resolve() }` sampler. That starved libuv
|
|
103
|
-
// I/O callbacks, so the 7 parallel `safeBuild()` calls never completed, the
|
|
104
|
-
// whole test process hung, and downstream test files (including this one
|
|
105
|
-
// when run in the same invocation) looked flaky when they were simply
|
|
106
|
-
// never reached. The handoff sampler now yields via `setImmediate`, which
|
|
107
|
-
// unblocks Bun.build completion and makes `bun test src/bundler/` finish
|
|
108
|
-
// deterministically in ~35s on Windows. Confirmed green 3/3 runs without
|
|
109
|
-
// the gate on 2026-04-20. If you are tempted to re-introduce the skip here,
|
|
110
|
-
// first check whether a sibling test is starving the event loop.
|
|
111
|
-
//
|
|
112
|
-
// A second, independent flake — Bun.build `AggregateError: Bundle failed`
|
|
113
|
-
// when another test file in the same invocation has imported `react` —
|
|
114
|
-
// is now sidestepped by running `buildClientBundles` in a spawned `bun`
|
|
115
|
-
// subprocess via `__tests__/build-runner.ts`. In-process retry does not
|
|
116
|
-
// recover from that one; a fresh module graph does.
|
|
117
|
-
describe("buildClientBundles vendor shims", () => {
|
|
118
|
-
test("build succeeds", () => {
|
|
119
|
-
if (!result.success) {
|
|
120
|
-
console.error("[build.test] errors:", result.errors);
|
|
121
|
-
}
|
|
122
|
-
expect(result.success).toBe(true);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("re-exports modern React 19 APIs used by islands", async () => {
|
|
126
|
-
const reactShim = await importBuiltModule(".mandu/client/_react.js");
|
|
127
|
-
const requiredExports = [
|
|
128
|
-
"Activity",
|
|
129
|
-
"__COMPILER_RUNTIME",
|
|
130
|
-
"cache",
|
|
131
|
-
"cacheSignal",
|
|
132
|
-
"startTransition",
|
|
133
|
-
"use",
|
|
134
|
-
"useActionState",
|
|
135
|
-
"useEffectEvent",
|
|
136
|
-
"useOptimistic",
|
|
137
|
-
"unstable_useCacheRefresh",
|
|
138
|
-
];
|
|
139
|
-
|
|
140
|
-
for (const exportName of requiredExports) {
|
|
141
|
-
expect(exportName in reactShim).toBe(true);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("re-exports modern react-dom and react-dom/client APIs", async () => {
|
|
146
|
-
const reactDomShim = await importBuiltModule(".mandu/client/_react-dom.js");
|
|
147
|
-
for (const exportName of [
|
|
148
|
-
"preconnect",
|
|
149
|
-
"prefetchDNS",
|
|
150
|
-
"preinit",
|
|
151
|
-
"preinitModule",
|
|
152
|
-
"preload",
|
|
153
|
-
"preloadModule",
|
|
154
|
-
"requestFormReset",
|
|
155
|
-
"unstable_batchedUpdates",
|
|
156
|
-
"useFormState",
|
|
157
|
-
"useFormStatus",
|
|
158
|
-
]) {
|
|
159
|
-
expect(exportName in reactDomShim).toBe(true);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const reactDomClientShim = await importBuiltModule(".mandu/client/_react-dom-client.js");
|
|
163
|
-
for (const exportName of ["createRoot", "hydrateRoot", "version"]) {
|
|
164
|
-
expect(exportName in reactDomClientShim).toBe(true);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test("embeds hydration guards for deferred trigger strategies", async () => {
|
|
169
|
-
const runtimeSource = await readFile(path.join(rootDir, ".mandu", "client", "_runtime.js"), "utf-8");
|
|
170
|
-
expect(runtimeSource).toContain("function resolveHydrationTarget");
|
|
171
|
-
expect(runtimeSource).toContain("function hasHydratableMarkup");
|
|
172
|
-
expect(runtimeSource).toContain("function shouldHydrateCompiledIsland");
|
|
173
|
-
expect(runtimeSource).toContain("onRecoverableError");
|
|
174
|
-
expect(runtimeSource).toContain("data-mandu-hydrating");
|
|
175
|
-
expect(runtimeSource).toContain("data-mandu-render-mode");
|
|
176
|
-
expect(runtimeSource).toContain("data-mandu-recoverable-error");
|
|
177
|
-
expect(runtimeSource).toContain("pointerdown");
|
|
178
|
-
});
|
|
179
|
-
});
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
5
|
+
|
|
6
|
+
// 모든 테스트가 하나의 빌드 결과를 공유 — 병렬 Bun.build 충돌 방지
|
|
7
|
+
let rootDir: string;
|
|
8
|
+
let result: { success: boolean; errors: string[] };
|
|
9
|
+
|
|
10
|
+
async function importBuiltModule(relativePath: string): Promise<Record<string, unknown>> {
|
|
11
|
+
const fileUrl = pathToFileURL(path.join(rootDir, relativePath)).href;
|
|
12
|
+
return import(`${fileUrl}?t=${Date.now()}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Run `buildClientBundles` in an isolated `bun` subprocess.
|
|
17
|
+
*
|
|
18
|
+
* Bun 1.3.x exhibits a deterministic `AggregateError: Bundle failed` when
|
|
19
|
+
* `buildClientBundles` is called from a test file AND the same `bun test`
|
|
20
|
+
* process has previously imported `react` / `react-dom` through any sibling
|
|
21
|
+
* test file (happens transitively through almost every `src/testing/*` or
|
|
22
|
+
* `src/runtime/*` consumer). Retrying in-process does not recover — the
|
|
23
|
+
* resolver state is sticky. A fresh subprocess has a clean module graph.
|
|
24
|
+
* See `__tests__/build-runner.ts` for the subprocess entrypoint and more
|
|
25
|
+
* background.
|
|
26
|
+
*/
|
|
27
|
+
async function runBuildInSubprocess(root: string): Promise<{
|
|
28
|
+
success: boolean;
|
|
29
|
+
errors: string[];
|
|
30
|
+
}> {
|
|
31
|
+
const runner = path.join(
|
|
32
|
+
import.meta.dir,
|
|
33
|
+
"__tests__",
|
|
34
|
+
"build-runner.ts",
|
|
35
|
+
);
|
|
36
|
+
try {
|
|
37
|
+
const proc = Bun.spawn([process.execPath, "run", runner, root], {
|
|
38
|
+
cwd: path.resolve(import.meta.dir, "..", ".."),
|
|
39
|
+
stdin: "ignore",
|
|
40
|
+
stdout: "pipe",
|
|
41
|
+
stderr: "inherit",
|
|
42
|
+
});
|
|
43
|
+
const out = await new Response(proc.stdout).text();
|
|
44
|
+
await proc.exited;
|
|
45
|
+
|
|
46
|
+
// Find the final JSON line — the runner may log Mandu dev banners
|
|
47
|
+
// (e.g. "[Mandu] DevTools …") before emitting the payload. The
|
|
48
|
+
// contract is: last non-empty line is the JSON blob.
|
|
49
|
+
const lines = out.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
50
|
+
const last = lines[lines.length - 1] ?? "";
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(last);
|
|
53
|
+
return {
|
|
54
|
+
success: parsed.success === true,
|
|
55
|
+
errors: Array.isArray(parsed.errors) ? parsed.errors : [],
|
|
56
|
+
};
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
errors: [
|
|
61
|
+
`build-runner output could not be parsed as JSON: ${String(e)}\nLast stdout line: ${last}`,
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return { success: false, errors: [`spawn failed: ${String(err)}`] };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
beforeAll(async () => {
|
|
71
|
+
rootDir = await mkdtemp(path.join(import.meta.dir, ".tmp-bundler-"));
|
|
72
|
+
|
|
73
|
+
await mkdir(path.join(rootDir, "app"), { recursive: true });
|
|
74
|
+
await writeFile(
|
|
75
|
+
path.join(rootDir, "package.json"),
|
|
76
|
+
JSON.stringify({ name: "mandu-build-test", type: "module" }, null, 2),
|
|
77
|
+
"utf-8",
|
|
78
|
+
);
|
|
79
|
+
await writeFile(
|
|
80
|
+
path.join(rootDir, "app", "demo.client.tsx"),
|
|
81
|
+
"export default function DemoIsland() { return null; }\n",
|
|
82
|
+
"utf-8",
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
result = await runBuildInSubprocess(rootDir);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterAll(async () => {
|
|
89
|
+
if (rootDir) {
|
|
90
|
+
await rm(rootDir, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Historical note — `MANDU_SKIP_BUNDLER_TESTS` gate REMOVED.
|
|
95
|
+
//
|
|
96
|
+
// A previous revision gated this describe block behind
|
|
97
|
+
// `describe.skipIf(MANDU_SKIP_BUNDLER_TESTS === "1")` because running
|
|
98
|
+
// `bun test src/bundler/` without the gate hung indefinitely on Windows
|
|
99
|
+
// (see Phase 0.6 and `docs/qa/wave-R2-integration-report.md`). Root cause
|
|
100
|
+
// was NOT actually in THIS file — it was a deadlock in `safe-build.test.ts`'s
|
|
101
|
+
// "slot handoff" regression test, which drove Bun's microtask queue with a
|
|
102
|
+
// `while (!stop) { await Promise.resolve() }` sampler. That starved libuv
|
|
103
|
+
// I/O callbacks, so the 7 parallel `safeBuild()` calls never completed, the
|
|
104
|
+
// whole test process hung, and downstream test files (including this one
|
|
105
|
+
// when run in the same invocation) looked flaky when they were simply
|
|
106
|
+
// never reached. The handoff sampler now yields via `setImmediate`, which
|
|
107
|
+
// unblocks Bun.build completion and makes `bun test src/bundler/` finish
|
|
108
|
+
// deterministically in ~35s on Windows. Confirmed green 3/3 runs without
|
|
109
|
+
// the gate on 2026-04-20. If you are tempted to re-introduce the skip here,
|
|
110
|
+
// first check whether a sibling test is starving the event loop.
|
|
111
|
+
//
|
|
112
|
+
// A second, independent flake — Bun.build `AggregateError: Bundle failed`
|
|
113
|
+
// when another test file in the same invocation has imported `react` —
|
|
114
|
+
// is now sidestepped by running `buildClientBundles` in a spawned `bun`
|
|
115
|
+
// subprocess via `__tests__/build-runner.ts`. In-process retry does not
|
|
116
|
+
// recover from that one; a fresh module graph does.
|
|
117
|
+
describe("buildClientBundles vendor shims", () => {
|
|
118
|
+
test("build succeeds", () => {
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
console.error("[build.test] errors:", result.errors);
|
|
121
|
+
}
|
|
122
|
+
expect(result.success).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("re-exports modern React 19 APIs used by islands", async () => {
|
|
126
|
+
const reactShim = await importBuiltModule(".mandu/client/_react.js");
|
|
127
|
+
const requiredExports = [
|
|
128
|
+
"Activity",
|
|
129
|
+
"__COMPILER_RUNTIME",
|
|
130
|
+
"cache",
|
|
131
|
+
"cacheSignal",
|
|
132
|
+
"startTransition",
|
|
133
|
+
"use",
|
|
134
|
+
"useActionState",
|
|
135
|
+
"useEffectEvent",
|
|
136
|
+
"useOptimistic",
|
|
137
|
+
"unstable_useCacheRefresh",
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
for (const exportName of requiredExports) {
|
|
141
|
+
expect(exportName in reactShim).toBe(true);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("re-exports modern react-dom and react-dom/client APIs", async () => {
|
|
146
|
+
const reactDomShim = await importBuiltModule(".mandu/client/_react-dom.js");
|
|
147
|
+
for (const exportName of [
|
|
148
|
+
"preconnect",
|
|
149
|
+
"prefetchDNS",
|
|
150
|
+
"preinit",
|
|
151
|
+
"preinitModule",
|
|
152
|
+
"preload",
|
|
153
|
+
"preloadModule",
|
|
154
|
+
"requestFormReset",
|
|
155
|
+
"unstable_batchedUpdates",
|
|
156
|
+
"useFormState",
|
|
157
|
+
"useFormStatus",
|
|
158
|
+
]) {
|
|
159
|
+
expect(exportName in reactDomShim).toBe(true);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const reactDomClientShim = await importBuiltModule(".mandu/client/_react-dom-client.js");
|
|
163
|
+
for (const exportName of ["createRoot", "hydrateRoot", "version"]) {
|
|
164
|
+
expect(exportName in reactDomClientShim).toBe(true);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("embeds hydration guards for deferred trigger strategies", async () => {
|
|
169
|
+
const runtimeSource = await readFile(path.join(rootDir, ".mandu", "client", "_runtime.js"), "utf-8");
|
|
170
|
+
expect(runtimeSource).toContain("function resolveHydrationTarget");
|
|
171
|
+
expect(runtimeSource).toContain("function hasHydratableMarkup");
|
|
172
|
+
expect(runtimeSource).toContain("function shouldHydrateCompiledIsland");
|
|
173
|
+
expect(runtimeSource).toContain("onRecoverableError");
|
|
174
|
+
expect(runtimeSource).toContain("data-mandu-hydrating");
|
|
175
|
+
expect(runtimeSource).toContain("data-mandu-render-mode");
|
|
176
|
+
expect(runtimeSource).toContain("data-mandu-recoverable-error");
|
|
177
|
+
expect(runtimeSource).toContain("pointerdown");
|
|
178
|
+
});
|
|
179
|
+
});
|
package/src/bundler/build.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type * as __ManduPluginsReactCompilerTypes0 from "./plugins/react-compiler";
|
|
1
|
+
import type * as __ManduPluginsReactCompilerTypes0 from "./plugins/react-compiler";
|
|
2
2
|
/**
|
|
3
3
|
* Mandu Client Bundler 📦
|
|
4
4
|
* Bun.build 기반 클라이언트 번들 빌드
|
|
@@ -65,9 +65,9 @@ function manduDefaultPlugins(options: BundlerOptions): BunPlugin[] {
|
|
|
65
65
|
* this; SSR builds use `manduDefaultPlugins()` directly because React
|
|
66
66
|
* Compiler offers zero benefit for one-shot HTML rendering.
|
|
67
67
|
*/
|
|
68
|
-
function manduClientPlugins(options: BundlerOptions): BunPlugin[] {
|
|
69
|
-
const base = manduDefaultPlugins(options);
|
|
70
|
-
if (options.reactCompiler?.enabled !== true) return base;
|
|
68
|
+
function manduClientPlugins(options: BundlerOptions): BunPlugin[] {
|
|
69
|
+
const base = manduDefaultPlugins(options);
|
|
70
|
+
if (options.reactCompiler?.enabled !== true) return base;
|
|
71
71
|
// Lazy import to avoid pulling the react-compiler module into every
|
|
72
72
|
// build graph — SSR / non-client paths never touch this branch.
|
|
73
73
|
const { reactCompiler } = require("./plugins/react-compiler") as typeof __ManduPluginsReactCompilerTypes0;
|
|
@@ -75,41 +75,41 @@ function manduClientPlugins(options: BundlerOptions): BunPlugin[] {
|
|
|
75
75
|
...base,
|
|
76
76
|
reactCompiler({
|
|
77
77
|
reactCompilerConfig: options.reactCompiler.compilerConfig,
|
|
78
|
-
}),
|
|
79
|
-
];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function resolveBundlerMode(options: BundlerOptions): "development" | "production" {
|
|
83
|
-
return options.mode ?? (process.env.NODE_ENV === "production" ? "production" : "development");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function isDevelopmentBuild(options: BundlerOptions): boolean {
|
|
87
|
-
return resolveBundlerMode(options) === "development";
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function shouldMinify(options: BundlerOptions): boolean {
|
|
91
|
-
return options.minify ?? (resolveBundlerMode(options) === "production");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function shouldSplitChunks(options: BundlerOptions): boolean {
|
|
95
|
-
return options.splitting ?? (resolveBundlerMode(options) === "production");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function nodeEnvDefine(options: BundlerOptions): string {
|
|
99
|
-
return JSON.stringify(resolveBundlerMode(options));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function resolveClientOutDir(rootDir: string, outDir?: string): string {
|
|
103
|
-
const defaultOutDir = path.join(rootDir, ".mandu/client");
|
|
104
|
-
if (!outDir) return defaultOutDir;
|
|
105
|
-
|
|
106
|
-
const resolvedOutDir = path.isAbsolute(outDir) ? outDir : path.resolve(rootDir, outDir);
|
|
107
|
-
if (path.normalize(resolvedOutDir) === path.normalize(path.join(rootDir, ".mandu"))) {
|
|
108
|
-
return defaultOutDir;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return resolvedOutDir;
|
|
112
|
-
}
|
|
78
|
+
}),
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveBundlerMode(options: BundlerOptions): "development" | "production" {
|
|
83
|
+
return options.mode ?? (process.env.NODE_ENV === "production" ? "production" : "development");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isDevelopmentBuild(options: BundlerOptions): boolean {
|
|
87
|
+
return resolveBundlerMode(options) === "development";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function shouldMinify(options: BundlerOptions): boolean {
|
|
91
|
+
return options.minify ?? (resolveBundlerMode(options) === "production");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function shouldSplitChunks(options: BundlerOptions): boolean {
|
|
95
|
+
return options.splitting ?? (resolveBundlerMode(options) === "production");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function nodeEnvDefine(options: BundlerOptions): string {
|
|
99
|
+
return JSON.stringify(resolveBundlerMode(options));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function resolveClientOutDir(rootDir: string, outDir?: string): string {
|
|
103
|
+
const defaultOutDir = path.join(rootDir, ".mandu/client");
|
|
104
|
+
if (!outDir) return defaultOutDir;
|
|
105
|
+
|
|
106
|
+
const resolvedOutDir = path.isAbsolute(outDir) ? outDir : path.resolve(rootDir, outDir);
|
|
107
|
+
if (path.normalize(resolvedOutDir) === path.normalize(path.join(rootDir, ".mandu"))) {
|
|
108
|
+
return defaultOutDir;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return resolvedOutDir;
|
|
112
|
+
}
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* Scan for *.island.tsx / *.island.ts files across hydrated route directories.
|
|
@@ -262,20 +262,20 @@ async function buildPerIslandBundle(
|
|
|
262
262
|
// Phase 7.1 B-1/B-4: wire Bun's native React Fast Refresh transform +
|
|
263
263
|
// Mandu's boundary injection plugin — but only in dev. Production
|
|
264
264
|
// bundles stay clean of `$RefreshReg$` / `$RefreshSig$` stubs.
|
|
265
|
-
const isDev = isDevelopmentBuild(options);
|
|
265
|
+
const isDev = isDevelopmentBuild(options);
|
|
266
266
|
try {
|
|
267
267
|
await Bun.write(entryPath, generateIslandEntry(entry.name, entry.filePath));
|
|
268
268
|
const result = await safeBuild({
|
|
269
269
|
entrypoints: [entryPath],
|
|
270
270
|
outdir: outDir,
|
|
271
271
|
naming: outputName,
|
|
272
|
-
minify: shouldMinify(options),
|
|
272
|
+
minify: shouldMinify(options),
|
|
273
273
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
274
274
|
target: "browser",
|
|
275
275
|
...(isDev ? { reactFastRefresh: true } : {}),
|
|
276
276
|
plugins: [...manduClientPlugins(options), ...(isDev ? [fastRefreshPlugin()] : [])],
|
|
277
277
|
external: ["react", "react-dom", "react-dom/client", ...(options.external || [])],
|
|
278
|
-
define: { "process.env.NODE_ENV": nodeEnvDefine(options), ...options.define },
|
|
278
|
+
define: { "process.env.NODE_ENV": nodeEnvDefine(options), ...options.define },
|
|
279
279
|
});
|
|
280
280
|
await fs.unlink(entryPath).catch(() => {});
|
|
281
281
|
if (!result.success) {
|
|
@@ -1267,12 +1267,12 @@ async function buildRouterRuntime(
|
|
|
1267
1267
|
entrypoints: [routerPath],
|
|
1268
1268
|
outdir: outDir,
|
|
1269
1269
|
naming: outputName,
|
|
1270
|
-
minify: shouldMinify(options),
|
|
1270
|
+
minify: shouldMinify(options),
|
|
1271
1271
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1272
1272
|
target: "browser",
|
|
1273
1273
|
plugins: manduDefaultPlugins(options),
|
|
1274
1274
|
define: {
|
|
1275
|
-
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1275
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1276
1276
|
...options.define,
|
|
1277
1277
|
},
|
|
1278
1278
|
});
|
|
@@ -1343,13 +1343,13 @@ async function buildRuntime(
|
|
|
1343
1343
|
entrypoints: [runtimePath],
|
|
1344
1344
|
outdir: outDir,
|
|
1345
1345
|
naming: outputName,
|
|
1346
|
-
minify: shouldMinify(options),
|
|
1346
|
+
minify: shouldMinify(options),
|
|
1347
1347
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1348
1348
|
target: "browser",
|
|
1349
1349
|
external: ["react", "react-dom", "react-dom/client"],
|
|
1350
1350
|
plugins: manduDefaultPlugins(options),
|
|
1351
1351
|
define: {
|
|
1352
|
-
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1352
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1353
1353
|
...options.define,
|
|
1354
1354
|
},
|
|
1355
1355
|
});
|
|
@@ -1504,7 +1504,7 @@ async function buildVendorShims(
|
|
|
1504
1504
|
// Phase 7.1 B-2: dev-only Fast Refresh shims. In production we skip
|
|
1505
1505
|
// them entirely so `react-refresh/runtime` is never bundled and the
|
|
1506
1506
|
// attack surface / bundle size regressions stay zero for deploys.
|
|
1507
|
-
const isDev = isDevelopmentBuild(options);
|
|
1507
|
+
const isDev = isDevelopmentBuild(options);
|
|
1508
1508
|
|
|
1509
1509
|
const shims: Array<{ name: string; source: string; key: VendorShimKey; cacheId: string }> = [
|
|
1510
1510
|
{ name: "_react", source: generateReactShimSource(), key: "react", cacheId: "react" },
|
|
@@ -1632,13 +1632,13 @@ async function buildVendorShims(
|
|
|
1632
1632
|
entrypoints: [srcPath],
|
|
1633
1633
|
outdir: outDir,
|
|
1634
1634
|
naming: outputName,
|
|
1635
|
-
minify: shouldMinify(options),
|
|
1635
|
+
minify: shouldMinify(options),
|
|
1636
1636
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1637
1637
|
target: "browser",
|
|
1638
1638
|
external: shimExternal,
|
|
1639
1639
|
plugins: manduDefaultPlugins(options),
|
|
1640
1640
|
define: {
|
|
1641
|
-
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1641
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1642
1642
|
...options.define,
|
|
1643
1643
|
},
|
|
1644
1644
|
});
|
|
@@ -1728,7 +1728,7 @@ async function buildIsland(
|
|
|
1728
1728
|
|
|
1729
1729
|
// Phase 7.1 B-1/B-4: wire native Fast Refresh transform + Mandu's
|
|
1730
1730
|
// boundary injection plugin. Dev-only; prod bundles remain clean.
|
|
1731
|
-
const isDev = isDevelopmentBuild(options);
|
|
1731
|
+
const isDev = isDevelopmentBuild(options);
|
|
1732
1732
|
try {
|
|
1733
1733
|
// 엔트리 래퍼 생성
|
|
1734
1734
|
await Bun.write(entryPath, generateIslandEntry(route.id, clientModulePath));
|
|
@@ -1739,15 +1739,15 @@ async function buildIsland(
|
|
|
1739
1739
|
entrypoints: [entryPath],
|
|
1740
1740
|
outdir: outDir,
|
|
1741
1741
|
naming: options.splitting ? "[name]-[hash].js" : outputName,
|
|
1742
|
-
minify: shouldMinify(options),
|
|
1742
|
+
minify: shouldMinify(options),
|
|
1743
1743
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1744
1744
|
target: "browser",
|
|
1745
|
-
splitting: shouldSplitChunks(options),
|
|
1745
|
+
splitting: shouldSplitChunks(options),
|
|
1746
1746
|
...(isDev ? { reactFastRefresh: true } : {}),
|
|
1747
1747
|
plugins: [...manduClientPlugins(options), ...(isDev ? [fastRefreshPlugin()] : [])],
|
|
1748
1748
|
external: ["react", "react-dom", "react-dom/client", ...(options.external || [])],
|
|
1749
1749
|
define: {
|
|
1750
|
-
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1750
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1751
1751
|
...options.define,
|
|
1752
1752
|
},
|
|
1753
1753
|
});
|
|
@@ -1941,13 +1941,13 @@ export async function buildClientBundles(
|
|
|
1941
1941
|
errors.push(`onBundleComplete[${e.source}]: ${e.error.message}`);
|
|
1942
1942
|
}
|
|
1943
1943
|
};
|
|
1944
|
-
const env = resolveBundlerMode(options);
|
|
1944
|
+
const env = resolveBundlerMode(options);
|
|
1945
1945
|
|
|
1946
1946
|
// 1. Hydration이 필요한 라우트 필터링
|
|
1947
1947
|
const hydratedRoutes = getHydratedRoutes(manifest);
|
|
1948
1948
|
|
|
1949
1949
|
// 2. 출력 디렉토리 생성 (항상 필요 - 매니페스트 저장용)
|
|
1950
|
-
const outDir = resolveClientOutDir(rootDir, options.outDir);
|
|
1950
|
+
const outDir = resolveClientOutDir(rootDir, options.outDir);
|
|
1951
1951
|
await fs.mkdir(outDir, { recursive: true });
|
|
1952
1952
|
|
|
1953
1953
|
// Hydration 라우트가 없어도 빈 매니페스트를 저장해야 함
|