react-client 1.0.17 → 1.0.18

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
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 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');
63
54
  }
64
- void safeResolveReactRefresh();
65
- // 🧩 Core + user plugins
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.'));
62
+ }
63
+ // Core plugin: CSS HMR
66
64
  const corePlugins = [
67
65
  {
68
66
  name: 'css-hmr',
@@ -83,24 +81,32 @@ 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
84
+ // Connect app
87
85
  const app = (0, connect_1.default)();
88
86
  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 || {});
87
+ // 🧠 Prewarm: analyze imports before first run
88
+ async function analyzeImports(entryFile) {
89
+ const entryCode = await fs_extra_1.default.readFile(entryFile, 'utf8');
90
+ const importMatches = [
91
+ ...entryCode.matchAll(/\bfrom\s+['"]([^'".\/][^'"]*)['"]/g),
92
+ ...entryCode.matchAll(/\bimport\(['"]([^'".\/][^'"]*)['"]\)/g),
93
+ ];
94
+ const deps = [...new Set(importMatches.map((m) => m[1]))];
95
+ console.log(chalk_1.default.gray(`🧩 Found ${deps.length} direct imports:`), deps.join(', '));
96
+ return deps;
97
+ }
98
+ // šŸ“¦ Smart prebundle cache
99
+ async function prebundleDeps(deps) {
96
100
  if (!deps.length)
97
101
  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)
102
+ const cached = (await fs_extra_1.default.readdir(cacheDir)).map((f) => f.replace('.js', ''));
103
+ const missing = deps.filter((d) => !cached.includes(d));
104
+ if (!missing.length) {
105
+ console.log(chalk_1.default.green('āœ… All dependencies already prebundled.'));
101
106
  return;
107
+ }
102
108
  console.log(chalk_1.default.cyan('šŸ“¦ Prebundling:'), missing.join(', '));
103
- for (const dep of missing) {
109
+ await Promise.all(missing.map(async (dep) => {
104
110
  try {
105
111
  const entryPath = require.resolve(dep, { paths: [appRoot] });
106
112
  const outFile = path_1.default.join(cacheDir, dep + '.js');
@@ -119,12 +125,20 @@ async function dev() {
119
125
  const e = err;
120
126
  console.warn(chalk_1.default.yellow(`āš ļø Skipped ${dep}: ${e.message}`));
121
127
  }
122
- }
128
+ }));
123
129
  }
124
- await prebundleDeps();
125
- // --- Serve prebundled modules (fixed resolver)
130
+ // 🧩 Smart rebuild trigger when package.json changes
131
+ chokidar_1.default.watch(pkgFile).on('change', async () => {
132
+ console.log(chalk_1.default.yellow('šŸ“¦ Detected package.json change — rebuilding prebundles...'));
133
+ const deps = await analyzeImports(entry);
134
+ await prebundleDeps(deps);
135
+ });
136
+ // Initial prewarm
137
+ const initialDeps = await analyzeImports(entry);
138
+ await prebundleDeps(initialDeps);
139
+ // --- Serve prebundled / node_modules
126
140
  app.use('/@modules/', async (req, res, next) => {
127
- const id = req.url?.replace(/^\/@modules\//, '');
141
+ const id = req.url?.replace(/^\/(@modules\/)?/, '');
128
142
  if (!id)
129
143
  return next();
130
144
  try {
@@ -138,18 +152,13 @@ async function dev() {
138
152
  entryPath = require.resolve(id, { paths: [path_1.default.join(appRoot, 'node_modules')] });
139
153
  }
140
154
  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
- }
155
+ if (id === 'react')
156
+ entryPath = require.resolve('react');
157
+ else if (id === 'react-dom' || id === 'react-dom/client')
158
+ entryPath = require.resolve('react-dom');
150
159
  }
151
160
  if (!entryPath)
152
- throw new Error(`Could not resolve ${id}`);
161
+ throw new Error(`Module ${id} not found.`);
153
162
  const result = await esbuild_1.default.build({
154
163
  entryPoints: [entryPath],
155
164
  bundle: true,
@@ -159,7 +168,7 @@ async function dev() {
159
168
  write: false,
160
169
  });
161
170
  let code = result.outputFiles[0].text;
162
- // āœ… Fix for react-dom/client exports
171
+ // Fix for react-dom/client exports
163
172
  if (id === 'react-dom/client') {
164
173
  code += `
165
174
  import * as ReactDOMClient from '/@modules/react-dom';
@@ -178,7 +187,7 @@ async function dev() {
178
187
  res.end(`// Failed to resolve module ${id}: ${e.message}`);
179
188
  }
180
189
  });
181
- // --- Serve /src files dynamically (auto extension fallback)
190
+ // --- Serve src files + HMR
182
191
  app.use(async (req, res, next) => {
183
192
  if (!req.url || (!req.url.startsWith('/src/') && !req.url.endsWith('.css')))
184
193
  return next();
@@ -194,12 +203,7 @@ async function dev() {
194
203
  if (!(await fs_extra_1.default.pathExists(filePath)))
195
204
  return next();
196
205
  try {
197
- if (transformCache.has(filePath)) {
198
- res.setHeader('Content-Type', 'application/javascript');
199
- return res.end(transformCache.get(filePath));
200
- }
201
206
  let code = await fs_extra_1.default.readFile(filePath, 'utf8');
202
- // Rewrite bare imports → /@modules/*
203
207
  code = code
204
208
  .replace(/\bfrom\s+['"]([^'".\/][^'"]*)['"]/g, (_match, dep) => `from "/@modules/${dep}"`)
205
209
  .replace(/\bimport\(['"]([^'".\/][^'"]*)['"]\)/g, (_match, dep) => `import("/@modules/${dep}")`);
@@ -230,7 +234,7 @@ async function dev() {
230
234
  res.end(`// Error: ${e.message}`);
231
235
  }
232
236
  });
233
- // --- Serve index.html + overlay + HMR client
237
+ // --- index.html + overlay
234
238
  app.use(async (req, res, next) => {
235
239
  if (req.url !== '/' && req.url !== '/index.html')
236
240
  return next();
@@ -278,23 +282,20 @@ async function dev() {
278
282
  res.setHeader('Content-Type', 'text/html');
279
283
  res.end(html);
280
284
  });
281
- // --- WebSocket + HMR via BroadcastManager
285
+ // --- WebSocket + HMR
282
286
  const server = http_1.default.createServer(app);
283
287
  const broadcaster = new broadcastManager_1.BroadcastManager(server);
284
288
  chokidar_1.default.watch(path_1.default.join(appRoot, 'src'), { ignoreInitial: true }).on('change', async (file) => {
285
289
  console.log(chalk_1.default.yellow(`šŸ”„ Changed: ${file}`));
286
290
  transformCache.delete(file);
287
291
  for (const p of plugins) {
288
- await p.onHotUpdate?.(file, {
289
- broadcast: (msg) => broadcaster.broadcast(msg),
290
- });
292
+ await p.onHotUpdate?.(file, { broadcast: (msg) => broadcaster.broadcast(msg) });
291
293
  }
292
294
  broadcaster.broadcast({
293
295
  type: 'update',
294
296
  path: '/' + path_1.default.relative(appRoot, file).replace(/\\/g, '/'),
295
297
  });
296
298
  });
297
- // šŸš€ Start server
298
299
  server.listen(port, async () => {
299
300
  const url = `http://localhost:${port}`;
300
301
  console.log(chalk_1.default.cyan.bold('\nšŸš€ React Client Dev Server'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
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",