@mandujs/core 0.53.0 → 0.53.2
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/package.json +38 -2
- package/src/a11y/run-audit.ts +6 -6
- package/src/brain/adapters/oauth-flow.ts +1 -1
- package/src/brain/architecture/analyzer.ts +0 -2
- package/src/brain/credentials.ts +2 -2
- package/src/bundler/__tests__/build-runner.ts +13 -1
- package/src/bundler/__tests__/extended-watch.test.ts +3 -2
- package/src/bundler/__tests__/fast-refresh.test.ts +10 -12
- package/src/bundler/build.test.ts +30 -33
- package/src/bundler/build.ts +56 -27
- package/src/bundler/dev.ts +44 -39
- package/src/bundler/prerender.ts +63 -0
- package/src/bundler/safe-build.test.ts +2 -1
- package/src/bundler/types.ts +13 -6
- package/src/change/history.ts +1 -1
- package/src/change/snapshot.ts +18 -9
- package/src/client/Link.tsx +9 -9
- package/src/client/globals.ts +3 -2
- package/src/client/hooks.ts +0 -4
- package/src/client/hydrate.ts +1 -1
- package/src/client/router.ts +12 -12
- package/src/client/rpc.ts +1 -1
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-sse.ts +5 -5
- package/src/config/mcp-status.ts +0 -1
- package/src/config/validate.ts +4 -4
- package/src/config/watcher.ts +1 -1
- package/src/content/collection.ts +8 -12
- package/src/content/content-layer.ts +5 -5
- package/src/content/data-store.ts +2 -2
- package/src/content/loader-context.ts +0 -1
- package/src/content/loaders/file.ts +1 -1
- package/src/content/loaders/glob.ts +4 -4
- package/src/content/loaders/types.ts +1 -1
- package/src/content/meta-store.ts +1 -1
- package/src/content/types.ts +1 -1
- package/src/contract/define.ts +11 -12
- package/src/contract/index.ts +3 -3
- package/src/contract/protection.ts +2 -2
- package/src/contract/types.ts +1 -1
- package/src/contract/validator.ts +0 -1
- package/src/db/__tests__/db.test.ts +4 -7
- package/src/db/index.ts +138 -51
- package/src/db/migrations/index.ts +3 -0
- package/src/db/migrations/lock.ts +78 -23
- package/src/db/migrations/runner.ts +118 -101
- package/src/deploy/cache.ts +1 -0
- package/src/design/extract.ts +2 -1
- package/src/design/patch.ts +0 -1
- package/src/devtools/ai/mcp-connector.ts +24 -24
- package/src/devtools/client/components/kitchen-root.tsx +1 -1
- package/src/devtools/client/components/panel/errors-panel.tsx +1 -1
- package/src/devtools/client/state-manager.ts +1 -1
- package/src/devtools/init.ts +2 -3
- package/src/devtools/server/source-context.ts +1 -1
- package/src/devtools/worker/redaction-worker.ts +8 -4
- package/src/devtools/worker/worker-manager.ts +4 -3
- package/src/error/classifier.ts +1 -1
- package/src/error/domains.ts +1 -1
- package/src/filling/sse.ts +5 -5
- package/src/generator/generate.ts +2 -2
- package/src/guard/analyzer.ts +1 -1
- package/src/guard/ast-analyzer.ts +7 -7
- package/src/guard/auto-correct.ts +2 -2
- package/src/guard/config-guard.ts +0 -2
- package/src/guard/contract-guard.ts +1 -1
- package/src/guard/decision-memory.ts +2 -2
- package/src/guard/design-inline-class.ts +2 -1
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -41
- package/src/guard/negotiation.ts +3 -3
- package/src/guard/reporter.ts +1 -4
- package/src/guard/semantic-slots.ts +2 -2
- package/src/guard/suggestions.ts +2 -2
- package/src/guard/watcher.ts +5 -4
- package/src/index.ts +5 -5
- package/src/intent/index.ts +1 -1
- package/src/kitchen/api/agent-devtools-api.ts +544 -0
- package/src/kitchen/api/contract-api.ts +1 -1
- package/src/kitchen/api/file-api.ts +3 -3
- package/src/kitchen/kitchen-handler.ts +63 -38
- package/src/kitchen/kitchen-ui.ts +346 -62
- package/src/lockfile/validate.ts +0 -1
- package/src/openapi/generator.ts +2 -1
- package/src/openapi/openapi.test.ts +43 -14
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/registry.ts +1 -2
- package/src/plugins/types.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +32 -15
- package/src/resource/ddl/__tests__/emit.test.ts +24 -0
- package/src/resource/ddl/diff.ts +0 -1
- package/src/resource/ddl/emit.ts +12 -1
- package/src/resource/generator-repo.ts +40 -20
- package/src/resource/generator.ts +1 -1
- package/src/resource/generators/contract.ts +1 -1
- package/src/resource/schema.ts +2 -1
- package/src/router/fs-routes.ts +6 -6
- package/src/router/fs-scanner.ts +1 -1
- package/src/runtime/env.ts +1 -1
- package/src/runtime/image-handler.ts +2 -1
- package/src/runtime/middleware.ts +2 -1
- package/src/runtime/server.ts +67 -60
- package/src/runtime/ssr.ts +3 -3
- package/src/runtime/streaming-ssr.ts +25 -27
- package/src/scheduler/validate.ts +1 -1
- package/src/seo/integration/ssr.ts +2 -3
- package/src/seo/render/opengraph.ts +1 -1
- package/src/seo/resolve/index.ts +0 -4
- package/src/slot/corrector.ts +1 -1
- package/src/slot/validator.ts +4 -4
- package/src/watcher/reporter.ts +1 -1
- package/src/watcher/watcher.ts +19 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mandujs/core",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.2",
|
|
4
4
|
"description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -24,19 +24,36 @@
|
|
|
24
24
|
"./content/slug": "./src/content/slug.ts",
|
|
25
25
|
"./content/llms-txt": "./src/content/llms-txt.ts",
|
|
26
26
|
"./content/schema": "./src/content/schema.ts",
|
|
27
|
+
"./config": "./src/config/index.ts",
|
|
28
|
+
"./config/mandu": "./src/config/mandu.ts",
|
|
29
|
+
"./config/validate": "./src/config/validate.ts",
|
|
27
30
|
"./db": "./src/db/index.ts",
|
|
31
|
+
"./db/migrations": "./src/db/migrations/index.ts",
|
|
32
|
+
"./db/migrations/history-table": "./src/db/migrations/history-table.ts",
|
|
33
|
+
"./db/migrations/runner": "./src/db/migrations/runner.ts",
|
|
28
34
|
"./deploy": "./src/deploy/index.ts",
|
|
29
35
|
"./design": "./src/design/index.ts",
|
|
30
36
|
"./desktop": "./src/desktop/index.ts",
|
|
31
37
|
"./desktop/worker": "./src/desktop/worker.ts",
|
|
32
38
|
"./email": "./src/email/index.ts",
|
|
39
|
+
"./filling": "./src/filling/index.ts",
|
|
40
|
+
"./filling/cookie-codec": "./src/filling/cookie-codec.ts",
|
|
33
41
|
"./filling/session-sqlite": "./src/filling/session-sqlite.ts",
|
|
34
42
|
"./kitchen": "./src/kitchen/index.ts",
|
|
43
|
+
"./logging": "./src/logging/index.ts",
|
|
35
44
|
"./middleware": "./src/middleware/index.ts",
|
|
45
|
+
"./middleware/csrf": "./src/middleware/csrf.ts",
|
|
36
46
|
"./middleware/oauth": "./src/middleware/oauth/index.ts",
|
|
37
47
|
"./middleware/secure": "./src/middleware/secure/index.ts",
|
|
38
48
|
"./middleware/rate-limit": "./src/middleware/rate-limit/index.ts",
|
|
49
|
+
"./middleware/session": "./src/middleware/session.ts",
|
|
39
50
|
"./testing": "./src/testing/index.ts",
|
|
51
|
+
"./testing/db": "./src/testing/db.ts",
|
|
52
|
+
"./testing/mocks": "./src/testing/mocks.ts",
|
|
53
|
+
"./testing/reporter": "./src/testing/reporter.ts",
|
|
54
|
+
"./testing/server": "./src/testing/server.ts",
|
|
55
|
+
"./testing/session": "./src/testing/session.ts",
|
|
56
|
+
"./testing/snapshot": "./src/testing/snapshot.ts",
|
|
40
57
|
"./plugins": "./src/plugins/index.ts",
|
|
41
58
|
"./plugins/define": "./src/plugins/define.ts",
|
|
42
59
|
"./plugins/runner": "./src/plugins/runner.ts",
|
|
@@ -44,17 +61,36 @@
|
|
|
44
61
|
"./i18n": "./src/i18n/index.ts",
|
|
45
62
|
"./id": "./src/id/index.ts",
|
|
46
63
|
"./observability": "./src/observability/index.ts",
|
|
64
|
+
"./openapi/generator": "./src/openapi/generator.ts",
|
|
47
65
|
"./perf": "./src/perf/index.ts",
|
|
48
66
|
"./perf/hmr-markers": "./src/perf/hmr-markers.ts",
|
|
49
67
|
"./perf/user-marks": "./src/perf/user-marks.ts",
|
|
68
|
+
"./resource": "./src/resource/index.ts",
|
|
69
|
+
"./resource/ddl/diff": "./src/resource/ddl/diff.ts",
|
|
70
|
+
"./resource/ddl/emit": "./src/resource/ddl/emit.ts",
|
|
71
|
+
"./resource/ddl/snapshot": "./src/resource/ddl/snapshot.ts",
|
|
72
|
+
"./resource/ddl/type-map": "./src/resource/ddl/type-map.ts",
|
|
73
|
+
"./resource/ddl/types": "./src/resource/ddl/types.ts",
|
|
74
|
+
"./resource/generator-repo": "./src/resource/generator-repo.ts",
|
|
75
|
+
"./resource/parser": "./src/resource/parser.ts",
|
|
76
|
+
"./resource/schema": "./src/resource/schema.ts",
|
|
77
|
+
"./router": "./src/router/index.ts",
|
|
50
78
|
"./routes": "./src/routes/index.ts",
|
|
79
|
+
"./runtime": "./src/runtime/index.ts",
|
|
80
|
+
"./runtime/cache": "./src/runtime/cache.ts",
|
|
81
|
+
"./runtime/router": "./src/runtime/router.ts",
|
|
82
|
+
"./runtime/server": "./src/runtime/server.ts",
|
|
51
83
|
"./scheduler": "./src/scheduler/index.ts",
|
|
52
84
|
"./storage/s3": "./src/storage/s3/index.ts",
|
|
85
|
+
"./guard": "./src/guard/index.ts",
|
|
53
86
|
"./guard/define-rule": "./src/guard/define-rule.ts",
|
|
87
|
+
"./guard/design-inline-class": "./src/guard/design-inline-class.ts",
|
|
54
88
|
"./guard/rule-presets": "./src/guard/rule-presets.ts",
|
|
55
89
|
"./guard/tsgolint-bridge": "./src/guard/tsgolint-bridge.ts",
|
|
90
|
+
"./bundler": "./src/bundler/index.ts",
|
|
56
91
|
"./bundler/prerender": "./src/bundler/prerender.ts",
|
|
57
92
|
"./bundler/safe-build": "./src/bundler/safe-build.ts",
|
|
93
|
+
"./bundler/types": "./src/bundler/types.ts",
|
|
58
94
|
"./bundler/hmr-types": "./src/bundler/hmr-types.ts",
|
|
59
95
|
"./bundler/scenario-matrix": "./src/bundler/scenario-matrix.ts",
|
|
60
96
|
"./bundler/vendor-cache-types": "./src/bundler/vendor-cache-types.ts",
|
|
@@ -71,7 +107,7 @@
|
|
|
71
107
|
"./middleware/bridge": "./src/middleware/bridge.ts",
|
|
72
108
|
"./client/hydrate": "./src/client/hydrate.ts",
|
|
73
109
|
"./runtime/fast-refresh-types": "./src/runtime/fast-refresh-types.ts",
|
|
74
|
-
"
|
|
110
|
+
"./components/Image": "./src/components/Image.tsx"
|
|
75
111
|
},
|
|
76
112
|
"files": [
|
|
77
113
|
"src/**/*"
|
package/src/a11y/run-audit.ts
CHANGED
|
@@ -66,7 +66,7 @@ interface AxeRunResult {
|
|
|
66
66
|
/** Minimal structural typing for the DOM provider handle. */
|
|
67
67
|
interface DomProvider {
|
|
68
68
|
kind: "jsdom" | "happy-dom";
|
|
69
|
-
fromHtml(html: string, url: string): Promise<{ window: unknown; dispose: () => void }>;
|
|
69
|
+
fromHtml(html: string, url: string): Promise<{ window: unknown; dispose: () => Promise<void> }>;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const DEFAULT_MAX_FILES = 500;
|
|
@@ -139,7 +139,7 @@ async function resolveDomProvider(options: RunAuditOptions): Promise<DomProvider
|
|
|
139
139
|
const window = (instance as { window: unknown }).window;
|
|
140
140
|
return {
|
|
141
141
|
window,
|
|
142
|
-
dispose() {
|
|
142
|
+
async dispose() {
|
|
143
143
|
const w = window as { close?: () => void };
|
|
144
144
|
if (typeof w.close === "function") {
|
|
145
145
|
try { w.close(); } catch { /* no-op */ }
|
|
@@ -170,9 +170,9 @@ async function resolveDomProvider(options: RunAuditOptions): Promise<DomProvider
|
|
|
170
170
|
window.document.close();
|
|
171
171
|
return {
|
|
172
172
|
window,
|
|
173
|
-
dispose() {
|
|
173
|
+
async dispose() {
|
|
174
174
|
if (typeof window.close === "function") {
|
|
175
|
-
try { window.close(); } catch { /* no-op */ }
|
|
175
|
+
try { await window.close(); } catch { /* no-op */ }
|
|
176
176
|
}
|
|
177
177
|
},
|
|
178
178
|
};
|
|
@@ -235,7 +235,7 @@ async function auditFile(
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
const pageUrl = "file://" + absFile.replace(/\\/g, "/");
|
|
238
|
-
let handle: { window: unknown; dispose: () => void };
|
|
238
|
+
let handle: { window: unknown; dispose: () => void | Promise<void> };
|
|
239
239
|
try {
|
|
240
240
|
handle = await dom.fromHtml(html, pageUrl);
|
|
241
241
|
} catch {
|
|
@@ -265,7 +265,7 @@ async function auditFile(
|
|
|
265
265
|
} catch {
|
|
266
266
|
return null;
|
|
267
267
|
} finally {
|
|
268
|
-
handle.dispose();
|
|
268
|
+
await handle.dispose();
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
|
package/src/brain/credentials.ts
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* expires_at) — `CredentialStore` serializes it as a JSON string.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
import { promises as fs
|
|
31
|
+
import { promises as fs } from "node:fs";
|
|
32
32
|
import os from "node:os";
|
|
33
33
|
import path from "node:path";
|
|
34
34
|
|
|
@@ -157,7 +157,7 @@ async function runWithStdin(
|
|
|
157
157
|
stdout: "pipe",
|
|
158
158
|
stderr: "pipe",
|
|
159
159
|
});
|
|
160
|
-
proc.stdin.write(stdin);
|
|
160
|
+
await proc.stdin.write(stdin);
|
|
161
161
|
await proc.stdin.end();
|
|
162
162
|
const [stdout, exitCode] = await Promise.all([
|
|
163
163
|
new Response(proc.stdout).text(),
|
|
@@ -30,7 +30,15 @@
|
|
|
30
30
|
* - stdout: JSON blob terminated by `\n`:
|
|
31
31
|
* { "success": boolean,
|
|
32
32
|
* "errors": string[],
|
|
33
|
-
* "manifest": {
|
|
33
|
+
* "manifest": {
|
|
34
|
+
* shared: {
|
|
35
|
+
* runtime?: string;
|
|
36
|
+
* vendor?: string;
|
|
37
|
+
* router?: string;
|
|
38
|
+
* fastRefresh?: { runtime: string; glue: string };
|
|
39
|
+
* };
|
|
40
|
+
* bundles: Record<string, unknown>;
|
|
41
|
+
* }
|
|
34
42
|
* }
|
|
35
43
|
* Only the fields tests consume are serialized; the in-memory manifest
|
|
36
44
|
* is also persisted at `<rootDir>/.mandu/manifest.json` by the build.
|
|
@@ -83,8 +91,12 @@ try {
|
|
|
83
91
|
errors: result.errors,
|
|
84
92
|
manifest: {
|
|
85
93
|
shared: {
|
|
94
|
+
runtime: result.manifest.shared?.runtime ?? null,
|
|
95
|
+
vendor: result.manifest.shared?.vendor ?? null,
|
|
96
|
+
router: result.manifest.shared?.router ?? null,
|
|
86
97
|
fastRefresh: result.manifest.shared?.fastRefresh ?? null,
|
|
87
98
|
},
|
|
99
|
+
bundles: result.manifest.bundles ?? {},
|
|
88
100
|
},
|
|
89
101
|
}) + "\n",
|
|
90
102
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type * as __ManduHmrTypesTypes0 from "../hmr-types";
|
|
1
2
|
/**
|
|
2
3
|
* Phase 7.0 R2 Agent D — Extended file watch tests.
|
|
3
4
|
*
|
|
@@ -702,8 +703,8 @@ describe("Phase 7.0 R2 Agent D — CoalescedChange kind surface", () => {
|
|
|
702
703
|
|
|
703
704
|
// Literal assignments — if the union shrinks, TS errors in this
|
|
704
705
|
// file before the suite even runs.
|
|
705
|
-
const a:
|
|
706
|
-
const b:
|
|
706
|
+
const a: __ManduHmrTypesTypes0.CoalescedChange["kind"] = "config-reload";
|
|
707
|
+
const b: __ManduHmrTypesTypes0.CoalescedChange["kind"] = "resource-regen";
|
|
707
708
|
expect(a).toBe("config-reload");
|
|
708
709
|
expect(b).toBe("resource-regen");
|
|
709
710
|
});
|
|
@@ -541,18 +541,16 @@ describe.skipIf(process.env.MANDU_SKIP_BUNDLER_TESTS === "1")(
|
|
|
541
541
|
// 7-parallel shim fan-out fail with `AggregateError: Bundle failed`
|
|
542
542
|
// ~100 % of the time for the affected shim(s). A subprocess has a
|
|
543
543
|
// clean module graph.
|
|
544
|
-
const
|
|
545
|
-
const
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
proc.on("close", () => resolve(buf));
|
|
555
|
-
});
|
|
544
|
+
const runner = path.join(import.meta.dir, "build-runner.ts");
|
|
545
|
+
const cwd = path.resolve(import.meta.dir, "..", "..", "..");
|
|
546
|
+
const proc = Bun.spawn([process.execPath, "run", runner, rootDir], {
|
|
547
|
+
cwd,
|
|
548
|
+
stdin: "ignore",
|
|
549
|
+
stdout: "pipe",
|
|
550
|
+
stderr: "inherit",
|
|
551
|
+
});
|
|
552
|
+
const out = await new Response(proc.stdout).text();
|
|
553
|
+
await proc.exited;
|
|
556
554
|
const jsonLine =
|
|
557
555
|
out
|
|
558
556
|
.split(/\r?\n/)
|
|
@@ -2,7 +2,6 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
|
2
2
|
import { mkdtemp, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { pathToFileURL } from "url";
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
6
5
|
|
|
7
6
|
// 모든 테스트가 하나의 빌드 결과를 공유 — 병렬 Bun.build 충돌 방지
|
|
8
7
|
let rootDir: string;
|
|
@@ -34,40 +33,38 @@ async function runBuildInSubprocess(root: string): Promise<{
|
|
|
34
33
|
"__tests__",
|
|
35
34
|
"build-runner.ts",
|
|
36
35
|
);
|
|
37
|
-
|
|
38
|
-
const proc = spawn(process.execPath,
|
|
36
|
+
try {
|
|
37
|
+
const proc = Bun.spawn([process.execPath, "run", runner, root], {
|
|
39
38
|
cwd: path.resolve(import.meta.dir, "..", ".."),
|
|
40
|
-
|
|
39
|
+
stdin: "ignore",
|
|
40
|
+
stdout: "pipe",
|
|
41
|
+
stderr: "inherit",
|
|
41
42
|
});
|
|
42
|
-
|
|
43
|
-
proc.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
proc.on("error", (err) => {
|
|
68
|
-
resolve({ success: false, errors: [`spawn failed: ${String(err)}`] });
|
|
69
|
-
});
|
|
70
|
-
});
|
|
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
|
+
}
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
beforeAll(async () => {
|
package/src/bundler/build.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type * as __ManduPluginsReactCompilerTypes0 from "./plugins/react-compiler";
|
|
1
2
|
/**
|
|
2
3
|
* Mandu Client Bundler 📦
|
|
3
4
|
* Bun.build 기반 클라이언트 번들 빌드
|
|
@@ -64,19 +65,51 @@ function manduDefaultPlugins(options: BundlerOptions): BunPlugin[] {
|
|
|
64
65
|
* this; SSR builds use `manduDefaultPlugins()` directly because React
|
|
65
66
|
* Compiler offers zero benefit for one-shot HTML rendering.
|
|
66
67
|
*/
|
|
67
|
-
function manduClientPlugins(options: BundlerOptions): BunPlugin[] {
|
|
68
|
-
const base = manduDefaultPlugins(options);
|
|
69
|
-
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;
|
|
70
71
|
// Lazy import to avoid pulling the react-compiler module into every
|
|
71
72
|
// build graph — SSR / non-client paths never touch this branch.
|
|
72
|
-
const { reactCompiler } = require("./plugins/react-compiler") as typeof
|
|
73
|
+
const { reactCompiler } = require("./plugins/react-compiler") as typeof __ManduPluginsReactCompilerTypes0;
|
|
73
74
|
return [
|
|
74
75
|
...base,
|
|
75
76
|
reactCompiler({
|
|
76
77
|
reactCompilerConfig: options.reactCompiler.compilerConfig,
|
|
77
|
-
}),
|
|
78
|
-
];
|
|
79
|
-
}
|
|
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
|
+
}
|
|
80
113
|
|
|
81
114
|
/**
|
|
82
115
|
* Scan for *.island.tsx / *.island.ts files across hydrated route directories.
|
|
@@ -229,20 +262,20 @@ async function buildPerIslandBundle(
|
|
|
229
262
|
// Phase 7.1 B-1/B-4: wire Bun's native React Fast Refresh transform +
|
|
230
263
|
// Mandu's boundary injection plugin — but only in dev. Production
|
|
231
264
|
// bundles stay clean of `$RefreshReg$` / `$RefreshSig$` stubs.
|
|
232
|
-
const isDev = (options
|
|
265
|
+
const isDev = isDevelopmentBuild(options);
|
|
233
266
|
try {
|
|
234
267
|
await Bun.write(entryPath, generateIslandEntry(entry.name, entry.filePath));
|
|
235
268
|
const result = await safeBuild({
|
|
236
269
|
entrypoints: [entryPath],
|
|
237
270
|
outdir: outDir,
|
|
238
271
|
naming: outputName,
|
|
239
|
-
minify: options
|
|
272
|
+
minify: shouldMinify(options),
|
|
240
273
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
241
274
|
target: "browser",
|
|
242
275
|
...(isDev ? { reactFastRefresh: true } : {}),
|
|
243
276
|
plugins: [...manduClientPlugins(options), ...(isDev ? [fastRefreshPlugin()] : [])],
|
|
244
277
|
external: ["react", "react-dom", "react-dom/client", ...(options.external || [])],
|
|
245
|
-
define: { "process.env.NODE_ENV":
|
|
278
|
+
define: { "process.env.NODE_ENV": nodeEnvDefine(options), ...options.define },
|
|
246
279
|
});
|
|
247
280
|
await fs.unlink(entryPath).catch(() => {});
|
|
248
281
|
if (!result.success) {
|
|
@@ -1234,12 +1267,12 @@ async function buildRouterRuntime(
|
|
|
1234
1267
|
entrypoints: [routerPath],
|
|
1235
1268
|
outdir: outDir,
|
|
1236
1269
|
naming: outputName,
|
|
1237
|
-
minify: options
|
|
1270
|
+
minify: shouldMinify(options),
|
|
1238
1271
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1239
1272
|
target: "browser",
|
|
1240
1273
|
plugins: manduDefaultPlugins(options),
|
|
1241
1274
|
define: {
|
|
1242
|
-
"process.env.NODE_ENV":
|
|
1275
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1243
1276
|
...options.define,
|
|
1244
1277
|
},
|
|
1245
1278
|
});
|
|
@@ -1310,13 +1343,13 @@ async function buildRuntime(
|
|
|
1310
1343
|
entrypoints: [runtimePath],
|
|
1311
1344
|
outdir: outDir,
|
|
1312
1345
|
naming: outputName,
|
|
1313
|
-
minify: options
|
|
1346
|
+
minify: shouldMinify(options),
|
|
1314
1347
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1315
1348
|
target: "browser",
|
|
1316
1349
|
external: ["react", "react-dom", "react-dom/client"],
|
|
1317
1350
|
plugins: manduDefaultPlugins(options),
|
|
1318
1351
|
define: {
|
|
1319
|
-
"process.env.NODE_ENV":
|
|
1352
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1320
1353
|
...options.define,
|
|
1321
1354
|
},
|
|
1322
1355
|
});
|
|
@@ -1471,8 +1504,7 @@ async function buildVendorShims(
|
|
|
1471
1504
|
// Phase 7.1 B-2: dev-only Fast Refresh shims. In production we skip
|
|
1472
1505
|
// them entirely so `react-refresh/runtime` is never bundled and the
|
|
1473
1506
|
// attack surface / bundle size regressions stay zero for deploys.
|
|
1474
|
-
const isDev =
|
|
1475
|
-
(options.minify ?? process.env.NODE_ENV === "production") === false;
|
|
1507
|
+
const isDev = isDevelopmentBuild(options);
|
|
1476
1508
|
|
|
1477
1509
|
const shims: Array<{ name: string; source: string; key: VendorShimKey; cacheId: string }> = [
|
|
1478
1510
|
{ name: "_react", source: generateReactShimSource(), key: "react", cacheId: "react" },
|
|
@@ -1600,13 +1632,13 @@ async function buildVendorShims(
|
|
|
1600
1632
|
entrypoints: [srcPath],
|
|
1601
1633
|
outdir: outDir,
|
|
1602
1634
|
naming: outputName,
|
|
1603
|
-
minify: options
|
|
1635
|
+
minify: shouldMinify(options),
|
|
1604
1636
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1605
1637
|
target: "browser",
|
|
1606
1638
|
external: shimExternal,
|
|
1607
1639
|
plugins: manduDefaultPlugins(options),
|
|
1608
1640
|
define: {
|
|
1609
|
-
"process.env.NODE_ENV":
|
|
1641
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1610
1642
|
...options.define,
|
|
1611
1643
|
},
|
|
1612
1644
|
});
|
|
@@ -1696,8 +1728,7 @@ async function buildIsland(
|
|
|
1696
1728
|
|
|
1697
1729
|
// Phase 7.1 B-1/B-4: wire native Fast Refresh transform + Mandu's
|
|
1698
1730
|
// boundary injection plugin. Dev-only; prod bundles remain clean.
|
|
1699
|
-
const isDev =
|
|
1700
|
-
(options.minify ?? process.env.NODE_ENV === "production") === false;
|
|
1731
|
+
const isDev = isDevelopmentBuild(options);
|
|
1701
1732
|
try {
|
|
1702
1733
|
// 엔트리 래퍼 생성
|
|
1703
1734
|
await Bun.write(entryPath, generateIslandEntry(route.id, clientModulePath));
|
|
@@ -1708,15 +1739,15 @@ async function buildIsland(
|
|
|
1708
1739
|
entrypoints: [entryPath],
|
|
1709
1740
|
outdir: outDir,
|
|
1710
1741
|
naming: options.splitting ? "[name]-[hash].js" : outputName,
|
|
1711
|
-
minify: options
|
|
1742
|
+
minify: shouldMinify(options),
|
|
1712
1743
|
sourcemap: options.sourcemap ? "external" : "none",
|
|
1713
1744
|
target: "browser",
|
|
1714
|
-
splitting: options
|
|
1745
|
+
splitting: shouldSplitChunks(options),
|
|
1715
1746
|
...(isDev ? { reactFastRefresh: true } : {}),
|
|
1716
1747
|
plugins: [...manduClientPlugins(options), ...(isDev ? [fastRefreshPlugin()] : [])],
|
|
1717
1748
|
external: ["react", "react-dom", "react-dom/client", ...(options.external || [])],
|
|
1718
1749
|
define: {
|
|
1719
|
-
"process.env.NODE_ENV":
|
|
1750
|
+
"process.env.NODE_ENV": nodeEnvDefine(options),
|
|
1720
1751
|
...options.define,
|
|
1721
1752
|
},
|
|
1722
1753
|
});
|
|
@@ -1910,15 +1941,13 @@ export async function buildClientBundles(
|
|
|
1910
1941
|
errors.push(`onBundleComplete[${e.source}]: ${e.error.message}`);
|
|
1911
1942
|
}
|
|
1912
1943
|
};
|
|
1913
|
-
const env = (
|
|
1914
|
-
| "development"
|
|
1915
|
-
| "production";
|
|
1944
|
+
const env = resolveBundlerMode(options);
|
|
1916
1945
|
|
|
1917
1946
|
// 1. Hydration이 필요한 라우트 필터링
|
|
1918
1947
|
const hydratedRoutes = getHydratedRoutes(manifest);
|
|
1919
1948
|
|
|
1920
1949
|
// 2. 출력 디렉토리 생성 (항상 필요 - 매니페스트 저장용)
|
|
1921
|
-
const outDir =
|
|
1950
|
+
const outDir = resolveClientOutDir(rootDir, options.outDir);
|
|
1922
1951
|
await fs.mkdir(outDir, { recursive: true });
|
|
1923
1952
|
|
|
1924
1953
|
// Hydration 라우트가 없어도 빈 매니페스트를 저장해야 함
|