@mandujs/core 0.54.7 → 0.54.8
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
CHANGED
|
@@ -183,28 +183,28 @@ export function IslandsPanel({ islands }: IslandsPanelProps): React.ReactElement
|
|
|
183
183
|
);
|
|
184
184
|
}, [islands]);
|
|
185
185
|
|
|
186
|
-
if (islands.length === 0) {
|
|
187
|
-
return (
|
|
188
|
-
<div style={styles.container}>
|
|
189
|
-
<div style={styles.emptyState}>
|
|
190
|
-
<p>
|
|
191
|
-
아직
|
|
192
|
-
|
|
193
|
-
</p>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
);
|
|
197
|
-
}
|
|
186
|
+
if (islands.length === 0) {
|
|
187
|
+
return (
|
|
188
|
+
<div style={styles.container}>
|
|
189
|
+
<div style={styles.emptyState}>
|
|
190
|
+
<p>
|
|
191
|
+
아직 hydration 이벤트가 없습니다.<br />
|
|
192
|
+
정적 상태는 mandu.runtime.status에서 확인하세요.
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
198
|
|
|
199
199
|
return (
|
|
200
200
|
<div style={styles.container}>
|
|
201
201
|
{/* Header Stats */}
|
|
202
202
|
<div style={styles.header}>
|
|
203
203
|
<div style={styles.stats}>
|
|
204
|
-
<div style={styles.stat}>
|
|
205
|
-
<span
|
|
206
|
-
<span style={styles.statValue}>{stats.total}</span>
|
|
207
|
-
</div>
|
|
204
|
+
<div style={styles.stat}>
|
|
205
|
+
<span>Mounts:</span>
|
|
206
|
+
<span style={styles.statValue}>{stats.total}</span>
|
|
207
|
+
</div>
|
|
208
208
|
<div style={styles.stat}>
|
|
209
209
|
<span>Ready</span>
|
|
210
210
|
<span style={styles.statValue}>{stats.hydrated}</span>
|
|
@@ -28,7 +28,7 @@ export interface PanelContainerProps {
|
|
|
28
28
|
|
|
29
29
|
export const TABS: TabDefinition[] = [
|
|
30
30
|
{ id: 'errors', label: 'Issues', icon: 'ERR', testId: testIds.tabErrors },
|
|
31
|
-
{ id: 'islands', label: '
|
|
31
|
+
{ id: 'islands', label: 'Client Mounts', icon: 'HYD', testId: testIds.tabIslands },
|
|
32
32
|
{ id: 'network', label: 'Network', icon: 'NET', testId: testIds.tabNetwork },
|
|
33
33
|
{ id: 'guard', label: 'Guard', icon: 'GRD', testId: testIds.tabGuard },
|
|
34
34
|
{ id: 'preview', label: 'Changes', icon: 'CHG', testId: testIds.tabPreview },
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { RouteSpec } from "../spec/schema";
|
|
3
|
+
import { generatePageComponent } from "./templates";
|
|
4
|
+
|
|
5
|
+
describe("generatePageComponent", () => {
|
|
6
|
+
test("refuses to emit a placeholder for hydrating routes without a clientModule", () => {
|
|
7
|
+
const route: RouteSpec = {
|
|
8
|
+
id: "login",
|
|
9
|
+
kind: "page",
|
|
10
|
+
pattern: "/login",
|
|
11
|
+
module: "app/login/page.tsx",
|
|
12
|
+
componentModule: "app/login/page.tsx",
|
|
13
|
+
hydration: {
|
|
14
|
+
strategy: "full",
|
|
15
|
+
priority: "immediate",
|
|
16
|
+
preload: false,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
expect(() => generatePageComponent(route)).toThrow("no clientModule");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("still emits placeholders for static routes without hydration", () => {
|
|
24
|
+
const route: RouteSpec = {
|
|
25
|
+
id: "about",
|
|
26
|
+
kind: "page",
|
|
27
|
+
pattern: "/about",
|
|
28
|
+
module: "app/about/page.tsx",
|
|
29
|
+
componentModule: "app/about/page.tsx",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
expect(generatePageComponent(route)).toContain("About Page");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
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,14 +238,21 @@ 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
|
-
|
|
248
|
-
|
|
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
|
+
if (needsHydration(route)) {
|
|
248
|
+
throw new Error(
|
|
249
|
+
`[${route.id}] Route has hydration strategy "${route.hydration?.strategy}" but no clientModule. ` +
|
|
250
|
+
"Refusing to generate a placeholder page because it would disagree with runtime hydration state.",
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// slotModule이 있으면 PageHandler 형식으로 생성 (filling 포함)
|
|
255
|
+
if (route.slotModule) {
|
|
249
256
|
return generatePageHandlerWithSlot(route);
|
|
250
257
|
}
|
|
251
258
|
|