react-client 1.0.10 → 1.0.12

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.
@@ -13,26 +13,27 @@ const detect_port_1 = __importDefault(require("detect-port"));
13
13
  const prompts_1 = __importDefault(require("prompts"));
14
14
  const path_1 = __importDefault(require("path"));
15
15
  const fs_extra_1 = __importDefault(require("fs-extra"));
16
- const trace_mapping_1 = require("@jridgewell/trace-mapping");
17
16
  const loadConfig_1 = require("../../utils/loadConfig");
18
17
  const open_1 = __importDefault(require("open"));
19
18
  const child_process_1 = require("child_process");
20
19
  const chalk_1 = __importDefault(require("chalk"));
21
20
  async function dev() {
22
21
  const root = process.cwd();
23
- // 🧩 Load config
22
+ // 🧩 Load user config
24
23
  const userConfig = await (0, loadConfig_1.loadReactClientConfig)(root);
25
24
  const appRoot = path_1.default.resolve(root, userConfig.root || '.');
26
25
  const defaultPort = userConfig.server?.port || 5173;
27
26
  const outDir = path_1.default.join(appRoot, userConfig.build?.outDir || '.react-client/dev');
28
- const entry = path_1.default.join(appRoot, 'src', 'main.tsx');
29
- const indexHtml = path_1.default.join(appRoot, 'index.html');
30
- if (!fs_extra_1.default.existsSync(entry)) {
31
- console.error(chalk_1.default.red('❌ Entry not found: src/main.tsx'));
27
+ // ✅ Dynamically detect entry (main.tsx or main.jsx)
28
+ const possibleEntries = ['src/main.tsx', 'src/main.jsx'];
29
+ const entry = possibleEntries.map((p) => path_1.default.join(appRoot, p)).find((p) => fs_extra_1.default.existsSync(p));
30
+ if (!entry) {
31
+ console.error(chalk_1.default.red('❌ No entry found: src/main.tsx or src/main.jsx'));
32
32
  process.exit(1);
33
33
  }
34
+ const indexHtml = path_1.default.join(appRoot, 'index.html');
34
35
  await fs_extra_1.default.ensureDir(outDir);
35
- // 🧠 Detect open port
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) {
@@ -72,7 +73,7 @@ async function dev() {
72
73
  }
73
74
  }
74
75
  const reactRefreshRuntime = safeResolveReactRefresh();
75
- // 🏗️ esbuild context
76
+ // 🏗️ Create esbuild context
76
77
  const ctx = await esbuild_1.default.context({
77
78
  entryPoints: [entry],
78
79
  bundle: true,
@@ -80,14 +81,14 @@ async function dev() {
80
81
  outdir: outDir,
81
82
  define: { 'process.env.NODE_ENV': '"development"' },
82
83
  loader: { '.ts': 'ts', '.tsx': 'tsx', '.js': 'jsx', '.jsx': 'jsx' },
83
- entryNames: '[name]', // ✅ output main.js (not src/main.js)
84
+ entryNames: '[name]',
84
85
  assetNames: 'assets/[name]',
85
86
  });
86
87
  await ctx.watch();
87
88
  console.log(chalk_1.default.gray('📦 Watching and building dev bundle...'));
88
89
  console.log(chalk_1.default.gray(' Output dir:'), chalk_1.default.blue(outDir));
89
90
  console.log(chalk_1.default.gray(' Entry file:'), chalk_1.default.yellow(entry));
90
- // 🌐 connect server
91
+ // 🌐 Connect server setup
91
92
  const app = (0, connect_1.default)();
92
93
  // 🛡 Security headers
93
94
  app.use((_req, res, next) => {
@@ -95,11 +96,12 @@ async function dev() {
95
96
  res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
96
97
  next();
97
98
  });
99
+ // 🧠 In-memory cache for /@modules
100
+ const moduleCache = new Map();
98
101
  // 1️⃣ Serve react-refresh runtime with safe browser shim
99
102
  app.use('/@react-refresh', async (_req, res) => {
100
103
  const runtime = await fs_extra_1.default.readFile(reactRefreshRuntime, 'utf8');
101
104
  const shim = `
102
- // React Refresh browser shims
103
105
  window.process = window.process || { env: { NODE_ENV: 'development' } };
104
106
  window.module = { exports: {} };
105
107
  window.global = window;
@@ -108,67 +110,73 @@ async function dev() {
108
110
  res.setHeader('Content-Type', 'application/javascript');
109
111
  res.end(shim + '\n' + runtime);
110
112
  });
111
- // 2️⃣ Serve PrismJS for code highlighting (overlay)
112
- app.use('/@prismjs', async (_req, res) => {
113
- const prismPath = require.resolve('prismjs/prism.js');
114
- const css = await fs_extra_1.default.readFile(require.resolve('prismjs/themes/prism-tomorrow.css'), 'utf8');
115
- const js = await fs_extra_1.default.readFile(prismPath, 'utf8');
116
- res.setHeader('Content-Type', 'application/javascript');
117
- res.end(`
118
- (function(){
119
- const style = document.createElement('style');
120
- style.textContent = \`${css}\`;
121
- document.head.appendChild(style);
122
- ${js}
123
- })();
124
- `);
125
- });
126
- // 3️⃣ Source map resolver (for overlay stack trace)
127
- app.use('/@source-map', async (req, res) => {
128
- const url = new URL(req.url ?? '', `http://localhost:${port}`);
129
- const file = url.searchParams.get('file');
130
- const line = Number(url.searchParams.get('line'));
131
- const column = Number(url.searchParams.get('column'));
132
- if (!file) {
133
- res.writeHead(400);
134
- res.end('Missing ?file parameter');
113
+ // 2️⃣ Bare module resolver with memory cache
114
+ app.use('/@modules/', async (req, res, next) => {
115
+ const id = req.url?.replace(/^\/@modules\//, '');
116
+ if (!id)
117
+ return next();
118
+ if (moduleCache.has(id)) {
119
+ res.setHeader('Content-Type', 'application/javascript');
120
+ res.end(moduleCache.get(id));
135
121
  return;
136
122
  }
137
- const mapPath = path_1.default.join(outDir, file + '.map');
138
- if (!fs_extra_1.default.existsSync(mapPath)) {
139
- res.writeHead(404);
140
- res.end('Map not found');
141
- return;
123
+ try {
124
+ const entryPath = require.resolve(id, { paths: [appRoot] });
125
+ const out = await esbuild_1.default.build({
126
+ entryPoints: [entryPath],
127
+ bundle: true,
128
+ write: false,
129
+ platform: 'browser',
130
+ format: 'esm',
131
+ target: 'es2020',
132
+ });
133
+ const code = out.outputFiles[0].text;
134
+ moduleCache.set(id, code); // ✅ cache module
135
+ res.setHeader('Content-Type', 'application/javascript');
136
+ res.end(code);
137
+ }
138
+ catch (err) {
139
+ const msg = err instanceof Error ? err.message : String(err);
140
+ console.error(chalk_1.default.red(`Failed to resolve module ${id}: ${msg}`));
141
+ res.writeHead(500);
142
+ res.end(`// Could not resolve module ${id}`);
142
143
  }
144
+ });
145
+ // 3️⃣ Serve /src/* files — on-the-fly transform + bare import rewrite
146
+ app.use(async (req, res, next) => {
147
+ if (!req.url || !req.url.startsWith('/src/'))
148
+ return next();
143
149
  try {
144
- const mapJson = JSON.parse(await fs_extra_1.default.readFile(mapPath, 'utf8'));
145
- const traceMap = new trace_mapping_1.TraceMap(mapJson);
146
- const pos = (0, trace_mapping_1.originalPositionFor)(traceMap, { line, column });
147
- if (!pos.source) {
148
- res.writeHead(404);
149
- res.end('Source not found');
150
- return;
151
- }
152
- const absSource = path_1.default.resolve(outDir, '../', pos.source);
153
- let snippet = '';
154
- if (await fs_extra_1.default.pathExists(absSource)) {
155
- const lines = (await fs_extra_1.default.readFile(absSource, 'utf8')).split('\n');
156
- const start = Math.max((pos.line || 1) - 3, 0);
157
- const end = Math.min(lines.length, (pos.line || 1) + 2);
158
- snippet = lines
159
- .slice(start, end)
160
- .map((l, i) => `<span class="line-number">${start + i + 1}</span> ${l
161
- .replace(/</g, '&lt;')
162
- .replace(/>/g, '&gt;')}`)
163
- .join('\\n');
164
- }
165
- res.setHeader('Content-Type', 'application/json');
166
- res.end(JSON.stringify({ ...pos, snippet }));
150
+ const filePath = path_1.default.join(appRoot, decodeURIComponent(req.url.split('?')[0]));
151
+ if (!(await fs_extra_1.default.pathExists(filePath)))
152
+ return next();
153
+ let code = await fs_extra_1.default.readFile(filePath, 'utf8');
154
+ const ext = path_1.default.extname(filePath).toLowerCase();
155
+ // 🪄 Rewrite bare imports → /@modules/
156
+ code = code.replace(/from\s+['"]([^'".\/][^'"]*)['"]/g, (_match, dep) => `from "/@modules/${dep}"`);
157
+ let loader = 'js';
158
+ if (ext === '.ts')
159
+ loader = 'ts';
160
+ else if (ext === '.tsx')
161
+ loader = 'tsx';
162
+ else if (ext === '.jsx')
163
+ loader = 'jsx';
164
+ const transformed = await esbuild_1.default.transform(code, {
165
+ loader,
166
+ sourcemap: 'inline',
167
+ sourcefile: req.url,
168
+ target: 'es2020',
169
+ jsxFactory: 'React.createElement',
170
+ jsxFragment: 'React.Fragment',
171
+ });
172
+ res.setHeader('Content-Type', 'application/javascript');
173
+ res.end(transformed.code);
167
174
  }
168
175
  catch (err) {
169
176
  const msg = err instanceof Error ? err.message : String(err);
177
+ console.error('Error serving /src file:', msg);
170
178
  res.writeHead(500);
171
- res.end(JSON.stringify({ error: msg }));
179
+ res.end(`// Error: ${msg}`);
172
180
  }
173
181
  });
174
182
  // 4️⃣ Serve index.html with injected refresh + HMR
@@ -183,7 +191,6 @@ async function dev() {
183
191
  html = html.replace('</body>', `
184
192
  <script type="module">
185
193
  import "/@react-refresh";
186
- import "/@prismjs";
187
194
  const ws = new WebSocket("ws://" + location.host);
188
195
  ws.onmessage = async (e) => {
189
196
  const msg = JSON.parse(e.data);
@@ -208,7 +215,6 @@ async function dev() {
208
215
  res.end(html);
209
216
  }
210
217
  else {
211
- // ✅ Serve compiled output files
212
218
  const filePath = path_1.default.join(outDir, req.url || '');
213
219
  if (await fs_extra_1.default.pathExists(filePath)) {
214
220
  const content = await fs_extra_1.default.readFile(filePath);
@@ -219,6 +225,7 @@ async function dev() {
219
225
  next();
220
226
  }
221
227
  });
228
+ // 🔁 HMR WebSocket server
222
229
  const server = http_1.default.createServer(app);
223
230
  const wss = new ws_1.WebSocketServer({ server });
224
231
  const broadcast = (data) => {
@@ -240,11 +247,12 @@ async function dev() {
240
247
  }
241
248
  }
242
249
  });
250
+ // 🟢 Start server
243
251
  server.listen(port, async () => {
244
252
  const url = `http://localhost:${port}`;
245
- console.log(chalk_1.default.green(`\n Dev Server running at ${url}`));
246
- if (port !== defaultPort)
247
- console.log(chalk_1.default.yellow(`⚠️ Using alternate port (default ${defaultPort} was occupied).`));
253
+ console.log(chalk_1.default.cyan.bold(`\n🚀 React Client Dev Server`));
254
+ console.log(chalk_1.default.gray('───────────────────────────────'));
255
+ console.log(chalk_1.default.green(`⚡ Running at: ${url}`));
248
256
  await (0, open_1.default)(url, { newInstance: true });
249
257
  });
250
258
  process.on('SIGINT', async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-client",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
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",
@@ -94,7 +94,6 @@
94
94
  ]
95
95
  },
96
96
  "dependencies": {
97
- "@jridgewell/trace-mapping": "^0.3.31",
98
97
  "chalk": "^4.1.2",
99
98
  "chokidar": "^4.0.3",
100
99
  "commander": "^14.0.2",