react-client 1.0.22 → 1.0.23

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.
@@ -1,14 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * šŸš€ React Client Dev Server — Final Version
4
- * ------------------------------------------
5
- * āœ… Local overlay-runtime.js (Prism + stack mapping)
6
- * āœ… Dynamic /@runtime/overlay-runtime.js alias
7
- * āœ… Automatic HTML injection for overlay + HMR
8
- * āœ… Prebundle cache (.react-client/deps)
9
- * āœ… CSS HMR, relative & bare import handling
10
- * āœ… Favicon & public assets serving
11
- */
12
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
13
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
4
  };
@@ -25,30 +15,8 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
25
15
  const open_1 = __importDefault(require("open"));
26
16
  const child_process_1 = require("child_process");
27
17
  const chalk_1 = __importDefault(require("chalk"));
28
- const zlib_1 = __importDefault(require("zlib"));
29
- const crypto_1 = __importDefault(require("crypto"));
30
18
  const loadConfig_1 = require("../../utils/loadConfig");
31
19
  const broadcastManager_1 = require("../../server/broadcastManager");
32
- const computeHash = (content) => crypto_1.default.createHash('sha1').update(content).digest('hex');
33
- const getMimeType = (file) => {
34
- const ext = path_1.default.extname(file).toLowerCase();
35
- const mime = {
36
- '.ico': 'image/x-icon',
37
- '.png': 'image/png',
38
- '.jpg': 'image/jpeg',
39
- '.jpeg': 'image/jpeg',
40
- '.gif': 'image/gif',
41
- '.svg': 'image/svg+xml',
42
- '.webp': 'image/webp',
43
- '.json': 'application/json',
44
- '.txt': 'text/plain',
45
- '.js': 'application/javascript',
46
- '.mjs': 'application/javascript',
47
- '.css': 'text/css',
48
- '.html': 'text/html',
49
- };
50
- return mime[ext] || 'application/octet-stream';
51
- };
52
20
  async function dev() {
53
21
  const root = process.cwd();
54
22
  const userConfig = await (0, loadConfig_1.loadReactClientConfig)(root);
@@ -56,36 +24,43 @@ async function dev() {
56
24
  const defaultPort = userConfig.server?.port || 5173;
57
25
  const cacheDir = path_1.default.join(appRoot, '.react-client', 'deps');
58
26
  const pkgFile = path_1.default.join(appRoot, 'package.json');
59
- const indexHtml = path_1.default.join(appRoot, 'index.html');
60
27
  await fs_extra_1.default.ensureDir(cacheDir);
61
- // āœ… Detect entry
28
+ // āœ… Detect entry (main.tsx or main.jsx)
62
29
  const possibleEntries = ['src/main.tsx', 'src/main.jsx'];
63
30
  const entry = possibleEntries.map((p) => path_1.default.join(appRoot, p)).find((p) => fs_extra_1.default.existsSync(p));
64
31
  if (!entry) {
65
32
  console.error(chalk_1.default.red('āŒ No entry found: src/main.tsx or src/main.jsx'));
66
33
  process.exit(1);
67
34
  }
68
- // āœ… Detect free port
69
- const port = await (0, detect_port_1.default)(defaultPort);
70
- if (port !== defaultPort) {
35
+ const indexHtml = path_1.default.join(appRoot, 'index.html');
36
+ // āœ… Detect open port
37
+ const availablePort = await (0, detect_port_1.default)(defaultPort);
38
+ const port = availablePort;
39
+ if (availablePort !== defaultPort) {
71
40
  const res = await (0, prompts_1.default)({
72
41
  type: 'confirm',
73
42
  name: 'useNewPort',
74
- message: `Port ${defaultPort} is occupied. Use ${port} instead?`,
43
+ message: `Port ${defaultPort} is occupied. Use ${availablePort} instead?`,
75
44
  initial: true,
76
45
  });
77
- if (!res.useNewPort)
46
+ if (!res.useNewPort) {
47
+ console.log('šŸ›‘ Dev server cancelled.');
78
48
  process.exit(0);
49
+ }
79
50
  }
80
- // āœ… Ensure react-refresh
51
+ // āœ… Ensure react-refresh installed
81
52
  try {
82
53
  require.resolve('react-refresh/runtime');
83
54
  }
84
55
  catch {
85
- console.log(chalk_1.default.yellow('Installing react-refresh...'));
86
- (0, child_process_1.execSync)('npm i react-refresh --silent', { cwd: root, stdio: 'inherit' });
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.'));
87
62
  }
88
- // āœ… Core + user plugins
63
+ // āœ… Core + User Plugins
89
64
  const corePlugins = [
90
65
  {
91
66
  name: 'css-hmr',
@@ -107,26 +82,43 @@ async function dev() {
107
82
  const userPlugins = Array.isArray(userConfig.plugins) ? userConfig.plugins : [];
108
83
  const plugins = [...corePlugins, ...userPlugins];
109
84
  const app = (0, connect_1.default)();
110
- const server = http_1.default.createServer(app);
111
- const broadcaster = new broadcastManager_1.BroadcastManager(server);
112
85
  const transformCache = new Map();
113
- // 🧱 Persistent prebundle cache
114
- async function prebundleDeps() {
115
- if (!(await fs_extra_1.default.pathExists(pkgFile)))
116
- return;
117
- const pkg = JSON.parse(await fs_extra_1.default.readFile(pkgFile, 'utf8'));
118
- const deps = Object.keys(pkg.dependencies || {});
119
- if (!deps.length)
86
+ // āœ… Analyze dependency graph recursively
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);
106
+ }
107
+ }
108
+ return seen;
109
+ }
110
+ // āœ… Smart prebundling cache
111
+ async function prebundleDeps(deps) {
112
+ if (!deps.size)
120
113
  return;
121
- const hash = computeHash(JSON.stringify(deps));
122
- const metaFile = path_1.default.join(cacheDir, '_meta.json');
123
- let prevHash = null;
124
- if (await fs_extra_1.default.pathExists(metaFile))
125
- prevHash = (await fs_extra_1.default.readJSON(metaFile)).hash;
126
- if (prevHash === hash)
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.'));
127
118
  return;
128
- console.log(chalk_1.default.cyan('šŸ“¦ Rebuilding prebundle cache...'));
129
- await Promise.all(deps.map(async (dep) => {
119
+ }
120
+ console.log(chalk_1.default.cyan('šŸ“¦ Prebundling:'), missing.join(', '));
121
+ await Promise.all(missing.map(async (dep) => {
130
122
  try {
131
123
  const entryPath = require.resolve(dep, { paths: [appRoot] });
132
124
  const outFile = path_1.default.join(cacheDir, dep + '.js');
@@ -135,53 +127,28 @@ async function dev() {
135
127
  bundle: true,
136
128
  platform: 'browser',
137
129
  format: 'esm',
138
- target: 'es2020',
139
130
  outfile: outFile,
140
131
  write: true,
132
+ target: 'es2020',
141
133
  });
142
- const content = await fs_extra_1.default.readFile(outFile);
143
- await fs_extra_1.default.writeFile(outFile + '.gz', zlib_1.default.gzipSync(content));
144
- await fs_extra_1.default.writeFile(outFile + '.br', zlib_1.default.brotliCompressSync(content));
145
134
  console.log(chalk_1.default.green(`āœ… Cached ${dep}`));
146
135
  }
147
- catch (e) {
148
- const err = e;
149
- console.warn(chalk_1.default.yellow(`āš ļø Skipped ${dep}: ${err.message}`));
136
+ catch (err) {
137
+ const e = err;
138
+ console.warn(chalk_1.default.yellow(`āš ļø Skipped ${dep}: ${e.message}`));
150
139
  }
151
140
  }));
152
- await fs_extra_1.default.writeJSON(metaFile, { hash });
153
141
  }
154
- await prebundleDeps();
155
- chokidar_1.default.watch(pkgFile).on('change', prebundleDeps);
156
- // 🧩 Serve local overlay runtime
157
- app.use('/@runtime/overlay-runtime.js', async (req, res) => {
158
- const overlayPath = path_1.default.join(appRoot, 'src/runtime/overlay-runtime.js');
159
- try {
160
- if (!(await fs_extra_1.default.pathExists(overlayPath))) {
161
- res.writeHead(404);
162
- return res.end(`// Overlay runtime not found: ${overlayPath}`);
163
- }
164
- let code = await fs_extra_1.default.readFile(overlayPath, 'utf8');
165
- // Transform bare imports → /@modules/*
166
- code = code
167
- .replace(/\bfrom\s+['"]([^'".\/][^'"]*)['"]/g, (_m, dep) => `from "/@modules/${dep}"`)
168
- .replace(/\bimport\(['"]([^'".\/][^'"]*)['"]\)/g, (_m, dep) => `import("/@modules/${dep}")`);
169
- const result = await esbuild_1.default.transform(code, {
170
- loader: 'js',
171
- sourcemap: 'inline',
172
- target: 'es2020',
173
- });
174
- res.setHeader('Content-Type', 'application/javascript');
175
- res.end(result.code);
176
- }
177
- catch (err) {
178
- const e = err;
179
- console.error(chalk_1.default.red(`āŒ Failed to load overlay runtime: ${e.message}`));
180
- res.writeHead(500);
181
- res.end(`// Failed to load overlay runtime: ${e.message}`);
182
- }
142
+ // āœ… Initial dependency prebundle
143
+ const deps = await analyzeGraph(entry);
144
+ await prebundleDeps(deps);
145
+ // āœ… Auto re-prebundle when package.json changes
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);
183
150
  });
184
- // 🧠 Serve /@modules/
151
+ // āœ… Serve /@modules/
185
152
  app.use('/@modules/', async (req, res, next) => {
186
153
  const id = req.url?.replace(/^\/(@modules\/)?/, '');
187
154
  if (!id)
@@ -206,42 +173,38 @@ async function dev() {
206
173
  res.setHeader('Content-Type', 'application/javascript');
207
174
  res.end(code);
208
175
  }
209
- catch (e) {
210
- const err = e;
211
- console.error(chalk_1.default.red(`āŒ Failed to load module ${id}: ${err.message}`));
176
+ catch (err) {
177
+ const e = err;
178
+ console.error(chalk_1.default.red(`āŒ Failed to load module ${id}: ${e.message}`));
212
179
  res.writeHead(500);
213
- res.end(`// Failed to resolve module ${id}: ${err.message}`);
180
+ res.end(`// Failed to resolve module ${id}: ${e.message}`);
214
181
  }
215
182
  });
216
- // 🧩 Serve /src/ and .css files dynamically
183
+ // āœ… Serve /src files dynamically
217
184
  app.use(async (req, res, next) => {
218
185
  if (!req.url || (!req.url.startsWith('/src/') && !req.url.endsWith('.css')))
219
186
  return next();
220
187
  const rawPath = decodeURIComponent(req.url.split('?')[0]);
221
- const filePath = path_1.default.join(appRoot, rawPath);
188
+ let filePath = path_1.default.join(appRoot, rawPath);
222
189
  const possibleExts = ['', '.tsx', '.ts', '.jsx', '.js'];
223
- let resolvedPath = null;
224
190
  for (const ext of possibleExts) {
225
- const candidate = filePath + ext;
226
- if (await fs_extra_1.default.pathExists(candidate)) {
227
- resolvedPath = candidate;
191
+ if (await fs_extra_1.default.pathExists(filePath + ext)) {
192
+ filePath += ext;
228
193
  break;
229
194
  }
230
195
  }
231
- if (!resolvedPath) {
232
- res.writeHead(404);
233
- return res.end(`// File not found: ${filePath}`);
234
- }
196
+ if (!(await fs_extra_1.default.pathExists(filePath)))
197
+ return next();
235
198
  try {
236
- let code = await fs_extra_1.default.readFile(resolvedPath, 'utf8');
199
+ let code = await fs_extra_1.default.readFile(filePath, 'utf8');
237
200
  // Rewrite bare imports → /@modules/*
238
201
  code = code
239
202
  .replace(/\bfrom\s+['"]([^'".\/][^'"]*)['"]/g, (_m, dep) => `from "/@modules/${dep}"`)
240
203
  .replace(/\bimport\(['"]([^'".\/][^'"]*)['"]\)/g, (_m, dep) => `import("/@modules/${dep}")`);
241
204
  for (const p of plugins)
242
205
  if (p.onTransform)
243
- code = await p.onTransform(code, resolvedPath);
244
- const ext = path_1.default.extname(resolvedPath);
206
+ code = await p.onTransform(code, filePath);
207
+ const ext = path_1.default.extname(filePath);
245
208
  let loader = 'js';
246
209
  if (ext === '.ts')
247
210
  loader = 'ts';
@@ -254,6 +217,7 @@ async function dev() {
254
217
  sourcemap: 'inline',
255
218
  target: 'es2020',
256
219
  });
220
+ transformCache.set(filePath, result.code);
257
221
  res.setHeader('Content-Type', 'application/javascript');
258
222
  res.end(result.code);
259
223
  }
@@ -264,31 +228,17 @@ async function dev() {
264
228
  res.end(`// Error: ${e.message}`);
265
229
  }
266
230
  });
267
- // šŸ–¼ļø Serve static assets (favicon + public)
268
- app.use(async (req, res, next) => {
269
- if (!req.url)
270
- return next();
271
- const publicDir = path_1.default.join(appRoot, 'public');
272
- const targetFile = path_1.default.join(publicDir, decodeURIComponent(req.url.split('?')[0]));
273
- if (!(await fs_extra_1.default.pathExists(targetFile)))
274
- return next();
275
- const stat = await fs_extra_1.default.stat(targetFile);
276
- if (!stat.isFile())
277
- return next();
278
- res.setHeader('Content-Type', getMimeType(targetFile));
279
- fs_extra_1.default.createReadStream(targetFile).pipe(res);
280
- });
281
- // 🧩 Serve index.html + overlay + HMR
231
+ // āœ… Serve index.html + overlay + runtime
282
232
  app.use(async (req, res, next) => {
283
233
  if (req.url !== '/' && req.url !== '/index.html')
284
234
  return next();
285
- if (!(await fs_extra_1.default.pathExists(indexHtml))) {
235
+ if (!fs_extra_1.default.existsSync(indexHtml)) {
286
236
  res.writeHead(404);
287
237
  return res.end('index.html not found');
288
238
  }
289
239
  let html = await fs_extra_1.default.readFile(indexHtml, 'utf8');
290
240
  html = html.replace('</body>', `
291
- <script type="module" src="/@runtime/overlay-runtime.js"></script>
241
+ <script type="module" src="/src/runtime/overlay-runtime.js"></script>
292
242
  <script type="module">
293
243
  const ws = new WebSocket("ws://" + location.host);
294
244
  ws.onmessage = (e) => {
@@ -305,21 +255,19 @@ async function dev() {
305
255
  res.setHeader('Content-Type', 'text/html');
306
256
  res.end(html);
307
257
  });
308
- // ā™»ļø Watchers
309
- chokidar_1.default.watch(path_1.default.join(appRoot, 'src'), { ignoreInitial: true }).on('change', (file) => {
258
+ // āœ… WebSocket + HMR
259
+ const server = http_1.default.createServer(app);
260
+ const broadcaster = new broadcastManager_1.BroadcastManager(server);
261
+ chokidar_1.default.watch(path_1.default.join(appRoot, 'src'), { ignoreInitial: true }).on('change', async (file) => {
310
262
  console.log(chalk_1.default.yellow(`šŸ”„ Changed: ${file}`));
311
263
  transformCache.delete(file);
264
+ for (const p of plugins)
265
+ await p.onHotUpdate?.(file, { broadcast: (msg) => broadcaster.broadcast(msg) });
312
266
  broadcaster.broadcast({
313
267
  type: 'update',
314
268
  path: '/' + path_1.default.relative(appRoot, file).replace(/\\/g, '/'),
315
269
  });
316
270
  });
317
- chokidar_1.default
318
- .watch(path_1.default.join(appRoot, 'src/runtime/overlay-runtime.js'), { ignoreInitial: true })
319
- .on('change', () => {
320
- console.log(chalk_1.default.magenta('ā™»ļø Overlay runtime updated — reloading browser...'));
321
- broadcaster.broadcast({ type: 'reload' });
322
- });
323
271
  server.listen(port, async () => {
324
272
  const url = `http://localhost:${port}`;
325
273
  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.22",
3
+ "version": "1.0.23",
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",