react-client 1.0.24 → 1.0.26
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 +124 -22
- 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
|
*
|
|
@@ -29,7 +28,6 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
29
28
|
const child_process_1 = require("child_process");
|
|
30
29
|
const loadConfig_1 = require("../../utils/loadConfig");
|
|
31
30
|
const broadcastManager_1 = require("../../server/broadcastManager");
|
|
32
|
-
const RUNTIME_OVERLAY = '/src/runtime/overlay-runtime.js';
|
|
33
31
|
async function dev() {
|
|
34
32
|
const root = process.cwd();
|
|
35
33
|
const userConfig = (await (0, loadConfig_1.loadReactClientConfig)(root));
|
|
@@ -169,7 +167,7 @@ async function dev() {
|
|
|
169
167
|
});
|
|
170
168
|
}
|
|
171
169
|
// --- Serve /@modules/<dep> (prebundled or on-demand esbuild bundle)
|
|
172
|
-
app.use(
|
|
170
|
+
app.use(async (req, res, next) => {
|
|
173
171
|
const url = req.url ?? '';
|
|
174
172
|
if (!url.startsWith('/@modules/'))
|
|
175
173
|
return next();
|
|
@@ -182,21 +180,45 @@ async function dev() {
|
|
|
182
180
|
const cacheFile = path_1.default.join(cacheDir, id.replace(/[\\/]/g, '_') + '.js');
|
|
183
181
|
if (await fs_extra_1.default.pathExists(cacheFile)) {
|
|
184
182
|
res.setHeader('Content-Type', 'application/javascript');
|
|
185
|
-
res.end(await fs_extra_1.default.readFile(cacheFile, 'utf8'));
|
|
186
|
-
|
|
183
|
+
return res.end(await fs_extra_1.default.readFile(cacheFile, 'utf8'));
|
|
184
|
+
}
|
|
185
|
+
// 🧠 Smart module resolver: handles subpaths like react-dom/client, react/jsx-runtime, etc.
|
|
186
|
+
let entryFile = null;
|
|
187
|
+
try {
|
|
188
|
+
// Try direct require.resolve first (works for most)
|
|
189
|
+
entryFile = require.resolve(id, { paths: [appRoot] });
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Handle subpath imports
|
|
193
|
+
const parts = id.split('/');
|
|
194
|
+
const pkgRoot = parts[0].startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
|
|
195
|
+
const subPath = parts.slice(pkgRoot.startsWith('@') ? 2 : 1).join('/');
|
|
196
|
+
const pkgJson = require.resolve(`${pkgRoot}/package.json`, { paths: [appRoot] });
|
|
197
|
+
const pkgDir = path_1.default.dirname(pkgJson);
|
|
198
|
+
const tryFiles = [
|
|
199
|
+
path_1.default.join(pkgDir, subPath),
|
|
200
|
+
path_1.default.join(pkgDir, subPath, 'index.js'),
|
|
201
|
+
path_1.default.join(pkgDir, subPath + '.js'),
|
|
202
|
+
path_1.default.join(pkgDir, subPath + '.mjs'),
|
|
203
|
+
];
|
|
204
|
+
for (const f of tryFiles) {
|
|
205
|
+
if (await fs_extra_1.default.pathExists(f)) {
|
|
206
|
+
entryFile = f;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
187
210
|
}
|
|
188
|
-
|
|
189
|
-
|
|
211
|
+
if (!entryFile)
|
|
212
|
+
throw new Error(`Cannot resolve module: ${id}`);
|
|
190
213
|
const result = await esbuild_1.default.build({
|
|
191
|
-
entryPoints: [
|
|
214
|
+
entryPoints: [entryFile],
|
|
192
215
|
bundle: true,
|
|
193
|
-
write: false,
|
|
194
216
|
platform: 'browser',
|
|
195
217
|
format: 'esm',
|
|
218
|
+
write: false,
|
|
196
219
|
target: ['es2020'],
|
|
197
220
|
});
|
|
198
221
|
const output = result.outputFiles?.[0]?.text ?? '';
|
|
199
|
-
// Persist to cache so next request is faster
|
|
200
222
|
await fs_extra_1.default.writeFile(cacheFile, output, 'utf8');
|
|
201
223
|
res.setHeader('Content-Type', 'application/javascript');
|
|
202
224
|
res.end(output);
|
|
@@ -205,20 +227,100 @@ async function dev() {
|
|
|
205
227
|
res.writeHead(500);
|
|
206
228
|
res.end(`// Failed to resolve module ${id}: ${err.message}`);
|
|
207
229
|
}
|
|
208
|
-
})
|
|
230
|
+
});
|
|
209
231
|
// --- Serve runtime overlay (local file) so overlay-runtime.js is loaded automatically
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
232
|
+
// --- Serve runtime overlay (inline in dev server)
|
|
233
|
+
const OVERLAY_RUNTIME = `
|
|
234
|
+
import "/@prismjs";
|
|
235
|
+
|
|
236
|
+
const overlayId = "__rc_error_overlay__";
|
|
237
|
+
|
|
238
|
+
const style = document.createElement("style");
|
|
239
|
+
style.textContent = \`
|
|
240
|
+
#\${overlayId} {
|
|
241
|
+
position: fixed;
|
|
242
|
+
inset: 0;
|
|
243
|
+
background: rgba(0, 0, 0, 0.9);
|
|
244
|
+
color: #fff;
|
|
245
|
+
font-family: Menlo, Consolas, monospace;
|
|
246
|
+
font-size: 14px;
|
|
247
|
+
z-index: 999999;
|
|
248
|
+
overflow: auto;
|
|
249
|
+
padding: 24px;
|
|
250
|
+
animation: fadeIn 0.2s ease-out;
|
|
251
|
+
}
|
|
252
|
+
@keyframes fadeIn { from {opacity: 0;} to {opacity: 1;} }
|
|
253
|
+
#\${overlayId} h2 { color: #ff6b6b; margin-bottom: 16px; }
|
|
254
|
+
#\${overlayId} pre { background: rgba(255,255,255,0.1); padding: 12px; border-radius: 6px; }
|
|
255
|
+
#\${overlayId} a { color: #9cf; text-decoration: underline; }
|
|
256
|
+
#\${overlayId} .frame { margin: 12px 0; }
|
|
257
|
+
#\${overlayId} .frame-file { color: #ffa500; cursor: pointer; font-weight: bold; margin-bottom: 4px; }
|
|
258
|
+
.line-number { opacity: 0.5; margin-right: 10px; }
|
|
259
|
+
\`;
|
|
260
|
+
document.head.appendChild(style);
|
|
261
|
+
|
|
262
|
+
async function mapStackFrame(frame) {
|
|
263
|
+
const m = frame.match(/(\\/src\\/[^\s:]+):(\\d+):(\\d+)/);
|
|
264
|
+
if (!m) return frame;
|
|
265
|
+
const [, file, line, col] = m;
|
|
266
|
+
const resp = await fetch(\`/@source-map?file=\${file}&line=\${line}&column=\${col}\`);
|
|
267
|
+
if (!resp.ok) return frame;
|
|
268
|
+
const pos = await resp.json();
|
|
269
|
+
if (pos.source) {
|
|
270
|
+
return {
|
|
271
|
+
file: pos.source,
|
|
272
|
+
line: pos.line,
|
|
273
|
+
column: pos.column,
|
|
274
|
+
snippet: pos.snippet || ""
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return frame;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async function renderOverlay(err) {
|
|
281
|
+
const overlay =
|
|
282
|
+
document.getElementById(overlayId) ||
|
|
283
|
+
document.body.appendChild(Object.assign(document.createElement("div"), { id: overlayId }));
|
|
284
|
+
overlay.innerHTML = "";
|
|
285
|
+
const title = document.createElement("h2");
|
|
286
|
+
title.textContent = "🔥 " + (err.message || "Error");
|
|
287
|
+
overlay.appendChild(title);
|
|
288
|
+
|
|
289
|
+
const frames = (err.stack || "").split("\\n").filter(l => /src\\//.test(l));
|
|
290
|
+
for (const frame of frames) {
|
|
291
|
+
const mapped = await mapStackFrame(frame);
|
|
292
|
+
if (typeof mapped === "string") continue;
|
|
293
|
+
const frameEl = document.createElement("div");
|
|
294
|
+
frameEl.className = "frame";
|
|
295
|
+
|
|
296
|
+
const link = document.createElement("div");
|
|
297
|
+
link.className = "frame-file";
|
|
298
|
+
link.textContent = \`\${mapped.file}:\${mapped.line}:\${mapped.column}\`;
|
|
299
|
+
link.onclick = () =>
|
|
300
|
+
window.open("vscode://file/" + location.origin.replace("http://", "") + mapped.file + ":" + mapped.line);
|
|
301
|
+
frameEl.appendChild(link);
|
|
302
|
+
|
|
303
|
+
if (mapped.snippet) {
|
|
304
|
+
const pre = document.createElement("pre");
|
|
305
|
+
pre.classList.add("language-jsx");
|
|
306
|
+
pre.innerHTML = Prism.highlight(mapped.snippet, Prism.languages.jsx, "jsx");
|
|
307
|
+
frameEl.appendChild(pre);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
overlay.appendChild(frameEl);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
window.showErrorOverlay = (err) => renderOverlay(err);
|
|
315
|
+
window.clearErrorOverlay = () => document.getElementById(overlayId)?.remove();
|
|
316
|
+
`;
|
|
317
|
+
app.use(async (req, res, next) => {
|
|
318
|
+
if (req.url === '/@runtime/overlay') {
|
|
319
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
320
|
+
return res.end(OVERLAY_RUNTIME);
|
|
218
321
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}));
|
|
322
|
+
next();
|
|
323
|
+
});
|
|
222
324
|
// --- minimal /@source-map: return snippet around requested line of original source file
|
|
223
325
|
app.use((async (req, res, next) => {
|
|
224
326
|
const url = req.url ?? '';
|