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.
@@ -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((async (req, res, next) => {
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
- return;
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
- // Resolve and bundle on-demand
189
- const entryResolved = require.resolve(id, { paths: [appRoot] });
211
+ if (!entryFile)
212
+ throw new Error(`Cannot resolve module: ${id}`);
190
213
  const result = await esbuild_1.default.build({
191
- entryPoints: [entryResolved],
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
- app.use((async (req, res, next) => {
211
- const url = req.url ?? '';
212
- if (url !== '/@runtime/overlay')
213
- return next();
214
- const overlayPath = path_1.default.join(appRoot, RUNTIME_OVERLAY);
215
- if (!(await fs_extra_1.default.pathExists(overlayPath))) {
216
- res.writeHead(404);
217
- return res.end('// overlay-runtime not found');
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
- res.setHeader('Content-Type', 'application/javascript');
220
- res.end(await fs_extra_1.default.readFile(overlayPath, 'utf8'));
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 ?? '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
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",