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.
package/dist/cli/commands/dev.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
90
|
-
async function
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
|
|
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(
|
|
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: [
|
|
164
|
+
entryPath = require.resolve(id, { paths: [appRoot] });
|
|
139
165
|
}
|
|
140
166
|
catch {
|
|
141
|
-
|
|
142
|
-
entryPath = require.resolve(
|
|
143
|
-
|
|
144
|
-
|
|
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(`
|
|
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
|
-
//
|
|
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
|
-
|
|
167
|
-
|
|
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
|
|
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
|
|
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
|
|
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);
|