@valentinkolb/ssr 0.3.0 → 0.4.0
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 +11 -1
- package/package.json +1 -1
- package/src/adapter/bun.ts +8 -9
- package/src/adapter/client.js +246 -74
- package/src/adapter/elysia.ts +4 -10
- package/src/adapter/hono.ts +2 -2
- package/src/index.ts +13 -14
- package/src/transform.ts +20 -13
package/README.md
CHANGED
|
@@ -279,11 +279,21 @@ src/
|
|
|
279
279
|
createConfig({
|
|
280
280
|
dev?: boolean; // Enable dev mode (default: false)
|
|
281
281
|
verbose?: boolean; // Enable verbose logging (default: !dev)
|
|
282
|
-
autoRefresh?: boolean; // Enable auto-reload in dev (default: true)
|
|
283
282
|
template?: (context) => string; // HTML template function (optional, has default)
|
|
284
283
|
})
|
|
285
284
|
```
|
|
286
285
|
|
|
286
|
+
## Dev Tools
|
|
287
|
+
|
|
288
|
+
In dev mode, a small `[ssr]` badge appears in the corner of the page. Click it to open the dev tools panel where you can:
|
|
289
|
+
|
|
290
|
+
- Toggle auto-reload on/off
|
|
291
|
+
- Highlight island components (green border)
|
|
292
|
+
- Highlight client components (blue border)
|
|
293
|
+
- Move the panel to any corner
|
|
294
|
+
|
|
295
|
+
Settings are persisted in localStorage.
|
|
296
|
+
|
|
287
297
|
## Contributing
|
|
288
298
|
|
|
289
299
|
Contributions are welcome! The codebase is intentionally minimal. Keep changes focused and avoid adding unnecessary complexity.
|
package/package.json
CHANGED
package/src/adapter/bun.ts
CHANGED
|
@@ -27,17 +27,16 @@ type Routes = Record<string, RouteHandler>;
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
export const routes = (config: SsrConfig): Routes => {
|
|
30
|
-
const { dev
|
|
30
|
+
const { dev } = config;
|
|
31
31
|
const ssrDir = getSsrDir(dev);
|
|
32
32
|
|
|
33
|
-
const devRoutes: Routes =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
: {};
|
|
33
|
+
const devRoutes: Routes = dev
|
|
34
|
+
? {
|
|
35
|
+
"/_ssr/_reload": () => createReloadResponse(),
|
|
36
|
+
"/_ssr/_ping": () => new Response("ok"),
|
|
37
|
+
"/_ssr/_client.js": () => createClientResponse(),
|
|
38
|
+
}
|
|
39
|
+
: {};
|
|
41
40
|
|
|
42
41
|
return {
|
|
43
42
|
...devRoutes,
|
package/src/adapter/client.js
CHANGED
|
@@ -4,78 +4,250 @@
|
|
|
4
4
|
if (!window.__ssr_reload) {
|
|
5
5
|
window.__ssr_reload = true;
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
7
|
+
(function () {
|
|
8
|
+
// ========================================
|
|
9
|
+
// Settings (persisted in localStorage)
|
|
10
|
+
// ========================================
|
|
11
|
+
const STORAGE_KEY = "_ssr";
|
|
12
|
+
const defaults = {
|
|
13
|
+
autoReload: true,
|
|
14
|
+
highlightIslands: false,
|
|
15
|
+
highlightClients: false,
|
|
16
|
+
position: "bl",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const loadSettings = () => {
|
|
20
|
+
try {
|
|
21
|
+
return {
|
|
22
|
+
...defaults,
|
|
23
|
+
...JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}"),
|
|
24
|
+
};
|
|
25
|
+
} catch {
|
|
26
|
+
return defaults;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const saveSettings = (s) =>
|
|
31
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(s));
|
|
32
|
+
|
|
33
|
+
let settings = loadSettings();
|
|
34
|
+
|
|
35
|
+
// ========================================
|
|
36
|
+
// Component counts from DOM
|
|
37
|
+
// ========================================
|
|
38
|
+
const islandCount = document.querySelectorAll("solid-island").length;
|
|
39
|
+
const clientCount = document.querySelectorAll("solid-client").length;
|
|
40
|
+
|
|
41
|
+
// ========================================
|
|
42
|
+
// Highlight styles
|
|
43
|
+
// ========================================
|
|
44
|
+
const style = document.head.appendChild(document.createElement("style"));
|
|
45
|
+
|
|
46
|
+
const updateStyles = () => {
|
|
47
|
+
style.textContent = `
|
|
48
|
+
${
|
|
49
|
+
settings.highlightIslands
|
|
50
|
+
? `
|
|
51
|
+
solid-island {
|
|
52
|
+
display: block;
|
|
53
|
+
box-shadow: 0 0 0 1px #22c55e !important;
|
|
54
|
+
position: relative;
|
|
55
|
+
}
|
|
56
|
+
solid-island::before {
|
|
57
|
+
content: attr(data-file);
|
|
58
|
+
position: absolute;
|
|
59
|
+
top: -17px;
|
|
60
|
+
left: -1px;
|
|
61
|
+
font-size: 10px;
|
|
62
|
+
font-family: monospace;
|
|
63
|
+
color: black;
|
|
64
|
+
background: #22c55e;
|
|
65
|
+
padding: 1px 4px;
|
|
66
|
+
white-space: nowrap;
|
|
67
|
+
}
|
|
68
|
+
`
|
|
69
|
+
: ""
|
|
70
|
+
}
|
|
71
|
+
${
|
|
72
|
+
settings.highlightClients
|
|
73
|
+
? `
|
|
74
|
+
solid-client {
|
|
75
|
+
display: block;
|
|
76
|
+
box-shadow: 0 0 0 1px #3b82f6 !important;
|
|
77
|
+
position: relative;
|
|
78
|
+
}
|
|
79
|
+
solid-client::before {
|
|
80
|
+
content: attr(data-file);
|
|
81
|
+
position: absolute;
|
|
82
|
+
top: -17px;
|
|
83
|
+
left: -1px;
|
|
84
|
+
font-size: 10px;
|
|
85
|
+
font-family: monospace;
|
|
86
|
+
color: black;
|
|
87
|
+
background: #3b82f6;
|
|
88
|
+
padding: 1px 4px;
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
}
|
|
91
|
+
`
|
|
92
|
+
: ""
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
};
|
|
96
|
+
updateStyles();
|
|
97
|
+
|
|
98
|
+
// ========================================
|
|
99
|
+
// Position logic
|
|
100
|
+
// ========================================
|
|
101
|
+
const positions = {
|
|
102
|
+
tl: {
|
|
103
|
+
badge: { top: "8px", left: "8px", bottom: "", right: "" },
|
|
104
|
+
panel: { top: "32px", left: "8px", bottom: "", right: "" },
|
|
105
|
+
},
|
|
106
|
+
tr: {
|
|
107
|
+
badge: { top: "8px", right: "8px", bottom: "", left: "" },
|
|
108
|
+
panel: { top: "32px", right: "8px", bottom: "", left: "" },
|
|
109
|
+
},
|
|
110
|
+
bl: {
|
|
111
|
+
badge: { bottom: "8px", left: "8px", top: "", right: "" },
|
|
112
|
+
panel: { bottom: "32px", left: "8px", top: "", right: "" },
|
|
113
|
+
},
|
|
114
|
+
br: {
|
|
115
|
+
badge: { bottom: "8px", right: "8px", top: "", left: "" },
|
|
116
|
+
panel: { bottom: "32px", right: "8px", top: "", left: "" },
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const applyPosition = () => {
|
|
121
|
+
const pos = positions[settings.position] || positions.bl;
|
|
122
|
+
Object.assign(badge.style, pos.badge);
|
|
123
|
+
Object.assign(panel.style, pos.panel);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// ========================================
|
|
127
|
+
// UI Elements
|
|
128
|
+
// ========================================
|
|
129
|
+
const panel = document.body.appendChild(document.createElement("div"));
|
|
130
|
+
panel.innerHTML = `
|
|
131
|
+
<div style="margin-bottom:8px;font-weight:bold">SSR Dev Tools</div>
|
|
132
|
+
<label style="display:block;margin:4px 0;cursor:pointer">
|
|
133
|
+
<input type="checkbox" id="_ssr_reload" ${settings.autoReload ? "checked" : ""}>
|
|
134
|
+
Auto reload
|
|
135
|
+
</label>
|
|
136
|
+
<label style="display:block;margin:4px 0;cursor:pointer">
|
|
137
|
+
<input type="checkbox" id="_ssr_islands" ${settings.highlightIslands ? "checked" : ""}>
|
|
138
|
+
Highlight islands (${islandCount})
|
|
139
|
+
</label>
|
|
140
|
+
<label style="display:block;margin:4px 0;cursor:pointer">
|
|
141
|
+
<input type="checkbox" id="_ssr_clients" ${settings.highlightClients ? "checked" : ""}>
|
|
142
|
+
Highlight clients (${clientCount})
|
|
143
|
+
</label>
|
|
144
|
+
<div style="margin-top:8px;border-top:1px solid #333;padding-top:8px">
|
|
145
|
+
<label style="color:#888">Position:
|
|
146
|
+
<select id="_ssr_position" style="background:#222;color:#ccc;border:1px solid #444;padding:2px;margin-left:4px">
|
|
147
|
+
<option value="tl" ${settings.position === "tl" ? "selected" : ""}>Top Left</option>
|
|
148
|
+
<option value="tr" ${settings.position === "tr" ? "selected" : ""}>Top Right</option>
|
|
149
|
+
<option value="bl" ${settings.position === "bl" ? "selected" : ""}>Bottom Left</option>
|
|
150
|
+
<option value="br" ${settings.position === "br" ? "selected" : ""}>Bottom Right</option>
|
|
151
|
+
</select>
|
|
152
|
+
</label>
|
|
153
|
+
</div>
|
|
154
|
+
`;
|
|
155
|
+
Object.assign(panel.style, {
|
|
156
|
+
fontFamily: "monospace",
|
|
157
|
+
fontSize: "12px",
|
|
158
|
+
color: "#ccc",
|
|
159
|
+
background: "#111",
|
|
160
|
+
padding: "12px",
|
|
161
|
+
border: "1px solid #333",
|
|
162
|
+
borderRadius: "4px",
|
|
163
|
+
position: "fixed",
|
|
164
|
+
zIndex: "9999",
|
|
165
|
+
display: "none",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Badge
|
|
169
|
+
const badge = document.body.appendChild(document.createElement("div"));
|
|
170
|
+
badge.innerText = "[ssr]";
|
|
171
|
+
badge.onclick = () => {
|
|
172
|
+
panel.style.display = panel.style.display === "none" ? "block" : "none";
|
|
173
|
+
};
|
|
174
|
+
Object.assign(badge.style, {
|
|
175
|
+
fontFamily: "monospace",
|
|
176
|
+
fontSize: "12px",
|
|
177
|
+
color: "#555",
|
|
178
|
+
position: "fixed",
|
|
179
|
+
zIndex: "9999",
|
|
180
|
+
cursor: "pointer",
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
applyPosition();
|
|
184
|
+
|
|
185
|
+
// ========================================
|
|
186
|
+
// Event handlers
|
|
187
|
+
// ========================================
|
|
188
|
+
panel.querySelector("#_ssr_islands").onchange = (e) => {
|
|
189
|
+
settings.highlightIslands = e.target.checked;
|
|
190
|
+
saveSettings(settings);
|
|
191
|
+
updateStyles();
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
panel.querySelector("#_ssr_clients").onchange = (e) => {
|
|
195
|
+
settings.highlightClients = e.target.checked;
|
|
196
|
+
saveSettings(settings);
|
|
197
|
+
updateStyles();
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
panel.querySelector("#_ssr_position").onchange = (e) => {
|
|
201
|
+
settings.position = e.target.value;
|
|
202
|
+
saveSettings(settings);
|
|
203
|
+
applyPosition();
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// ========================================
|
|
207
|
+
// Live reload via SSE
|
|
208
|
+
// ========================================
|
|
209
|
+
let es, checkInterval;
|
|
210
|
+
|
|
211
|
+
const startReload = () => {
|
|
212
|
+
if (es) return;
|
|
213
|
+
try {
|
|
214
|
+
es = new EventSource("/_ssr/_reload");
|
|
215
|
+
badge.innerText = "[ssr]";
|
|
216
|
+
} catch {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
es.onerror = (e) => {
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
stopReload();
|
|
222
|
+
badge.innerText = "[...]";
|
|
223
|
+
if (!settings.autoReload) return;
|
|
224
|
+
checkInterval = setInterval(() => {
|
|
225
|
+
fetch("/_ssr/_ping")
|
|
226
|
+
.then(({ ok }) => ok && location.reload())
|
|
227
|
+
.catch(() => {});
|
|
228
|
+
}, 300);
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const stopReload = () => {
|
|
233
|
+
if (es) {
|
|
234
|
+
es.close();
|
|
235
|
+
es = null;
|
|
236
|
+
}
|
|
237
|
+
if (checkInterval) {
|
|
238
|
+
clearInterval(checkInterval);
|
|
239
|
+
checkInterval = null;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
if (settings.autoReload) startReload();
|
|
244
|
+
|
|
245
|
+
panel.querySelector("#_ssr_reload").onchange = (e) => {
|
|
246
|
+
settings.autoReload = e.target.checked;
|
|
247
|
+
saveSettings(settings);
|
|
248
|
+
settings.autoReload ? startReload() : stopReload();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
window.addEventListener("pagehide", stopReload);
|
|
252
|
+
})();
|
|
81
253
|
}
|
package/src/adapter/elysia.ts
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
export const routes = (config: SsrConfig) => {
|
|
26
|
-
const { dev
|
|
26
|
+
const { dev } = config;
|
|
27
27
|
const ssrDir = getSsrDir(dev);
|
|
28
28
|
|
|
29
29
|
return new Elysia({ name: "ssr" })
|
|
@@ -34,13 +34,7 @@ export const routes = (config: SsrConfig) => {
|
|
|
34
34
|
headers: { "Cache-Control": getCacheHeaders(dev) },
|
|
35
35
|
}),
|
|
36
36
|
)
|
|
37
|
-
.get("/_ssr/_reload", () =>
|
|
38
|
-
|
|
39
|
-
)
|
|
40
|
-
.get("/_ssr/_ping", () =>
|
|
41
|
-
dev && autoRefresh ? new Response("ok") : notFound(),
|
|
42
|
-
)
|
|
43
|
-
.get("/_ssr/_client.js", () =>
|
|
44
|
-
dev && autoRefresh ? createClientResponse() : notFound(),
|
|
45
|
-
);
|
|
37
|
+
.get("/_ssr/_reload", () => (dev ? createReloadResponse() : notFound()))
|
|
38
|
+
.get("/_ssr/_ping", () => (dev ? new Response("ok") : notFound()))
|
|
39
|
+
.get("/_ssr/_client.js", () => (dev ? createClientResponse() : notFound()));
|
|
46
40
|
};
|
package/src/adapter/hono.ts
CHANGED
|
@@ -21,13 +21,13 @@ import {
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
export const routes = (config: SsrConfig) => {
|
|
24
|
-
const { dev
|
|
24
|
+
const { dev } = config;
|
|
25
25
|
const ssrDir = getSsrDir(dev);
|
|
26
26
|
|
|
27
27
|
const app = new Hono();
|
|
28
28
|
|
|
29
29
|
// Dev mode endpoints
|
|
30
|
-
if (dev
|
|
30
|
+
if (dev) {
|
|
31
31
|
app.get("/_client.js", () => createClientResponse());
|
|
32
32
|
app.get("/_reload", () => createReloadResponse());
|
|
33
33
|
app.get("/_ping", (c) => c.text("ok"));
|
package/src/index.ts
CHANGED
|
@@ -21,8 +21,6 @@ export type SsrOptions<T extends object = object> = {
|
|
|
21
21
|
dev?: boolean;
|
|
22
22
|
/** Enable verbose logging (default: true in prod, false in dev) */
|
|
23
23
|
verbose?: boolean;
|
|
24
|
-
/** Enable auto page refresh in dev mode (default: true) */
|
|
25
|
-
autoRefresh?: boolean;
|
|
26
24
|
/** HTML template function (optional, has default) */
|
|
27
25
|
template?: (
|
|
28
26
|
ctx: {
|
|
@@ -35,7 +33,6 @@ export type SsrOptions<T extends object = object> = {
|
|
|
35
33
|
export type SsrConfig = {
|
|
36
34
|
dev: boolean;
|
|
37
35
|
verbose?: boolean;
|
|
38
|
-
autoRefresh: boolean;
|
|
39
36
|
};
|
|
40
37
|
|
|
41
38
|
type HtmlFn<T extends object> = (
|
|
@@ -85,7 +82,7 @@ export type SsrResult<T extends object> = {
|
|
|
85
82
|
export const createConfig = <T extends object = object>(
|
|
86
83
|
options: SsrOptions<T> = {},
|
|
87
84
|
): SsrResult<T> => {
|
|
88
|
-
const { dev = false, verbose,
|
|
85
|
+
const { dev = false, verbose, template } = options;
|
|
89
86
|
|
|
90
87
|
// Default template if none provided
|
|
91
88
|
const htmlTemplate =
|
|
@@ -108,7 +105,6 @@ export const createConfig = <T extends object = object>(
|
|
|
108
105
|
const config: SsrConfig = {
|
|
109
106
|
dev,
|
|
110
107
|
verbose,
|
|
111
|
-
autoRefresh,
|
|
112
108
|
};
|
|
113
109
|
|
|
114
110
|
// HTML renderer
|
|
@@ -116,21 +112,24 @@ export const createConfig = <T extends object = object>(
|
|
|
116
112
|
const body = renderToString(() => element);
|
|
117
113
|
|
|
118
114
|
// Extract island and client component IDs from rendered HTML
|
|
119
|
-
const
|
|
120
|
-
...
|
|
121
|
-
[...body.matchAll(/<solid-(island|client) data-id="([^"]+)"/g)].map(
|
|
122
|
-
(m) => m[2],
|
|
123
|
-
),
|
|
124
|
-
),
|
|
115
|
+
const matches = [
|
|
116
|
+
...body.matchAll(/<solid-(island|client) data-id="([^"]+)"/g),
|
|
125
117
|
];
|
|
118
|
+
const islands = [
|
|
119
|
+
...new Set(matches.filter((m) => m[1] === "island").map((m) => m[2])),
|
|
120
|
+
];
|
|
121
|
+
const clients = [
|
|
122
|
+
...new Set(matches.filter((m) => m[1] === "client").map((m) => m[2])),
|
|
123
|
+
];
|
|
124
|
+
const islandIds = [...islands, ...clients];
|
|
126
125
|
|
|
127
126
|
// Component scripts
|
|
128
127
|
let scripts = islandIds
|
|
129
128
|
.map((id) => `<script type="module" src="/_ssr/${id}.js"></script>`)
|
|
130
129
|
.join("\n");
|
|
131
130
|
|
|
132
|
-
// Add dev
|
|
133
|
-
if (dev
|
|
131
|
+
// Add dev tools script in dev mode
|
|
132
|
+
if (dev) {
|
|
134
133
|
scripts += `\n<script type="module" src="/_ssr/_client.js"></script>`;
|
|
135
134
|
}
|
|
136
135
|
|
|
@@ -186,7 +185,7 @@ export const createConfig = <T extends object = object>(
|
|
|
186
185
|
// Issue: https://github.com/oven-sh/bun/issues/4689
|
|
187
186
|
const contents = await import(`${path}?`, { with: { type: "text" } });
|
|
188
187
|
return {
|
|
189
|
-
contents: await transform(contents.default, path, "ssr"),
|
|
188
|
+
contents: await transform(contents.default, path, "ssr", dev),
|
|
190
189
|
loader: "js",
|
|
191
190
|
};
|
|
192
191
|
});
|
package/src/transform.ts
CHANGED
|
@@ -35,7 +35,7 @@ const attr = (name: string, value: any) =>
|
|
|
35
35
|
|
|
36
36
|
type ComponentType = "island" | "client";
|
|
37
37
|
|
|
38
|
-
const componentWrapperPlugin = (filename: string) => ({
|
|
38
|
+
const componentWrapperPlugin = (filename: string, dev: boolean) => ({
|
|
39
39
|
visitor: {
|
|
40
40
|
Program(programPath: any) {
|
|
41
41
|
const componentImports = new Map<
|
|
@@ -109,17 +109,23 @@ const componentWrapperPlugin = (filename: string) => ({
|
|
|
109
109
|
// For islands: wrap the component, for client: empty wrapper (no SSR)
|
|
110
110
|
const children = component.type === "island" ? [path.node] : [];
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
),
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
// Extract filename from path (e.g., "Counter.island.tsx")
|
|
113
|
+
const file = component.path.split("/").pop() || "";
|
|
114
|
+
|
|
115
|
+
const attrs = [
|
|
116
|
+
attr("data-id", id),
|
|
117
|
+
attr(
|
|
118
|
+
"data-props",
|
|
119
|
+
t.callExpression(t.identifier("__seroval_serialize"), [props]),
|
|
120
|
+
),
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
// Add file attribute in dev mode
|
|
124
|
+
if (dev) {
|
|
125
|
+
attrs.push(attr("data-file", file));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const wrapper = jsx(wrapperTag, attrs, children);
|
|
123
129
|
|
|
124
130
|
path.replaceWith(wrapper);
|
|
125
131
|
path.skip();
|
|
@@ -137,6 +143,7 @@ export const transform = async (
|
|
|
137
143
|
source: string,
|
|
138
144
|
filename: string,
|
|
139
145
|
mode: "ssr" | "dom",
|
|
146
|
+
dev: boolean = false,
|
|
140
147
|
): Promise<string> => {
|
|
141
148
|
let code = source;
|
|
142
149
|
|
|
@@ -144,7 +151,7 @@ export const transform = async (
|
|
|
144
151
|
const result = await transformAsync(code, {
|
|
145
152
|
filename,
|
|
146
153
|
parserOpts: { plugins: ["jsx", "typescript"] },
|
|
147
|
-
plugins: [() => componentWrapperPlugin(filename)],
|
|
154
|
+
plugins: [() => componentWrapperPlugin(filename, dev)],
|
|
148
155
|
});
|
|
149
156
|
code = result?.code || code;
|
|
150
157
|
}
|