react-client 1.0.17 → 1.0.19

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.
@@ -23,8 +23,9 @@ async function dev() {
23
23
  const appRoot = path_1.default.resolve(root, userConfig.root || '.');
24
24
  const defaultPort = userConfig.server?.port || 5173;
25
25
  const cacheDir = path_1.default.join(appRoot, '.react-client', 'deps');
26
+ const pkgFile = path_1.default.join(appRoot, 'package.json');
26
27
  await fs_extra_1.default.ensureDir(cacheDir);
27
- // šŸ” Dynamically detect entry (main.tsx or main.jsx)
28
+ // Detect entry file
28
29
  const possibleEntries = ['src/main.tsx', 'src/main.jsx'];
29
30
  const entry = possibleEntries.map((p) => path_1.default.join(appRoot, p)).find((p) => fs_extra_1.default.existsSync(p));
30
31
  if (!entry) {
@@ -32,7 +33,7 @@ async function dev() {
32
33
  process.exit(1);
33
34
  }
34
35
  const indexHtml = path_1.default.join(appRoot, 'index.html');
35
- // 🧭 Port selection
36
+ // Detect open port
36
37
  const availablePort = await (0, detect_port_1.default)(defaultPort);
37
38
  const port = availablePort;
38
39
  if (availablePort !== defaultPort) {
@@ -47,22 +48,19 @@ async function dev() {
47
48
  process.exit(0);
48
49
  }
49
50
  }
50
- // ⚔ Ensure react-refresh runtime installed
51
- function safeResolveReactRefresh() {
52
- try {
53
- return require.resolve('react-refresh/runtime');
54
- }
55
- catch {
56
- console.warn(chalk_1.default.yellow('āš ļø react-refresh not found — installing...'));
57
- (0, child_process_1.execSync)('npm install react-refresh --no-audit --no-fund --silent', {
58
- cwd: root,
59
- stdio: 'inherit',
60
- });
61
- return require.resolve('react-refresh/runtime');
62
- }
51
+ // Ensure react-refresh installed
52
+ try {
53
+ require.resolve('react-refresh/runtime');
54
+ }
55
+ catch {
56
+ console.warn(chalk_1.default.yellow('āš ļø react-refresh not found — installing...'));
57
+ (0, child_process_1.execSync)('npm install react-refresh --no-audit --no-fund --silent', {
58
+ cwd: root,
59
+ stdio: 'inherit',
60
+ });
61
+ console.log(chalk_1.default.green('āœ… react-refresh installed successfully.'));
63
62
  }
64
- void safeResolveReactRefresh();
65
- // 🧩 Core + user plugins
63
+ // --- Plugins
66
64
  const corePlugins = [
67
65
  {
68
66
  name: 'css-hmr',
@@ -83,24 +81,44 @@ async function dev() {
83
81
  ];
84
82
  const userPlugins = Array.isArray(userConfig.plugins) ? userConfig.plugins : [];
85
83
  const plugins = [...corePlugins, ...userPlugins];
86
- // 🌐 Connect app + caches
87
84
  const app = (0, connect_1.default)();
88
85
  const transformCache = new Map();
89
- // šŸ“¦ Prebundle persistent deps
90
- async function prebundleDeps() {
91
- const pkgFile = path_1.default.join(appRoot, 'package.json');
92
- if (!fs_extra_1.default.existsSync(pkgFile))
93
- return;
94
- const pkg = JSON.parse(await fs_extra_1.default.readFile(pkgFile, 'utf8'));
95
- const deps = Object.keys(pkg.dependencies || {});
96
- if (!deps.length)
86
+ // 🧠 Deep dependency graph analyzer
87
+ async function analyzeGraph(file, seen = new Set()) {
88
+ if (seen.has(file))
89
+ return seen;
90
+ seen.add(file);
91
+ const code = await fs_extra_1.default.readFile(file, 'utf8');
92
+ const matches = [
93
+ ...code.matchAll(/\bfrom\s+['"]([^'".\/][^'"]*)['"]/g),
94
+ ...code.matchAll(/\bimport\(['"]([^'".\/][^'"]*)['"]\)/g),
95
+ ];
96
+ for (const m of matches) {
97
+ const dep = m[1];
98
+ if (!dep || dep.startsWith('.') || dep.startsWith('/'))
99
+ continue;
100
+ try {
101
+ const resolved = require.resolve(dep, { paths: [appRoot] });
102
+ await analyzeGraph(resolved, seen);
103
+ }
104
+ catch {
105
+ seen.add(dep); // bare dependency
106
+ }
107
+ }
108
+ return seen;
109
+ }
110
+ // šŸ“¦ Smart prebundle cache (parallelized)
111
+ async function prebundleDeps(deps) {
112
+ if (!deps.size)
97
113
  return;
98
- const cached = await fs_extra_1.default.readdir(cacheDir);
99
- const missing = deps.filter((d) => !cached.includes(d + '.js'));
100
- if (!missing.length)
114
+ const cached = (await fs_extra_1.default.readdir(cacheDir)).map((f) => f.replace('.js', ''));
115
+ const missing = [...deps].filter((d) => !cached.includes(d));
116
+ if (!missing.length) {
117
+ console.log(chalk_1.default.green('āœ… All dependencies already prebundled.'));
101
118
  return;
119
+ }
102
120
  console.log(chalk_1.default.cyan('šŸ“¦ Prebundling:'), missing.join(', '));
103
- for (const dep of missing) {
121
+ await Promise.all(missing.map(async (dep) => {
104
122
  try {
105
123
  const entryPath = require.resolve(dep, { paths: [appRoot] });
106
124
  const outFile = path_1.default.join(cacheDir, dep + '.js');
@@ -119,12 +137,20 @@ async function dev() {
119
137
  const e = err;
120
138
  console.warn(chalk_1.default.yellow(`āš ļø Skipped ${dep}: ${e.message}`));
121
139
  }
122
- }
140
+ }));
123
141
  }
124
- await prebundleDeps();
125
- // --- Serve prebundled modules (fixed resolver)
142
+ // 🧩 Prewarm Graph
143
+ const deps = await analyzeGraph(entry);
144
+ await prebundleDeps(deps);
145
+ // Auto rebuild on package.json change
146
+ chokidar_1.default.watch(pkgFile).on('change', async () => {
147
+ console.log(chalk_1.default.yellow('šŸ“¦ package.json changed — rebuilding prebundle cache...'));
148
+ const newDeps = await analyzeGraph(entry);
149
+ await prebundleDeps(newDeps);
150
+ });
151
+ // --- Serve /@modules/
126
152
  app.use('/@modules/', async (req, res, next) => {
127
- const id = req.url?.replace(/^\/@modules\//, '');
153
+ const id = req.url?.replace(/^\/(@modules\/)?/, '');
128
154
  if (!id)
129
155
  return next();
130
156
  try {
@@ -135,21 +161,16 @@ async function dev() {
135
161
  }
136
162
  let entryPath = null;
137
163
  try {
138
- entryPath = require.resolve(id, { paths: [path_1.default.join(appRoot, 'node_modules')] });
164
+ entryPath = require.resolve(id, { paths: [appRoot] });
139
165
  }
140
166
  catch {
141
- try {
142
- entryPath = require.resolve(id, { paths: [root] });
143
- }
144
- catch {
145
- if (id === 'react')
146
- entryPath = require.resolve('react');
147
- else if (id === 'react-dom' || id === 'react-dom/client')
148
- entryPath = require.resolve('react-dom');
149
- }
167
+ if (id === 'react')
168
+ entryPath = require.resolve('react');
169
+ else if (id === 'react-dom' || id === 'react-dom/client')
170
+ entryPath = require.resolve('react-dom');
150
171
  }
151
172
  if (!entryPath)
152
- throw new Error(`Could not resolve ${id}`);
173
+ throw new Error(`Module ${id} not found.`);
153
174
  const result = await esbuild_1.default.build({
154
175
  entryPoints: [entryPath],
155
176
  bundle: true,
@@ -159,12 +180,19 @@ async function dev() {
159
180
  write: false,
160
181
  });
161
182
  let code = result.outputFiles[0].text;
162
- // āœ… Fix for react-dom/client exports
183
+ // 🩹 Fixed react-dom/client shim (no duplicate export)
163
184
  if (id === 'react-dom/client') {
164
185
  code += `
186
+ // React Client auto-shim for React 18+
165
187
  import * as ReactDOMClient from '/@modules/react-dom';
166
- export const createRoot = ReactDOMClient.createRoot || ReactDOMClient.default?.createRoot;
167
- export default ReactDOMClient.default || ReactDOMClient;
188
+ if (!('createRoot' in ReactDOMClient) && ReactDOMClient.default) {
189
+ Object.assign(ReactDOMClient, ReactDOMClient.default);
190
+ }
191
+ export const createRoot =
192
+ ReactDOMClient.createRoot ||
193
+ ReactDOMClient.default?.createRoot ||
194
+ (() => { throw new Error('ReactDOM.createRoot not found'); });
195
+ export default ReactDOMClient;
168
196
  `;
169
197
  }
170
198
  await fs_extra_1.default.writeFile(cacheFile, code, 'utf8');
@@ -178,7 +206,7 @@ async function dev() {
178
206
  res.end(`// Failed to resolve module ${id}: ${e.message}`);
179
207
  }
180
208
  });
181
- // --- Serve /src files dynamically (auto extension fallback)
209
+ // --- Serve /src files dynamically
182
210
  app.use(async (req, res, next) => {
183
211
  if (!req.url || (!req.url.startsWith('/src/') && !req.url.endsWith('.css')))
184
212
  return next();
@@ -194,10 +222,6 @@ async function dev() {
194
222
  if (!(await fs_extra_1.default.pathExists(filePath)))
195
223
  return next();
196
224
  try {
197
- if (transformCache.has(filePath)) {
198
- res.setHeader('Content-Type', 'application/javascript');
199
- return res.end(transformCache.get(filePath));
200
- }
201
225
  let code = await fs_extra_1.default.readFile(filePath, 'utf8');
202
226
  // Rewrite bare imports → /@modules/*
203
227
  code = code
@@ -230,7 +254,7 @@ async function dev() {
230
254
  res.end(`// Error: ${e.message}`);
231
255
  }
232
256
  });
233
- // --- Serve index.html + overlay + HMR client
257
+ // --- Serve index.html + overlay + HMR
234
258
  app.use(async (req, res, next) => {
235
259
  if (req.url !== '/' && req.url !== '/index.html')
236
260
  return next();
@@ -278,23 +302,20 @@ async function dev() {
278
302
  res.setHeader('Content-Type', 'text/html');
279
303
  res.end(html);
280
304
  });
281
- // --- WebSocket + HMR via BroadcastManager
305
+ // --- WebSocket + HMR
282
306
  const server = http_1.default.createServer(app);
283
307
  const broadcaster = new broadcastManager_1.BroadcastManager(server);
284
308
  chokidar_1.default.watch(path_1.default.join(appRoot, 'src'), { ignoreInitial: true }).on('change', async (file) => {
285
309
  console.log(chalk_1.default.yellow(`šŸ”„ Changed: ${file}`));
286
310
  transformCache.delete(file);
287
311
  for (const p of plugins) {
288
- await p.onHotUpdate?.(file, {
289
- broadcast: (msg) => broadcaster.broadcast(msg),
290
- });
312
+ await p.onHotUpdate?.(file, { broadcast: (msg) => broadcaster.broadcast(msg) });
291
313
  }
292
314
  broadcaster.broadcast({
293
315
  type: 'update',
294
316
  path: '/' + path_1.default.relative(appRoot, file).replace(/\\/g, '/'),
295
317
  });
296
318
  });
297
- // šŸš€ Start server
298
319
  server.listen(port, async () => {
299
320
  const url = `http://localhost:${port}`;
300
321
  console.log(chalk_1.default.cyan.bold('\nšŸš€ React Client Dev Server'));
@@ -16,10 +16,8 @@ class BroadcastManager {
16
16
  this.wss = new ws_1.WebSocketServer({ server });
17
17
  this.wss.on('connection', (ws) => {
18
18
  this.clients.add(ws);
19
- console.log(chalk_1.default.gray('šŸ”Œ Client connected'));
20
19
  ws.on('close', () => {
21
20
  this.clients.delete(ws);
22
- console.log(chalk_1.default.gray('āŽ Client disconnected'));
23
21
  });
24
22
  ws.on('error', (err) => {
25
23
  console.error(chalk_1.default.red('āš ļø WebSocket error:'), err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
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",