react-client 1.0.25 → 1.0.27
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/dist/cli/commands/dev.js +122 -26
- package/package.json +1 -1
package/dist/cli/commands/dev.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* - prebundles deps into .react-client/deps
|
|
6
6
|
* - serves /@modules/<dep>
|
|
7
7
|
* - serves /src/* with esbuild transform & inline sourcemap
|
|
8
|
-
* - serves /@runtime/overlay -> src/runtime/overlay-runtime.js
|
|
9
8
|
* - /@source-map returns a snippet for overlay mapping
|
|
10
9
|
* - HMR broadcast via BroadcastManager (ws)
|
|
11
10
|
*
|
|
@@ -183,32 +182,40 @@ async function dev() {
|
|
|
183
182
|
res.setHeader('Content-Type', 'application/javascript');
|
|
184
183
|
return res.end(await fs_extra_1.default.readFile(cacheFile, 'utf8'));
|
|
185
184
|
}
|
|
186
|
-
// 🧠 Handle
|
|
187
|
-
let
|
|
185
|
+
// 🧠 Handle subpath imports correctly (like react-dom/client)
|
|
186
|
+
let entryFile = null;
|
|
188
187
|
try {
|
|
189
|
-
|
|
188
|
+
entryFile = require.resolve(id, { paths: [appRoot] });
|
|
190
189
|
}
|
|
191
190
|
catch {
|
|
192
|
-
// Fallback: handle packages with subpaths dynamically
|
|
193
191
|
const parts = id.split('/');
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
192
|
+
const pkgRoot = parts[0].startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
193
|
+
const subPath = parts.slice(pkgRoot.startsWith('@') ? 2 : 1).join('/');
|
|
194
|
+
const pkgJsonPath = require.resolve(`${pkgRoot}/package.json`, { paths: [appRoot] });
|
|
195
|
+
const pkgDir = path_1.default.dirname(pkgJsonPath);
|
|
196
|
+
// Special case: react-dom/client
|
|
197
|
+
if (pkgRoot === 'react-dom' && subPath === 'client') {
|
|
198
|
+
entryFile = path_1.default.join(pkgDir, 'client.js');
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const candidates = [
|
|
202
|
+
path_1.default.join(pkgDir, subPath),
|
|
203
|
+
path_1.default.join(pkgDir, subPath, 'index.js'),
|
|
204
|
+
path_1.default.join(pkgDir, subPath + '.js'),
|
|
205
|
+
path_1.default.join(pkgDir, subPath + '.mjs'),
|
|
206
|
+
];
|
|
207
|
+
for (const f of candidates) {
|
|
208
|
+
if (await fs_extra_1.default.pathExists(f)) {
|
|
209
|
+
entryFile = f;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
206
213
|
}
|
|
207
214
|
}
|
|
208
|
-
if (!
|
|
209
|
-
throw new Error(`
|
|
215
|
+
if (!entryFile)
|
|
216
|
+
throw new Error(`Cannot resolve module: ${id}`);
|
|
210
217
|
const result = await esbuild_1.default.build({
|
|
211
|
-
entryPoints: [
|
|
218
|
+
entryPoints: [entryFile],
|
|
212
219
|
bundle: true,
|
|
213
220
|
platform: 'browser',
|
|
214
221
|
format: 'esm',
|
|
@@ -225,16 +232,105 @@ async function dev() {
|
|
|
225
232
|
res.end(`// Failed to resolve module ${id}: ${err.message}`);
|
|
226
233
|
}
|
|
227
234
|
});
|
|
235
|
+
app.use(async (req, res, next) => {
|
|
236
|
+
if (req.url?.startsWith('/@prismjs')) {
|
|
237
|
+
const prismPath = require.resolve('prismjs', { paths: [appRoot] });
|
|
238
|
+
const code = await fs_extra_1.default.readFile(prismPath, 'utf8');
|
|
239
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
240
|
+
return res.end(code);
|
|
241
|
+
}
|
|
242
|
+
next();
|
|
243
|
+
});
|
|
228
244
|
// --- Serve runtime overlay (local file) so overlay-runtime.js is loaded automatically
|
|
245
|
+
// --- Serve runtime overlay (inline in dev server)
|
|
246
|
+
const OVERLAY_RUNTIME = `
|
|
247
|
+
import "/@prismjs";
|
|
248
|
+
|
|
249
|
+
const overlayId = "__rc_error_overlay__";
|
|
250
|
+
|
|
251
|
+
const style = document.createElement("style");
|
|
252
|
+
style.textContent = \`
|
|
253
|
+
#\${overlayId} {
|
|
254
|
+
position: fixed;
|
|
255
|
+
inset: 0;
|
|
256
|
+
background: rgba(0, 0, 0, 0.9);
|
|
257
|
+
color: #fff;
|
|
258
|
+
font-family: Menlo, Consolas, monospace;
|
|
259
|
+
font-size: 14px;
|
|
260
|
+
z-index: 999999;
|
|
261
|
+
overflow: auto;
|
|
262
|
+
padding: 24px;
|
|
263
|
+
animation: fadeIn 0.2s ease-out;
|
|
264
|
+
}
|
|
265
|
+
@keyframes fadeIn { from {opacity: 0;} to {opacity: 1;} }
|
|
266
|
+
#\${overlayId} h2 { color: #ff6b6b; margin-bottom: 16px; }
|
|
267
|
+
#\${overlayId} pre { background: rgba(255,255,255,0.1); padding: 12px; border-radius: 6px; }
|
|
268
|
+
#\${overlayId} a { color: #9cf; text-decoration: underline; }
|
|
269
|
+
#\${overlayId} .frame { margin: 12px 0; }
|
|
270
|
+
#\${overlayId} .frame-file { color: #ffa500; cursor: pointer; font-weight: bold; margin-bottom: 4px; }
|
|
271
|
+
.line-number { opacity: 0.5; margin-right: 10px; }
|
|
272
|
+
\`;
|
|
273
|
+
document.head.appendChild(style);
|
|
274
|
+
|
|
275
|
+
async function mapStackFrame(frame) {
|
|
276
|
+
const m = frame.match(/(\\/src\\/[^\s:]+):(\\d+):(\\d+)/);
|
|
277
|
+
if (!m) return frame;
|
|
278
|
+
const [, file, line, col] = m;
|
|
279
|
+
const resp = await fetch(\`/@source-map?file=\${file}&line=\${line}&column=\${col}\`);
|
|
280
|
+
if (!resp.ok) return frame;
|
|
281
|
+
const pos = await resp.json();
|
|
282
|
+
if (pos.source) {
|
|
283
|
+
return {
|
|
284
|
+
file: pos.source,
|
|
285
|
+
line: pos.line,
|
|
286
|
+
column: pos.column,
|
|
287
|
+
snippet: pos.snippet || ""
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return frame;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function renderOverlay(err) {
|
|
294
|
+
const overlay =
|
|
295
|
+
document.getElementById(overlayId) ||
|
|
296
|
+
document.body.appendChild(Object.assign(document.createElement("div"), { id: overlayId }));
|
|
297
|
+
overlay.innerHTML = "";
|
|
298
|
+
const title = document.createElement("h2");
|
|
299
|
+
title.textContent = "🔥 " + (err.message || "Error");
|
|
300
|
+
overlay.appendChild(title);
|
|
301
|
+
|
|
302
|
+
const frames = (err.stack || "").split("\\n").filter(l => /src\\//.test(l));
|
|
303
|
+
for (const frame of frames) {
|
|
304
|
+
const mapped = await mapStackFrame(frame);
|
|
305
|
+
if (typeof mapped === "string") continue;
|
|
306
|
+
const frameEl = document.createElement("div");
|
|
307
|
+
frameEl.className = "frame";
|
|
308
|
+
|
|
309
|
+
const link = document.createElement("div");
|
|
310
|
+
link.className = "frame-file";
|
|
311
|
+
link.textContent = \`\${mapped.file}:\${mapped.line}:\${mapped.column}\`;
|
|
312
|
+
link.onclick = () =>
|
|
313
|
+
window.open("vscode://file/" + location.origin.replace("http://", "") + mapped.file + ":" + mapped.line);
|
|
314
|
+
frameEl.appendChild(link);
|
|
315
|
+
|
|
316
|
+
if (mapped.snippet) {
|
|
317
|
+
const pre = document.createElement("pre");
|
|
318
|
+
pre.classList.add("language-jsx");
|
|
319
|
+
pre.innerHTML = Prism.highlight(mapped.snippet, Prism.languages.jsx, "jsx");
|
|
320
|
+
frameEl.appendChild(pre);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
overlay.appendChild(frameEl);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
window.showErrorOverlay = (err) => renderOverlay(err);
|
|
328
|
+
window.clearErrorOverlay = () => document.getElementById(overlayId)?.remove();
|
|
329
|
+
`;
|
|
229
330
|
app.use(async (req, res, next) => {
|
|
230
331
|
if (req.url === '/@runtime/overlay') {
|
|
231
|
-
const overlayPath = path_1.default.join(appRoot, 'src/runtime/overlay-runtime.js');
|
|
232
|
-
if (!(await fs_extra_1.default.pathExists(overlayPath))) {
|
|
233
|
-
res.writeHead(404);
|
|
234
|
-
return res.end('// overlay-runtime.js not found');
|
|
235
|
-
}
|
|
236
332
|
res.setHeader('Content-Type', 'application/javascript');
|
|
237
|
-
return res.end(
|
|
333
|
+
return res.end(OVERLAY_RUNTIME);
|
|
238
334
|
}
|
|
239
335
|
next();
|
|
240
336
|
});
|