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.
@@ -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 subpaths correctly: react-dom/client, react/jsx-runtime, etc.
187
- let entryPath = null;
185
+ // 🧠 Handle subpath imports correctly (like react-dom/client)
186
+ let entryFile = null;
188
187
  try {
189
- entryPath = require.resolve(id, { paths: [appRoot] });
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
- if (parts.length > 1) {
195
- const pkgRoot = parts[0].startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
196
- const subPath = parts.slice(pkgRoot.startsWith('@') ? 2 : 1).join('/');
197
- const pkgDir = path_1.default.dirname(require.resolve(`${pkgRoot}/package.json`, { paths: [appRoot] }));
198
- // resolve full subpath from package root
199
- const tryPath = path_1.default.join(pkgDir, subPath);
200
- if (await fs_extra_1.default.pathExists(tryPath + '.js'))
201
- entryPath = tryPath + '.js';
202
- else if (await fs_extra_1.default.pathExists(tryPath + '.mjs'))
203
- entryPath = tryPath + '.mjs';
204
- else if (await fs_extra_1.default.pathExists(tryPath + '/index.js'))
205
- entryPath = tryPath + '/index.js';
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 (!entryPath)
209
- throw new Error(`Could not resolve ${id}`);
215
+ if (!entryFile)
216
+ throw new Error(`Cannot resolve module: ${id}`);
210
217
  const result = await esbuild_1.default.build({
211
- entryPoints: [entryPath],
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(await fs_extra_1.default.readFile(overlayPath, 'utf8'));
333
+ return res.end(OVERLAY_RUNTIME);
238
334
  }
239
335
  next();
240
336
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.25",
3
+ "version": "1.0.27",
4
4
  "description": "react-client is a lightweight CLI and runtime for building React apps with fast iteration.",
5
5
  "license": "MIT",
6
6
  "author": "Venkatesh Sundaram",