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.
- package/dist/cli/commands/dev.js +77 -69
- package/package.json +1 -2
package/dist/cli/commands/dev.js
CHANGED
|
@@ -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
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
//
|
|
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]',
|
|
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
|
-
// 🌐
|
|
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️⃣
|
|
112
|
-
app.use('/@
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
res.
|
|
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(
|
|
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.
|
|
246
|
-
|
|
247
|
-
|
|
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.
|
|
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",
|