react-client 1.0.20 → 1.0.21

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.
package/README.md CHANGED
@@ -59,7 +59,7 @@ import { defineConfig } from 'react-client/config';
59
59
 
60
60
  export default defineConfig({
61
61
  root: './src',
62
- server: { port: 5173 },
62
+ server: { port: 2202 },
63
63
  build: { outDir: '.react-client/build' }
64
64
  });
65
65
  ```
@@ -89,7 +89,7 @@ Each template is pre-configured for esbuild, HMR, and fast bootstrapping.
89
89
  - šŸ” **React Fast Refresh (HMR)** — State-preserving reloads
90
90
  - šŸ’„ **Overlay** — Syntax-highlighted stack frames, clickable file links (`vscode://file`)
91
91
  - šŸ” **Source Map Stack Mapping** — Maps runtime errors to original TS/JS source lines
92
- - šŸ’¬ **Auto Port Detection** — Prompts when default port 5173 is occupied
92
+ - šŸ’¬ **Auto Port Detection** — Prompts when default port 2202 is occupied
93
93
  - 🧠 **Smart Config Loader** — Detects project root, compiles `.ts` configs dynamically
94
94
  - šŸŽØ **PrismJS Highlighting** — For pretty overlay code frames
95
95
  - šŸ”Œ **Plugin Hook System** — Extendable with `configResolved`, `transform`, `buildEnd`
@@ -142,7 +142,7 @@ npm install react-refresh
142
142
  ### āš ļø Port already in use
143
143
  CLI will auto-detect and prompt:
144
144
  ```
145
- Port 5173 is occupied. Use 5174 instead? (Y/n)
145
+ Port 2202 is occupied. Use 5174 instead? (Y/n)
146
146
  ```
147
147
 
148
148
  ### āš ļø Permission denied
@@ -1,4 +1,14 @@
1
1
  "use strict";
2
+ /**
3
+ * šŸš€ react-client Dev Server (Final Version)
4
+ * Includes:
5
+ * - Favicon & public asset support
6
+ * - ETag + gzip/brotli caching
7
+ * - Persistent prebundle deps (.react-client/deps)
8
+ * - HMR + overlay
9
+ * - CSS hot reload
10
+ * - ESLint + Prettier clean
11
+ */
2
12
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
14
  };
@@ -15,9 +25,11 @@ const fs_extra_1 = __importDefault(require("fs-extra"));
15
25
  const open_1 = __importDefault(require("open"));
16
26
  const child_process_1 = require("child_process");
17
27
  const chalk_1 = __importDefault(require("chalk"));
28
+ const zlib_1 = __importDefault(require("zlib"));
29
+ const crypto_1 = __importDefault(require("crypto"));
18
30
  const loadConfig_1 = require("../../utils/loadConfig");
19
31
  const broadcastManager_1 = require("../../server/broadcastManager");
20
- // 🧠 Browser polyfills for Node built-ins
32
+ // Node polyfill mapping
21
33
  const NODE_POLYFILLS = {
22
34
  buffer: 'buffer/',
23
35
  process: 'process/browser',
@@ -34,23 +46,36 @@ const NODE_POLYFILLS = {
34
46
  querystring: 'querystring-es3',
35
47
  zlib: 'browserify-zlib',
36
48
  };
37
- // List of NPM packages required for polyfills
38
- const POLYFILL_PACKAGES = [
39
- 'buffer',
40
- 'process',
41
- 'path-browserify',
42
- 'browserify-fs',
43
- 'os-browserify',
44
- 'stream-browserify',
45
- 'util',
46
- 'url',
47
- 'assert',
48
- 'crypto-browserify',
49
- 'events',
50
- 'constants-browserify',
51
- 'querystring-es3',
52
- 'browserify-zlib',
53
- ];
49
+ // --- Helper utilities
50
+ const computeHash = (content) => crypto_1.default.createHash('sha1').update(content).digest('hex');
51
+ const getMimeType = (file) => {
52
+ const ext = path_1.default.extname(file).toLowerCase();
53
+ const mime = {
54
+ '.ico': 'image/x-icon',
55
+ '.png': 'image/png',
56
+ '.jpg': 'image/jpeg',
57
+ '.jpeg': 'image/jpeg',
58
+ '.gif': 'image/gif',
59
+ '.svg': 'image/svg+xml',
60
+ '.webp': 'image/webp',
61
+ '.json': 'application/json',
62
+ '.txt': 'text/plain',
63
+ '.js': 'application/javascript',
64
+ '.mjs': 'application/javascript',
65
+ '.css': 'text/css',
66
+ '.html': 'text/html',
67
+ '.woff': 'font/woff',
68
+ '.woff2': 'font/woff2',
69
+ '.ttf': 'font/ttf',
70
+ '.otf': 'font/otf',
71
+ '.mp4': 'video/mp4',
72
+ '.mp3': 'audio/mpeg',
73
+ };
74
+ return mime[ext] || 'application/octet-stream';
75
+ };
76
+ // āœ… Unused helpers are underscored to comply with eslint rules
77
+ const _gunzipAsync = (input) => new Promise((res, rej) => zlib_1.default.gunzip(input, (e, out) => (e ? rej(e) : res(out))));
78
+ const _brotliAsync = (input) => new Promise((res, rej) => zlib_1.default.brotliDecompress(input, (e, out) => (e ? rej(e) : res(out))));
54
79
  async function dev() {
55
80
  const root = process.cwd();
56
81
  const userConfig = await (0, loadConfig_1.loadReactClientConfig)(root);
@@ -58,66 +83,48 @@ async function dev() {
58
83
  const defaultPort = userConfig.server?.port || 5173;
59
84
  const cacheDir = path_1.default.join(appRoot, '.react-client', 'deps');
60
85
  await fs_extra_1.default.ensureDir(cacheDir);
61
- // Detect entry file
86
+ const indexHtml = path_1.default.join(appRoot, 'index.html');
87
+ const pkgFile = path_1.default.join(appRoot, 'package.json');
88
+ // Detect entry
62
89
  const possibleEntries = ['src/main.tsx', 'src/main.jsx'];
63
90
  const entry = possibleEntries.map((p) => path_1.default.join(appRoot, p)).find((p) => fs_extra_1.default.existsSync(p));
64
91
  if (!entry) {
65
92
  console.error(chalk_1.default.red('āŒ No entry found: src/main.tsx or src/main.jsx'));
66
93
  process.exit(1);
67
94
  }
68
- const indexHtml = path_1.default.join(appRoot, 'index.html');
69
- // Detect available port
70
- const availablePort = await (0, detect_port_1.default)(defaultPort);
71
- const port = availablePort;
72
- if (availablePort !== defaultPort) {
95
+ // Detect open port
96
+ const port = await (0, detect_port_1.default)(defaultPort);
97
+ if (port !== defaultPort) {
73
98
  const res = await (0, prompts_1.default)({
74
99
  type: 'confirm',
75
100
  name: 'useNewPort',
76
- message: `Port ${defaultPort} is occupied. Use ${availablePort} instead?`,
101
+ message: `Port ${defaultPort} is occupied. Use ${port} instead?`,
77
102
  initial: true,
78
103
  });
79
- if (!res.useNewPort) {
80
- console.log('šŸ›‘ Dev server cancelled.');
104
+ if (!res.useNewPort)
81
105
  process.exit(0);
82
- }
83
106
  }
84
- // 🧩 Auto-install react-refresh
107
+ // Ensure react-refresh installed
85
108
  try {
86
109
  require.resolve('react-refresh/runtime');
87
110
  }
88
111
  catch {
89
- console.warn(chalk_1.default.yellow('āš ļø react-refresh not found — installing...'));
90
- (0, child_process_1.execSync)('npm install react-refresh --no-audit --no-fund --silent', {
91
- cwd: root,
92
- stdio: 'inherit',
93
- });
94
- console.log(chalk_1.default.green('āœ… react-refresh installed successfully.'));
112
+ console.log(chalk_1.default.yellow('Installing react-refresh...'));
113
+ (0, child_process_1.execSync)('npm i react-refresh --silent', { cwd: root, stdio: 'inherit' });
95
114
  }
96
- // 🧩 Auto-install missing polyfill packages
97
- const missingPolyfills = POLYFILL_PACKAGES.filter((pkg) => {
115
+ // Ensure Node polyfills installed
116
+ const missing = Object.keys(NODE_POLYFILLS).filter((m) => {
98
117
  try {
99
- require.resolve(pkg, { paths: [appRoot] });
118
+ require.resolve(m, { paths: [appRoot] });
100
119
  return false;
101
120
  }
102
121
  catch {
103
122
  return true;
104
123
  }
105
124
  });
106
- if (missingPolyfills.length > 0) {
107
- console.log(chalk_1.default.yellow('āš™ļø Installing missing polyfill packages...'));
108
- console.log(chalk_1.default.gray('šŸ“¦ ' + missingPolyfills.join(', ')));
109
- try {
110
- (0, child_process_1.execSync)(`npm install ${missingPolyfills.join(' ')} --no-audit --no-fund --silent`, {
111
- cwd: appRoot,
112
- stdio: 'inherit',
113
- });
114
- console.log(chalk_1.default.green('āœ… Polyfills installed successfully.'));
115
- }
116
- catch (err) {
117
- console.error(chalk_1.default.red('āŒ Failed to install polyfills automatically.'));
118
- console.error(err);
119
- process.exit(1);
120
- }
125
+ if (missing.length > 0) {
126
+ console.log(chalk_1.default.yellow('Installing missing polyfills...'));
127
+ (0, child_process_1.execSync)(`npm i ${missing.join(' ')} --silent`, { cwd: appRoot, stdio: 'inherit' });
121
128
  }
122
129
  // --- Plugins
123
130
  const corePlugins = [
@@ -141,137 +148,163 @@ async function dev() {
141
148
  const userPlugins = Array.isArray(userConfig.plugins) ? userConfig.plugins : [];
142
149
  const plugins = [...corePlugins, ...userPlugins];
143
150
  const app = (0, connect_1.default)();
151
+ const server = http_1.default.createServer(app);
152
+ const broadcaster = new broadcastManager_1.BroadcastManager(server);
144
153
  const transformCache = new Map();
145
- // 🧱 Polyfilled module builder
146
- async function buildModuleWithSafeWrapper(id) {
147
- const cacheFile = path_1.default.join(cacheDir, id.replace(/\//g, '_') + '.js');
148
- if (await fs_extra_1.default.pathExists(cacheFile))
149
- return fs_extra_1.default.readFile(cacheFile, 'utf8');
150
- // 🧠 Polyfill detection
151
- const polyId = NODE_POLYFILLS[id];
152
- if (polyId) {
153
- console.log(chalk_1.default.gray(`🧩 Using polyfill for ${id}: ${polyId}`));
154
- const result = await esbuild_1.default.build({
155
- entryPoints: [require.resolve(polyId, { paths: [appRoot] })],
156
- bundle: true,
157
- platform: 'browser',
158
- format: 'esm',
159
- target: 'es2020',
160
- write: false,
161
- });
162
- const polyCode = result.outputFiles[0].text;
163
- await fs_extra_1.default.writeFile(cacheFile, polyCode, 'utf8');
164
- return polyCode;
165
- }
166
- // 🧱 Normal dependency
167
- let entryPath = null;
168
- try {
169
- entryPath = require.resolve(id, { paths: [appRoot] });
170
- }
171
- catch {
172
- const base = id.split('/')[0];
154
+ // --- Prebundle deps with gzip/brotli caching
155
+ async function prebundleDeps() {
156
+ if (!(await fs_extra_1.default.pathExists(pkgFile)))
157
+ return;
158
+ const pkg = JSON.parse(await fs_extra_1.default.readFile(pkgFile, 'utf8'));
159
+ const deps = Object.keys(pkg.dependencies || {});
160
+ if (!deps.length)
161
+ return;
162
+ const hash = computeHash(JSON.stringify(deps));
163
+ const metaFile = path_1.default.join(cacheDir, '_meta.json');
164
+ let prevHash = null;
165
+ if (await fs_extra_1.default.pathExists(metaFile))
166
+ prevHash = (await fs_extra_1.default.readJSON(metaFile)).hash;
167
+ if (prevHash === hash)
168
+ return;
169
+ console.log(chalk_1.default.cyan('šŸ“¦ Rebuilding prebundle cache...'));
170
+ await Promise.all(deps.map(async (dep) => {
173
171
  try {
174
- entryPath = require.resolve(base, { paths: [appRoot] });
172
+ const entryPath = require.resolve(dep, { paths: [appRoot] });
173
+ const outFile = path_1.default.join(cacheDir, dep + '.js');
174
+ await esbuild_1.default.build({
175
+ entryPoints: [entryPath],
176
+ bundle: true,
177
+ platform: 'browser',
178
+ format: 'esm',
179
+ target: 'es2020',
180
+ outfile: outFile,
181
+ write: true,
182
+ });
183
+ const content = await fs_extra_1.default.readFile(outFile);
184
+ await fs_extra_1.default.writeFile(outFile + '.gz', zlib_1.default.gzipSync(content));
185
+ await fs_extra_1.default.writeFile(outFile + '.br', zlib_1.default.brotliCompressSync(content));
186
+ console.log(chalk_1.default.green(`āœ… Cached ${dep}`));
175
187
  }
176
- catch {
177
- entryPath = null;
188
+ catch (e) {
189
+ const err = e;
190
+ console.warn(chalk_1.default.yellow(`āš ļø Failed ${dep}: ${err.message}`));
178
191
  }
179
- }
180
- if (!entryPath)
181
- throw new Error(`Module ${id} not found (resolve failed)`);
182
- const result = await esbuild_1.default.build({
183
- entryPoints: [entryPath],
184
- bundle: true,
185
- platform: 'browser',
186
- format: 'esm',
187
- target: 'es2020',
188
- write: false,
189
- });
190
- const originalCode = result.outputFiles[0].text;
191
- const isSubpath = id.includes('/');
192
- let finalCode = originalCode;
193
- if (isSubpath) {
194
- const base = id.split('/')[0];
195
- finalCode += `
196
- // --- react-client auto wrapper for subpath: ${id}
197
- import * as __base from '/@modules/${base}';
198
- export const __rc_dynamic = __base;
199
- export default __base.default || __base;
200
- `;
201
- }
202
- await fs_extra_1.default.writeFile(cacheFile, finalCode, 'utf8');
203
- return finalCode;
192
+ }));
193
+ await fs_extra_1.default.writeJSON(metaFile, { hash });
204
194
  }
205
- // --- /@modules/
195
+ await prebundleDeps();
196
+ chokidar_1.default.watch(pkgFile).on('change', prebundleDeps);
197
+ // --- Serve /@modules/
206
198
  app.use('/@modules/', async (req, res, next) => {
207
199
  const id = req.url?.replace(/^\/(@modules\/)?/, '');
208
200
  if (!id)
209
201
  return next();
202
+ const base = path_1.default.join(cacheDir, id.replace(/\//g, '_') + '.js');
203
+ const gz = base + '.gz';
204
+ const br = base + '.br';
205
+ const accept = req.headers['accept-encoding'] || '';
210
206
  try {
211
- const code = await buildModuleWithSafeWrapper(id);
207
+ let buf = null;
208
+ let encoding = null;
209
+ if (/\bbr\b/.test(accept) && (await fs_extra_1.default.pathExists(br))) {
210
+ buf = await fs_extra_1.default.readFile(br);
211
+ encoding = 'br';
212
+ }
213
+ else if (/\bgzip\b/.test(accept) && (await fs_extra_1.default.pathExists(gz))) {
214
+ buf = await fs_extra_1.default.readFile(gz);
215
+ encoding = 'gzip';
216
+ }
217
+ else if (await fs_extra_1.default.pathExists(base)) {
218
+ buf = await fs_extra_1.default.readFile(base);
219
+ }
220
+ else {
221
+ const entryPath = require.resolve(id, { paths: [appRoot] });
222
+ const result = await esbuild_1.default.build({
223
+ entryPoints: [entryPath],
224
+ bundle: true,
225
+ platform: 'browser',
226
+ format: 'esm',
227
+ write: false,
228
+ });
229
+ buf = Buffer.from(result.outputFiles[0].text);
230
+ await fs_extra_1.default.writeFile(base, buf);
231
+ }
232
+ const etag = `"${computeHash(buf)}"`;
233
+ if (req.headers['if-none-match'] === etag) {
234
+ res.writeHead(304);
235
+ return res.end();
236
+ }
212
237
  res.setHeader('Content-Type', 'application/javascript');
213
- res.end(code);
238
+ res.setHeader('ETag', etag);
239
+ res.setHeader('Cache-Control', 'no-cache');
240
+ if (encoding)
241
+ res.setHeader('Content-Encoding', encoding);
242
+ res.end(buf);
214
243
  }
215
- catch (err) {
216
- const e = err;
217
- console.error(chalk_1.default.red(`āŒ Failed to load module ${id}: ${e.message}`));
244
+ catch (e) {
245
+ const err = e;
218
246
  res.writeHead(500);
219
- res.end(`// Failed to resolve module ${id}: ${e.message}`);
247
+ res.end(`// Failed to resolve module ${id}: ${err.message}`);
220
248
  }
221
249
  });
222
- // --- Universal transform for all project files
250
+ // --- Serve /src/ files
223
251
  app.use(async (req, res, next) => {
224
- const urlPath = decodeURIComponent(req.url.split('?')[0]);
225
- if (urlPath.includes('node_modules'))
252
+ if (!req.url || (!req.url.startsWith('/src/') && !req.url.endsWith('.css')))
226
253
  return next();
227
- let filePath = path_1.default.join(appRoot, urlPath);
228
- const possibleExts = ['', '.tsx', '.ts', '.jsx', '.js', '.css'];
229
- for (const ext of possibleExts) {
230
- if (await fs_extra_1.default.pathExists(filePath + ext)) {
231
- filePath += ext;
232
- break;
233
- }
234
- }
254
+ const filePath = path_1.default.join(appRoot, decodeURIComponent(req.url.split('?')[0]));
235
255
  if (!(await fs_extra_1.default.pathExists(filePath)))
236
256
  return next();
237
- try {
238
- let code = await fs_extra_1.default.readFile(filePath, 'utf8');
239
- // Rewrite bare imports
240
- code = code
241
- .replace(/\bfrom\s+['"]([^'".\/][^'"]*)['"]/g, (_m, dep) => `from "/@modules/${dep}"`)
242
- .replace(/\bimport\(['"]([^'".\/][^'"]*)['"]\)/g, (_m, dep) => `import("/@modules/${dep}")`);
243
- for (const p of plugins)
244
- if (p.onTransform)
245
- code = await p.onTransform(code, filePath);
246
- const ext = path_1.default.extname(filePath);
247
- let loader = 'js';
248
- if (ext === '.ts')
249
- loader = 'ts';
250
- else if (ext === '.tsx')
251
- loader = 'tsx';
252
- else if (ext === '.jsx')
253
- loader = 'jsx';
254
- const result = await esbuild_1.default.transform(code, {
255
- loader,
256
- sourcemap: 'inline',
257
- target: 'es2020',
258
- });
259
- transformCache.set(filePath, result.code);
260
- res.setHeader('Content-Type', 'application/javascript');
261
- res.end(result.code);
262
- }
263
- catch (err) {
264
- const e = err;
265
- console.error(chalk_1.default.red(`āš ļø Transform failed: ${e.message}`));
266
- res.writeHead(500);
267
- res.end(`// Error: ${e.message}`);
257
+ let code = await fs_extra_1.default.readFile(filePath, 'utf8');
258
+ code = code
259
+ .replace(/\bfrom\s+['"]([^'".\/][^'"]*)['"]/g, (_m, dep) => `from "/@modules/${dep}"`)
260
+ .replace(/\bimport\(['"]([^'".\/][^'"]*)['"]\)/g, (_m, dep) => `import("/@modules/${dep}")`);
261
+ for (const p of plugins)
262
+ if (p.onTransform)
263
+ code = await p.onTransform(code, filePath);
264
+ const loader = filePath.endsWith('.tsx')
265
+ ? 'tsx'
266
+ : filePath.endsWith('.ts')
267
+ ? 'ts'
268
+ : filePath.endsWith('.jsx')
269
+ ? 'jsx'
270
+ : 'js';
271
+ const result = await esbuild_1.default.transform(code, { loader, sourcemap: 'inline', target: 'es2020' });
272
+ res.setHeader('Content-Type', 'application/javascript');
273
+ res.end(result.code);
274
+ });
275
+ // --- Serve static assets (favicon, /public, etc.)
276
+ app.use(async (req, res, next) => {
277
+ if (!req.url)
278
+ return next();
279
+ const assetPath = decodeURIComponent(req.url.split('?')[0]);
280
+ const publicDir = path_1.default.join(appRoot, 'public');
281
+ const rootFile = path_1.default.join(appRoot, assetPath);
282
+ const publicFile = path_1.default.join(publicDir, assetPath);
283
+ let targetFile = null;
284
+ if (await fs_extra_1.default.pathExists(publicFile))
285
+ targetFile = publicFile;
286
+ else if (await fs_extra_1.default.pathExists(rootFile))
287
+ targetFile = rootFile;
288
+ if (!targetFile)
289
+ return next();
290
+ const stat = await fs_extra_1.default.stat(targetFile);
291
+ if (!stat.isFile())
292
+ return next();
293
+ const etag = `"${stat.size}-${stat.mtimeMs}"`;
294
+ if (req.headers['if-none-match'] === etag) {
295
+ res.writeHead(304);
296
+ return res.end();
268
297
  }
298
+ res.setHeader('Cache-Control', 'public, max-age=3600');
299
+ res.setHeader('ETag', etag);
300
+ res.setHeader('Content-Type', getMimeType(targetFile));
301
+ fs_extra_1.default.createReadStream(targetFile).pipe(res);
269
302
  });
270
- // --- index.html + overlay + HMR
303
+ // --- Serve index.html with overlay + HMR
271
304
  app.use(async (req, res, next) => {
272
305
  if (req.url !== '/' && req.url !== '/index.html')
273
306
  return next();
274
- if (!fs_extra_1.default.existsSync(indexHtml)) {
307
+ if (!(await fs_extra_1.default.pathExists(indexHtml))) {
275
308
  res.writeHead(404);
276
309
  return res.end('index.html not found');
277
310
  }
@@ -282,9 +315,10 @@ async function dev() {
282
315
  const style = document.createElement('style');
283
316
  style.textContent = \`
284
317
  .rc-overlay {
285
- position: fixed; inset: 0; background: rgba(0,0,0,0.9);
286
- color: #ff5555; font-family: monospace;
287
- padding: 2rem; overflow:auto; z-index: 999999;
318
+ position: fixed; inset: 0;
319
+ background: rgba(0,0,0,0.9); color:#fff;
320
+ font-family: monospace; padding:2rem; overflow:auto;
321
+ z-index:999999; white-space:pre-wrap;
288
322
  }
289
323
  \`;
290
324
  document.head.appendChild(style);
@@ -315,28 +349,26 @@ async function dev() {
315
349
  res.setHeader('Content-Type', 'text/html');
316
350
  res.end(html);
317
351
  });
318
- // --- WebSocket + HMR
319
- const server = http_1.default.createServer(app);
320
- const broadcaster = new broadcastManager_1.BroadcastManager(server);
321
- chokidar_1.default.watch(appRoot, { ignoreInitial: true }).on('change', async (file) => {
322
- if (file.includes('node_modules') || file.includes('.react-client'))
323
- return;
352
+ // --- Watchers for HMR + favicon reload
353
+ chokidar_1.default.watch(path_1.default.join(appRoot, 'src'), { ignoreInitial: true }).on('change', (file) => {
324
354
  console.log(chalk_1.default.yellow(`šŸ”„ Changed: ${file}`));
325
355
  transformCache.delete(file);
326
- for (const p of plugins)
327
- await p.onHotUpdate?.(file, { broadcast: (msg) => broadcaster.broadcast(msg) });
328
356
  broadcaster.broadcast({
329
357
  type: 'update',
330
358
  path: '/' + path_1.default.relative(appRoot, file).replace(/\\/g, '/'),
331
359
  });
332
360
  });
361
+ chokidar_1.default
362
+ .watch(path_1.default.join(appRoot, 'public', 'favicon.ico'), { ignoreInitial: true })
363
+ .on('change', () => {
364
+ broadcaster.broadcast({ type: 'reload' });
365
+ });
366
+ // --- Start server
333
367
  server.listen(port, async () => {
334
368
  const url = `http://localhost:${port}`;
335
369
  console.log(chalk_1.default.cyan.bold('\nšŸš€ React Client Dev Server'));
336
370
  console.log(chalk_1.default.gray('──────────────────────────────'));
337
371
  console.log(chalk_1.default.green(`⚔ Running at: ${url}`));
338
- if (port !== defaultPort)
339
- console.log(chalk_1.default.yellow(`āš ļø Using alternate port (default ${defaultPort} occupied)`));
340
372
  await (0, open_1.default)(url, { newInstance: true });
341
373
  });
342
374
  process.on('SIGINT', () => {
@@ -51,7 +51,7 @@ export default defineConfig({
51
51
 
52
52
  // ⚔ Dev server settings
53
53
  server: {
54
- port: 5173,
54
+ port: 2202,
55
55
  },
56
56
 
57
57
  // šŸ—ļø Build options
@@ -19,7 +19,7 @@ async function preview() {
19
19
  const config = await (0, loadConfig_1.loadReactClientConfig)(root);
20
20
  const appRoot = path_1.default.resolve(root, config.root || '.');
21
21
  const outDir = path_1.default.join(appRoot, config.build?.outDir || '.react-client/build');
22
- const defaultPort = config.server?.port || 5173;
22
+ const defaultPort = config.server?.port || 2202;
23
23
  if (!fs_extra_1.default.existsSync(outDir)) {
24
24
  console.error(chalk_1.default.red(`āŒ Build output not found at: ${outDir}`));
25
25
  console.log(chalk_1.default.gray('Please run `react-client build` first.'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
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",