@mandujs/core 0.3.4 โ 0.4.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/package.json +41 -40
- package/src/bundler/build.ts +609 -0
- package/src/bundler/dev.ts +362 -0
- package/src/bundler/index.ts +8 -0
- package/src/bundler/types.ts +100 -0
- package/src/client/index.ts +68 -0
- package/src/client/island.ts +197 -0
- package/src/client/runtime.ts +335 -0
- package/src/filling/filling.ts +76 -5
- package/src/index.ts +1 -0
- package/src/runtime/server.ts +24 -3
- package/src/runtime/ssr.ts +199 -2
- package/src/spec/schema.ts +132 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Dev Bundler ๐ฅ
|
|
3
|
+
* ๊ฐ๋ฐ ๋ชจ๋ ๋ฒ๋ค๋ง + HMR (Hot Module Replacement)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { RoutesManifest, RouteSpec } from "../spec/schema";
|
|
7
|
+
import { buildClientBundles } from "./build";
|
|
8
|
+
import type { BundleResult } from "./types";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
|
|
12
|
+
export interface DevBundlerOptions {
|
|
13
|
+
/** ํ๋ก์ ํธ ๋ฃจํธ */
|
|
14
|
+
rootDir: string;
|
|
15
|
+
/** ๋ผ์ฐํธ ๋งค๋ํ์คํธ */
|
|
16
|
+
manifest: RoutesManifest;
|
|
17
|
+
/** ์ฌ๋น๋ ์ฝ๋ฐฑ */
|
|
18
|
+
onRebuild?: (result: RebuildResult) => void;
|
|
19
|
+
/** ์๋ฌ ์ฝ๋ฐฑ */
|
|
20
|
+
onError?: (error: Error, routeId?: string) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RebuildResult {
|
|
24
|
+
routeId: string;
|
|
25
|
+
success: boolean;
|
|
26
|
+
buildTime: number;
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DevBundler {
|
|
31
|
+
/** ์ด๊ธฐ ๋น๋ ๊ฒฐ๊ณผ */
|
|
32
|
+
initialBuild: BundleResult;
|
|
33
|
+
/** ํ์ผ ๊ฐ์ ์ค์ง */
|
|
34
|
+
close: () => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* ๊ฐ๋ฐ ๋ชจ๋ ๋ฒ๋ค๋ฌ ์์
|
|
39
|
+
* ํ์ผ ๋ณ๊ฒฝ ๊ฐ์ ๋ฐ ์๋ ์ฌ๋น๋
|
|
40
|
+
*/
|
|
41
|
+
export async function startDevBundler(options: DevBundlerOptions): Promise<DevBundler> {
|
|
42
|
+
const { rootDir, manifest, onRebuild, onError } = options;
|
|
43
|
+
const slotsDir = path.join(rootDir, "spec", "slots");
|
|
44
|
+
|
|
45
|
+
// ์ด๊ธฐ ๋น๋
|
|
46
|
+
console.log("๐จ Initial client bundle build...");
|
|
47
|
+
const initialBuild = await buildClientBundles(manifest, rootDir, {
|
|
48
|
+
minify: false,
|
|
49
|
+
sourcemap: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (initialBuild.success) {
|
|
53
|
+
console.log(`โ
Built ${initialBuild.stats.bundleCount} islands`);
|
|
54
|
+
} else {
|
|
55
|
+
console.error("โ ๏ธ Initial build had errors:", initialBuild.errors);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ํ์ผ ๊ฐ์ ์ค์
|
|
59
|
+
let watcher: fs.FSWatcher | null = null;
|
|
60
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await fs.promises.access(slotsDir);
|
|
64
|
+
|
|
65
|
+
watcher = fs.watch(slotsDir, { recursive: true }, async (event, filename) => {
|
|
66
|
+
if (!filename) return;
|
|
67
|
+
|
|
68
|
+
// .client.ts ํ์ผ๋ง ๊ฐ์
|
|
69
|
+
if (!filename.endsWith(".client.ts")) return;
|
|
70
|
+
|
|
71
|
+
// Debounce - ์ฐ์ ๋ณ๊ฒฝ ๋ฌด์
|
|
72
|
+
if (debounceTimer) {
|
|
73
|
+
clearTimeout(debounceTimer);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
debounceTimer = setTimeout(async () => {
|
|
77
|
+
const routeId = filename.replace(".client.ts", "").replace(/\\/g, "/").split("/").pop();
|
|
78
|
+
if (!routeId) return;
|
|
79
|
+
|
|
80
|
+
const route = manifest.routes.find((r) => r.id === routeId);
|
|
81
|
+
if (!route || !route.clientModule) return;
|
|
82
|
+
|
|
83
|
+
console.log(`\n๐ Rebuilding: ${routeId}`);
|
|
84
|
+
const startTime = performance.now();
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const result = await buildClientBundles(manifest, rootDir, {
|
|
88
|
+
minify: false,
|
|
89
|
+
sourcemap: true,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const buildTime = performance.now() - startTime;
|
|
93
|
+
|
|
94
|
+
if (result.success) {
|
|
95
|
+
console.log(`โ
Rebuilt in ${buildTime.toFixed(0)}ms`);
|
|
96
|
+
onRebuild?.({
|
|
97
|
+
routeId,
|
|
98
|
+
success: true,
|
|
99
|
+
buildTime,
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
console.error(`โ Build failed:`, result.errors);
|
|
103
|
+
onRebuild?.({
|
|
104
|
+
routeId,
|
|
105
|
+
success: false,
|
|
106
|
+
buildTime,
|
|
107
|
+
error: result.errors.join(", "),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
112
|
+
console.error(`โ Build error:`, err.message);
|
|
113
|
+
onError?.(err, routeId);
|
|
114
|
+
}
|
|
115
|
+
}, 100); // 100ms debounce
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log("๐ Watching for client slot changes...");
|
|
119
|
+
} catch {
|
|
120
|
+
console.warn(`โ ๏ธ Slots directory not found: ${slotsDir}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
initialBuild,
|
|
125
|
+
close: () => {
|
|
126
|
+
if (debounceTimer) {
|
|
127
|
+
clearTimeout(debounceTimer);
|
|
128
|
+
}
|
|
129
|
+
if (watcher) {
|
|
130
|
+
watcher.close();
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* HMR WebSocket ์๋ฒ
|
|
138
|
+
*/
|
|
139
|
+
export interface HMRServer {
|
|
140
|
+
/** ์ฐ๊ฒฐ๋ ํด๋ผ์ด์ธํธ ์ */
|
|
141
|
+
clientCount: number;
|
|
142
|
+
/** ๋ชจ๋ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์์ง ์ ์ก */
|
|
143
|
+
broadcast: (message: HMRMessage) => void;
|
|
144
|
+
/** ์๋ฒ ์ค์ง */
|
|
145
|
+
close: () => void;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface HMRMessage {
|
|
149
|
+
type: "connected" | "reload" | "island-update" | "error" | "ping";
|
|
150
|
+
data?: {
|
|
151
|
+
routeId?: string;
|
|
152
|
+
message?: string;
|
|
153
|
+
timestamp?: number;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* HMR WebSocket ์๋ฒ ์์ฑ
|
|
159
|
+
*/
|
|
160
|
+
export function createHMRServer(port: number): HMRServer {
|
|
161
|
+
const clients = new Set<any>();
|
|
162
|
+
const hmrPort = port + 1;
|
|
163
|
+
|
|
164
|
+
const server = Bun.serve({
|
|
165
|
+
port: hmrPort,
|
|
166
|
+
fetch(req, server) {
|
|
167
|
+
// WebSocket ์
๊ทธ๋ ์ด๋
|
|
168
|
+
if (server.upgrade(req)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ์ผ๋ฐ HTTP ์์ฒญ์ ์ํ ๋ฐํ
|
|
173
|
+
return new Response(
|
|
174
|
+
JSON.stringify({
|
|
175
|
+
status: "ok",
|
|
176
|
+
clients: clients.size,
|
|
177
|
+
port: hmrPort,
|
|
178
|
+
}),
|
|
179
|
+
{
|
|
180
|
+
headers: { "Content-Type": "application/json" },
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
},
|
|
184
|
+
websocket: {
|
|
185
|
+
open(ws) {
|
|
186
|
+
clients.add(ws);
|
|
187
|
+
ws.send(
|
|
188
|
+
JSON.stringify({
|
|
189
|
+
type: "connected",
|
|
190
|
+
data: { timestamp: Date.now() },
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
close(ws) {
|
|
195
|
+
clients.delete(ws);
|
|
196
|
+
},
|
|
197
|
+
message(ws, message) {
|
|
198
|
+
// ํด๋ผ์ด์ธํธ๋ก๋ถํฐ์ ping ์ฒ๋ฆฌ
|
|
199
|
+
try {
|
|
200
|
+
const data = JSON.parse(String(message));
|
|
201
|
+
if (data.type === "ping") {
|
|
202
|
+
ws.send(JSON.stringify({ type: "pong", data: { timestamp: Date.now() } }));
|
|
203
|
+
}
|
|
204
|
+
} catch {
|
|
205
|
+
// ๋ฌด์
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
console.log(`๐ฅ HMR server running on ws://localhost:${hmrPort}`);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
get clientCount() {
|
|
215
|
+
return clients.size;
|
|
216
|
+
},
|
|
217
|
+
broadcast: (message: HMRMessage) => {
|
|
218
|
+
const payload = JSON.stringify(message);
|
|
219
|
+
for (const client of clients) {
|
|
220
|
+
try {
|
|
221
|
+
client.send(payload);
|
|
222
|
+
} catch {
|
|
223
|
+
clients.delete(client);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
close: () => {
|
|
228
|
+
for (const client of clients) {
|
|
229
|
+
try {
|
|
230
|
+
client.close();
|
|
231
|
+
} catch {
|
|
232
|
+
// ๋ฌด์
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
clients.clear();
|
|
236
|
+
server.stop();
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* HMR ํด๋ผ์ด์ธํธ ์คํฌ๋ฆฝํธ ์์ฑ
|
|
243
|
+
* ๋ธ๋ผ์ฐ์ ์์ ์คํ๋์ด HMR ์๋ฒ์ ์ฐ๊ฒฐ
|
|
244
|
+
*/
|
|
245
|
+
export function generateHMRClientScript(port: number): string {
|
|
246
|
+
const hmrPort = port + 1;
|
|
247
|
+
|
|
248
|
+
return `
|
|
249
|
+
(function() {
|
|
250
|
+
const HMR_PORT = ${hmrPort};
|
|
251
|
+
let ws = null;
|
|
252
|
+
let reconnectAttempts = 0;
|
|
253
|
+
const maxReconnectAttempts = 10;
|
|
254
|
+
const reconnectDelay = 1000;
|
|
255
|
+
|
|
256
|
+
function connect() {
|
|
257
|
+
try {
|
|
258
|
+
ws = new WebSocket('ws://localhost:' + HMR_PORT);
|
|
259
|
+
|
|
260
|
+
ws.onopen = function() {
|
|
261
|
+
console.log('[Mandu HMR] Connected');
|
|
262
|
+
reconnectAttempts = 0;
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
ws.onmessage = function(event) {
|
|
266
|
+
try {
|
|
267
|
+
const message = JSON.parse(event.data);
|
|
268
|
+
handleMessage(message);
|
|
269
|
+
} catch (e) {
|
|
270
|
+
console.error('[Mandu HMR] Invalid message:', e);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
ws.onclose = function() {
|
|
275
|
+
console.log('[Mandu HMR] Disconnected');
|
|
276
|
+
scheduleReconnect();
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
ws.onerror = function(error) {
|
|
280
|
+
console.error('[Mandu HMR] Error:', error);
|
|
281
|
+
};
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error('[Mandu HMR] Connection failed:', error);
|
|
284
|
+
scheduleReconnect();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function scheduleReconnect() {
|
|
289
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
290
|
+
reconnectAttempts++;
|
|
291
|
+
console.log('[Mandu HMR] Reconnecting... (' + reconnectAttempts + '/' + maxReconnectAttempts + ')');
|
|
292
|
+
setTimeout(connect, reconnectDelay * reconnectAttempts);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function handleMessage(message) {
|
|
297
|
+
switch (message.type) {
|
|
298
|
+
case 'connected':
|
|
299
|
+
console.log('[Mandu HMR] Ready');
|
|
300
|
+
break;
|
|
301
|
+
|
|
302
|
+
case 'reload':
|
|
303
|
+
console.log('[Mandu HMR] Full reload requested');
|
|
304
|
+
location.reload();
|
|
305
|
+
break;
|
|
306
|
+
|
|
307
|
+
case 'island-update':
|
|
308
|
+
const routeId = message.data?.routeId;
|
|
309
|
+
console.log('[Mandu HMR] Island updated:', routeId);
|
|
310
|
+
|
|
311
|
+
// ํ์ฌ ํ์ด์ง์ island์ธ์ง ํ์ธ
|
|
312
|
+
const island = document.querySelector('[data-mandu-island="' + routeId + '"]');
|
|
313
|
+
if (island) {
|
|
314
|
+
console.log('[Mandu HMR] Reloading page for island update');
|
|
315
|
+
location.reload();
|
|
316
|
+
}
|
|
317
|
+
break;
|
|
318
|
+
|
|
319
|
+
case 'error':
|
|
320
|
+
console.error('[Mandu HMR] Build error:', message.data?.message);
|
|
321
|
+
showErrorOverlay(message.data?.message);
|
|
322
|
+
break;
|
|
323
|
+
|
|
324
|
+
case 'pong':
|
|
325
|
+
// ์ฐ๊ฒฐ ํ์ธ
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function showErrorOverlay(message) {
|
|
331
|
+
// ๊ธฐ์กด ์ค๋ฒ๋ ์ด ์ ๊ฑฐ
|
|
332
|
+
const existing = document.getElementById('mandu-hmr-error');
|
|
333
|
+
if (existing) existing.remove();
|
|
334
|
+
|
|
335
|
+
const overlay = document.createElement('div');
|
|
336
|
+
overlay.id = 'mandu-hmr-error';
|
|
337
|
+
overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.9);color:#ff6b6b;font-family:monospace;padding:40px;z-index:99999;overflow:auto;';
|
|
338
|
+
overlay.innerHTML = '<h2 style="color:#ff6b6b;margin:0 0 20px;">๐ฅ Build Error</h2><pre style="white-space:pre-wrap;word-break:break-all;">' + (message || 'Unknown error') + '</pre><button onclick="this.parentElement.remove()" style="position:fixed;top:20px;right:20px;background:#333;color:#fff;border:none;padding:10px 20px;cursor:pointer;">Close</button>';
|
|
339
|
+
document.body.appendChild(overlay);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ํ์ด์ง ๋ก๋ ์ ์ฐ๊ฒฐ
|
|
343
|
+
if (document.readyState === 'loading') {
|
|
344
|
+
document.addEventListener('DOMContentLoaded', connect);
|
|
345
|
+
} else {
|
|
346
|
+
connect();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ํ์ด์ง ์ดํ ์ ์ ๋ฆฌ
|
|
350
|
+
window.addEventListener('beforeunload', function() {
|
|
351
|
+
if (ws) ws.close();
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Ping ์ ์ก (์ฐ๊ฒฐ ์ ์ง)
|
|
355
|
+
setInterval(function() {
|
|
356
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
357
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
358
|
+
}
|
|
359
|
+
}, 30000);
|
|
360
|
+
})();
|
|
361
|
+
`;
|
|
362
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Bundler Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ๋ฒ๋ค ๋น๋ ๊ฒฐ๊ณผ
|
|
7
|
+
*/
|
|
8
|
+
export interface BundleResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
outputs: BundleOutput[];
|
|
11
|
+
errors: string[];
|
|
12
|
+
manifest: BundleManifest;
|
|
13
|
+
stats: BundleStats;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ๊ฐ๋ณ ๋ฒ๋ค ์ถ๋ ฅ
|
|
18
|
+
*/
|
|
19
|
+
export interface BundleOutput {
|
|
20
|
+
/** ๋ผ์ฐํธ ID */
|
|
21
|
+
routeId: string;
|
|
22
|
+
/** ์๋ณธ ์ํธ๋ฆฌํฌ์ธํธ */
|
|
23
|
+
entrypoint: string;
|
|
24
|
+
/** ์ถ๋ ฅ ๊ฒฝ๋ก (์๋ฒ ๊ธฐ์ค) */
|
|
25
|
+
outputPath: string;
|
|
26
|
+
/** ํ์ผ ํฌ๊ธฐ (bytes) */
|
|
27
|
+
size: number;
|
|
28
|
+
/** gzip ์์ถ ํฌ๊ธฐ (bytes) */
|
|
29
|
+
gzipSize: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* ๋ฒ๋ค ๋งค๋ํ์คํธ
|
|
34
|
+
*/
|
|
35
|
+
export interface BundleManifest {
|
|
36
|
+
/** ๋งค๋ํ์คํธ ๋ฒ์ */
|
|
37
|
+
version: number;
|
|
38
|
+
/** ๋น๋ ์๊ฐ */
|
|
39
|
+
buildTime: string;
|
|
40
|
+
/** ํ๊ฒฝ */
|
|
41
|
+
env: "development" | "production";
|
|
42
|
+
/** ๋ผ์ฐํธ๋ณ ๋ฒ๋ค ์ ๋ณด */
|
|
43
|
+
bundles: Record<
|
|
44
|
+
string,
|
|
45
|
+
{
|
|
46
|
+
/** JavaScript ๋ฒ๋ค ๊ฒฝ๋ก */
|
|
47
|
+
js: string;
|
|
48
|
+
/** CSS ๋ฒ๋ค ๊ฒฝ๋ก (์๋ ๊ฒฝ์ฐ) */
|
|
49
|
+
css?: string;
|
|
50
|
+
/** ์์กดํ๋ ๊ณต์ ์ฒญํฌ */
|
|
51
|
+
dependencies: string[];
|
|
52
|
+
/** Hydration ์ฐ์ ์์ */
|
|
53
|
+
priority: "immediate" | "visible" | "idle" | "interaction";
|
|
54
|
+
}
|
|
55
|
+
>;
|
|
56
|
+
/** ๊ณต์ ์ฒญํฌ */
|
|
57
|
+
shared: {
|
|
58
|
+
/** Hydration ๋ฐํ์ */
|
|
59
|
+
runtime: string;
|
|
60
|
+
/** ๋ฒค๋ ๋ฒ๋ค (React ๋ฑ) */
|
|
61
|
+
vendor: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* ๋ฒ๋ค ํต๊ณ
|
|
67
|
+
*/
|
|
68
|
+
export interface BundleStats {
|
|
69
|
+
/** ์ ์ฒด ํฌ๊ธฐ */
|
|
70
|
+
totalSize: number;
|
|
71
|
+
/** ์ ์ฒด gzip ํฌ๊ธฐ */
|
|
72
|
+
totalGzipSize: number;
|
|
73
|
+
/** ๊ฐ์ฅ ํฐ ๋ฒ๋ค */
|
|
74
|
+
largestBundle: {
|
|
75
|
+
routeId: string;
|
|
76
|
+
size: number;
|
|
77
|
+
};
|
|
78
|
+
/** ๋น๋ ์๊ฐ (ms) */
|
|
79
|
+
buildTime: number;
|
|
80
|
+
/** ๋ฒ๋ค ์ */
|
|
81
|
+
bundleCount: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* ๋ฒ๋ค๋ฌ ์ต์
|
|
86
|
+
*/
|
|
87
|
+
export interface BundlerOptions {
|
|
88
|
+
/** ์ฝ๋ ์์ถ ์ฌ๋ถ (๊ธฐ๋ณธ: production์์ true) */
|
|
89
|
+
minify?: boolean;
|
|
90
|
+
/** ์์ค๋งต ์์ฑ ์ฌ๋ถ */
|
|
91
|
+
sourcemap?: boolean;
|
|
92
|
+
/** ํ์ผ ๊ฐ์ ๋ชจ๋ */
|
|
93
|
+
watch?: boolean;
|
|
94
|
+
/** ์ถ๋ ฅ ๋๋ ํ ๋ฆฌ (๊ธฐ๋ณธ: .mandu/client) */
|
|
95
|
+
outDir?: string;
|
|
96
|
+
/** ์ธ๋ถ ๋ชจ๋ (๋ฒ๋ค์์ ์ ์ธ) */
|
|
97
|
+
external?: string[];
|
|
98
|
+
/** ํ๊ฒฝ ๋ณ์ ์ฃผ์
*/
|
|
99
|
+
define?: Record<string, string>;
|
|
100
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mandu Client Module ๐๏ธ
|
|
3
|
+
* ํด๋ผ์ด์ธํธ ์ฌ์ด๋ hydration์ ์ํ API
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* // spec/slots/todos.client.ts
|
|
8
|
+
* import { Mandu } from "@mandujs/core/client";
|
|
9
|
+
*
|
|
10
|
+
* export default Mandu.island<TodosData>({
|
|
11
|
+
* setup: (data) => { ... },
|
|
12
|
+
* render: (props) => <TodoList {...props} />
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Island API
|
|
18
|
+
export {
|
|
19
|
+
island,
|
|
20
|
+
useServerData,
|
|
21
|
+
useHydrated,
|
|
22
|
+
useIslandEvent,
|
|
23
|
+
fetchApi,
|
|
24
|
+
type IslandDefinition,
|
|
25
|
+
type IslandMetadata,
|
|
26
|
+
type CompiledIsland,
|
|
27
|
+
type FetchOptions,
|
|
28
|
+
} from "./island";
|
|
29
|
+
|
|
30
|
+
// Runtime API
|
|
31
|
+
export {
|
|
32
|
+
registerIsland,
|
|
33
|
+
getRegisteredIslands,
|
|
34
|
+
getServerData,
|
|
35
|
+
hydrateIslands,
|
|
36
|
+
getHydrationState,
|
|
37
|
+
unmountIsland,
|
|
38
|
+
unmountAllIslands,
|
|
39
|
+
initializeRuntime,
|
|
40
|
+
type IslandLoader,
|
|
41
|
+
} from "./runtime";
|
|
42
|
+
|
|
43
|
+
// Re-export as Mandu namespace for consistent API
|
|
44
|
+
import { island } from "./island";
|
|
45
|
+
import { hydrateIslands, initializeRuntime } from "./runtime";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Mandu Client namespace
|
|
49
|
+
*/
|
|
50
|
+
export const Mandu = {
|
|
51
|
+
/**
|
|
52
|
+
* Create an island component
|
|
53
|
+
* @see island
|
|
54
|
+
*/
|
|
55
|
+
island,
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Hydrate all islands on the page
|
|
59
|
+
* @see hydrateIslands
|
|
60
|
+
*/
|
|
61
|
+
hydrate: hydrateIslands,
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Initialize the hydration runtime
|
|
65
|
+
* @see initializeRuntime
|
|
66
|
+
*/
|
|
67
|
+
init: initializeRuntime,
|
|
68
|
+
};
|