react-client 1.0.7 → 1.0.9
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 +141 -185
- package/dist/cli/commands/build.js +30 -47
- package/dist/cli/commands/build.ssr.js +31 -54
- package/dist/cli/commands/dev.js +221 -6
- package/dist/cli/commands/init.js +79 -48
- package/dist/cli/commands/preview.js +44 -16
- package/dist/cli/index.js +104 -18
- package/dist/utils/loadConfig.js +109 -0
- package/package.json +77 -34
- package/templates/react/index.html +1 -1
- package/templates/react-ssr/index.html +1 -1
- package/templates/react-ssr-ts/index.html +1 -1
- package/templates/react-tailwind/index.html +1 -1
- package/templates/react-tailwind-ts/index.html +1 -1
- package/templates/react-ts/index.html +1 -1
package/dist/cli/commands/dev.js
CHANGED
|
@@ -5,25 +5,240 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.default = dev;
|
|
7
7
|
const esbuild_1 = __importDefault(require("esbuild"));
|
|
8
|
+
const connect_1 = __importDefault(require("connect"));
|
|
9
|
+
const http_1 = __importDefault(require("http"));
|
|
10
|
+
const ws_1 = require("ws");
|
|
11
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
12
|
+
const detect_port_1 = __importDefault(require("detect-port"));
|
|
13
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
8
14
|
const path_1 = __importDefault(require("path"));
|
|
9
15
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
16
|
+
const trace_mapping_1 = require("@jridgewell/trace-mapping");
|
|
17
|
+
const loadConfig_1 = require("../../utils/loadConfig");
|
|
18
|
+
const open_1 = __importDefault(require("open"));
|
|
19
|
+
const child_process_1 = require("child_process");
|
|
20
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
21
|
async function dev() {
|
|
11
22
|
const root = process.cwd();
|
|
12
|
-
|
|
23
|
+
// 🧩 Load config
|
|
24
|
+
const userConfig = await (0, loadConfig_1.loadReactClientConfig)(root);
|
|
25
|
+
const appRoot = path_1.default.resolve(root, userConfig.root || '.');
|
|
26
|
+
const defaultPort = userConfig.server?.port || 5173;
|
|
27
|
+
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');
|
|
13
30
|
if (!fs_extra_1.default.existsSync(entry)) {
|
|
14
|
-
console.error('Entry not found: src/main.tsx');
|
|
31
|
+
console.error(chalk_1.default.red('❌ Entry not found: src/main.tsx'));
|
|
15
32
|
process.exit(1);
|
|
16
33
|
}
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
await fs_extra_1.default.ensureDir(outDir);
|
|
35
|
+
// 🧠 Detect open port
|
|
36
|
+
const availablePort = await (0, detect_port_1.default)(defaultPort);
|
|
37
|
+
const port = availablePort;
|
|
38
|
+
if (availablePort !== defaultPort) {
|
|
39
|
+
const res = await (0, prompts_1.default)({
|
|
40
|
+
type: 'confirm',
|
|
41
|
+
name: 'useNewPort',
|
|
42
|
+
message: `Port ${defaultPort} is occupied. Use ${availablePort} instead?`,
|
|
43
|
+
initial: true,
|
|
44
|
+
});
|
|
45
|
+
if (!res.useNewPort) {
|
|
46
|
+
console.log('🛑 Dev server cancelled.');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ⚡ Auto-install + resolve react-refresh runtime
|
|
51
|
+
function safeResolveReactRefresh() {
|
|
52
|
+
try {
|
|
53
|
+
return require.resolve('react-refresh/runtime');
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
console.warn(chalk_1.default.yellow('⚠️ react-refresh not found — attempting to install...'));
|
|
57
|
+
try {
|
|
58
|
+
(0, child_process_1.execSync)('npm install react-refresh --no-audit --no-fund --silent', {
|
|
59
|
+
cwd: root,
|
|
60
|
+
stdio: 'inherit',
|
|
61
|
+
});
|
|
62
|
+
console.log(chalk_1.default.green('✅ react-refresh installed successfully.'));
|
|
63
|
+
return require.resolve('react-refresh/runtime');
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
67
|
+
console.error(msg);
|
|
68
|
+
console.error(chalk_1.default.red('❌ Failed to install react-refresh automatically.'));
|
|
69
|
+
console.error('Please run: npm install react-refresh');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const reactRefreshRuntime = safeResolveReactRefresh();
|
|
75
|
+
// 🏗️ esbuild context
|
|
19
76
|
const ctx = await esbuild_1.default.context({
|
|
20
77
|
entryPoints: [entry],
|
|
21
78
|
bundle: true,
|
|
22
79
|
sourcemap: true,
|
|
23
|
-
outdir,
|
|
80
|
+
outdir: outDir,
|
|
24
81
|
define: { 'process.env.NODE_ENV': '"development"' },
|
|
25
82
|
loader: { '.ts': 'ts', '.tsx': 'tsx', '.js': 'jsx', '.jsx': 'jsx' },
|
|
26
83
|
});
|
|
27
84
|
await ctx.watch();
|
|
28
|
-
|
|
85
|
+
// 🌐 connect server
|
|
86
|
+
const app = (0, connect_1.default)();
|
|
87
|
+
// 🛡 Security headers
|
|
88
|
+
app.use((_req, res, next) => {
|
|
89
|
+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
|
90
|
+
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
|
|
91
|
+
next();
|
|
92
|
+
});
|
|
93
|
+
// 1️⃣ Serve react-refresh runtime with browser-safe shim
|
|
94
|
+
app.use('/@react-refresh', async (_req, res) => {
|
|
95
|
+
const runtime = await fs_extra_1.default.readFile(reactRefreshRuntime, 'utf8');
|
|
96
|
+
const shim = `
|
|
97
|
+
// React Refresh browser shims
|
|
98
|
+
window.process = window.process || { env: { NODE_ENV: 'development' } };
|
|
99
|
+
window.module = { exports: {} };
|
|
100
|
+
window.global = window; // ensure global scope for refresh
|
|
101
|
+
`;
|
|
102
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
103
|
+
res.end(shim + '\n' + runtime);
|
|
104
|
+
});
|
|
105
|
+
// 2️⃣ Serve PrismJS (for code frame overlay)
|
|
106
|
+
app.use('/@prismjs', async (_req, res) => {
|
|
107
|
+
const prismPath = require.resolve('prismjs/prism.js');
|
|
108
|
+
const css = await fs_extra_1.default.readFile(require.resolve('prismjs/themes/prism-tomorrow.css'), 'utf8');
|
|
109
|
+
const js = await fs_extra_1.default.readFile(prismPath, 'utf8');
|
|
110
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
111
|
+
res.end(`
|
|
112
|
+
(function(){
|
|
113
|
+
const style = document.createElement('style');
|
|
114
|
+
style.textContent = \`${css}\`;
|
|
115
|
+
document.head.appendChild(style);
|
|
116
|
+
${js}
|
|
117
|
+
})();
|
|
118
|
+
`);
|
|
119
|
+
});
|
|
120
|
+
// 3️⃣ Source map resolver (for overlay stack trace)
|
|
121
|
+
app.use('/@source-map', async (req, res) => {
|
|
122
|
+
const url = new URL(req.url ?? '', `http://localhost:${port}`);
|
|
123
|
+
const file = url.searchParams.get('file');
|
|
124
|
+
const line = Number(url.searchParams.get('line'));
|
|
125
|
+
const column = Number(url.searchParams.get('column'));
|
|
126
|
+
if (!file) {
|
|
127
|
+
res.writeHead(400);
|
|
128
|
+
res.end('Missing ?file parameter');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const mapPath = path_1.default.join(outDir, file + '.map');
|
|
132
|
+
if (!fs_extra_1.default.existsSync(mapPath)) {
|
|
133
|
+
res.writeHead(404);
|
|
134
|
+
res.end('Map not found');
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const mapJson = JSON.parse(await fs_extra_1.default.readFile(mapPath, 'utf8'));
|
|
139
|
+
const traceMap = new trace_mapping_1.TraceMap(mapJson);
|
|
140
|
+
const pos = (0, trace_mapping_1.originalPositionFor)(traceMap, { line, column });
|
|
141
|
+
if (!pos.source) {
|
|
142
|
+
res.writeHead(404);
|
|
143
|
+
res.end('Source not found');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const absSource = path_1.default.resolve(outDir, '../', pos.source);
|
|
147
|
+
let snippet = '';
|
|
148
|
+
if (await fs_extra_1.default.pathExists(absSource)) {
|
|
149
|
+
const lines = (await fs_extra_1.default.readFile(absSource, 'utf8')).split('\n');
|
|
150
|
+
const start = Math.max((pos.line || 1) - 3, 0);
|
|
151
|
+
const end = Math.min(lines.length, (pos.line || 1) + 2);
|
|
152
|
+
snippet = lines
|
|
153
|
+
.slice(start, end)
|
|
154
|
+
.map((l, i) => `<span class="line-number">${start + i + 1}</span> ${l
|
|
155
|
+
.replace(/</g, '<')
|
|
156
|
+
.replace(/>/g, '>')}`)
|
|
157
|
+
.join('\\n');
|
|
158
|
+
}
|
|
159
|
+
res.setHeader('Content-Type', 'application/json');
|
|
160
|
+
res.end(JSON.stringify({ ...pos, snippet }));
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
164
|
+
res.writeHead(500);
|
|
165
|
+
res.end(JSON.stringify({ error: msg }));
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
// 4️⃣ Serve HTML and inject overlay + HMR
|
|
169
|
+
app.use(async (req, res, next) => {
|
|
170
|
+
if (req.url === '/' || req.url === '/index.html') {
|
|
171
|
+
if (!fs_extra_1.default.existsSync(indexHtml)) {
|
|
172
|
+
res.writeHead(404);
|
|
173
|
+
res.end('index.html not found');
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let html = await fs_extra_1.default.readFile(indexHtml, 'utf8');
|
|
177
|
+
// Ensure main entry reference
|
|
178
|
+
html = html.replace(/<script[^>]*src="\/bundle\.js"[^>]*><\/script>/, '');
|
|
179
|
+
html = html.replace('</body>', `
|
|
180
|
+
<script type="module" src="/main.js"></script>
|
|
181
|
+
<script type="module">
|
|
182
|
+
import "/@react-refresh";
|
|
183
|
+
import "/@prismjs";
|
|
184
|
+
const ws = new WebSocket("ws://" + location.host);
|
|
185
|
+
ws.onmessage = async (e) => {
|
|
186
|
+
const msg = JSON.parse(e.data);
|
|
187
|
+
if (msg.type === "error") {
|
|
188
|
+
console.error(msg);
|
|
189
|
+
return window.showErrorOverlay?.(msg);
|
|
190
|
+
}
|
|
191
|
+
if (msg.type === "update") {
|
|
192
|
+
try {
|
|
193
|
+
await import(msg.path + "?t=" + Date.now());
|
|
194
|
+
window.clearErrorOverlay?.();
|
|
195
|
+
window.$RefreshRuntime?.performReactRefresh?.();
|
|
196
|
+
} catch (err) {
|
|
197
|
+
window.showErrorOverlay?.(err);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (msg.type === "reload") location.reload();
|
|
201
|
+
};
|
|
202
|
+
</script>
|
|
203
|
+
</body>`);
|
|
204
|
+
res.setHeader('Content-Type', 'text/html');
|
|
205
|
+
res.end(html);
|
|
206
|
+
}
|
|
207
|
+
else
|
|
208
|
+
next();
|
|
209
|
+
});
|
|
210
|
+
const server = http_1.default.createServer(app);
|
|
211
|
+
const wss = new ws_1.WebSocketServer({ server });
|
|
212
|
+
const broadcast = (data) => {
|
|
213
|
+
const json = JSON.stringify(data);
|
|
214
|
+
wss.clients.forEach((c) => c.readyState === 1 && c.send(json));
|
|
215
|
+
};
|
|
216
|
+
chokidar_1.default.watch(path_1.default.join(appRoot, 'src'), { ignoreInitial: true }).on('change', async (file) => {
|
|
217
|
+
try {
|
|
218
|
+
console.log(`🔄 Rebuilding: ${file}`);
|
|
219
|
+
await ctx.rebuild();
|
|
220
|
+
broadcast({ type: 'update', path: '/' + path_1.default.relative(appRoot, file).replace(/\\/g, '/') });
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
if (err instanceof Error) {
|
|
224
|
+
broadcast({ type: 'error', message: err.message, stack: err.stack });
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
broadcast({ type: 'error', message: String(err) });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
server.listen(port, async () => {
|
|
232
|
+
const url = `http://localhost:${port}`;
|
|
233
|
+
console.log(chalk_1.default.green(`\n⚡ Dev Server running at ${url}`));
|
|
234
|
+
if (port !== defaultPort)
|
|
235
|
+
console.log(chalk_1.default.yellow(`⚠️ Using alternate port (default ${defaultPort} was occupied).`));
|
|
236
|
+
await (0, open_1.default)(url, { newInstance: true });
|
|
237
|
+
});
|
|
238
|
+
process.on('SIGINT', async () => {
|
|
239
|
+
console.log(chalk_1.default.red('\n🛑 Shutting down...'));
|
|
240
|
+
await ctx.dispose();
|
|
241
|
+
server.close();
|
|
242
|
+
process.exit(0);
|
|
243
|
+
});
|
|
29
244
|
}
|
|
@@ -3,61 +3,92 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.default =
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
6
|
+
exports.default = initCmd;
|
|
8
7
|
const path_1 = __importDefault(require("path"));
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
async function initCmd(name, opts) {
|
|
13
|
+
const root = process.cwd();
|
|
14
|
+
const projectDir = path_1.default.resolve(root, name);
|
|
11
15
|
const template = opts.template || 'react-ts';
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
console.log(chalk_1.default.cyan(`\n📦 Creating new React Client app: ${chalk_1.default.bold(name)}`));
|
|
17
|
+
// 1️⃣ Check if directory exists
|
|
18
|
+
if (fs_extra_1.default.existsSync(projectDir)) {
|
|
19
|
+
const res = await (0, prompts_1.default)({
|
|
20
|
+
type: 'confirm',
|
|
21
|
+
name: 'overwrite',
|
|
22
|
+
message: chalk_1.default.yellow(`Directory "${name}" already exists. Overwrite?`),
|
|
23
|
+
initial: false,
|
|
24
|
+
});
|
|
25
|
+
if (!res.overwrite) {
|
|
26
|
+
console.log(chalk_1.default.red('❌ Operation cancelled.'));
|
|
27
|
+
process.exit(1);
|
|
23
28
|
}
|
|
24
|
-
|
|
25
|
-
if (parent === cur)
|
|
26
|
-
break; // reached filesystem root
|
|
27
|
-
cur = parent;
|
|
29
|
+
await fs_extra_1.default.remove(projectDir);
|
|
28
30
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
await fs_extra_1.default.ensureDir(projectDir);
|
|
32
|
+
// 2️⃣ Locate template
|
|
33
|
+
const templateDir = path_1.default.resolve(__dirname, '../../../templates', template);
|
|
34
|
+
if (!fs_extra_1.default.existsSync(templateDir)) {
|
|
35
|
+
console.error(chalk_1.default.red(`❌ Template not found: ${template}`));
|
|
31
36
|
process.exit(1);
|
|
32
37
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
// 3️⃣ Copy template
|
|
39
|
+
console.log(chalk_1.default.gray(`\n📁 Copying template: ${template}...`));
|
|
40
|
+
await fs_extra_1.default.copy(templateDir, projectDir);
|
|
41
|
+
// 4️⃣ Optionally create react-client.config.js (not .ts)
|
|
42
|
+
if (opts.withConfig) {
|
|
43
|
+
const configPath = path_1.default.join(projectDir, 'react-client.config.js');
|
|
44
|
+
if (!fs_extra_1.default.existsSync(configPath)) {
|
|
45
|
+
const configContent = `// react-client.config.js
|
|
46
|
+
import { defineConfig } from 'react-client/config';
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
// 🧭 Root directory for the app
|
|
50
|
+
root: './src',
|
|
51
|
+
|
|
52
|
+
// ⚡ Dev server settings
|
|
53
|
+
server: {
|
|
54
|
+
port: 5173,
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// 🏗️ Build options
|
|
58
|
+
build: {
|
|
59
|
+
outDir: '.react-client/build',
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// 💡 Add plugins, aliases, etc.
|
|
63
|
+
});
|
|
64
|
+
`;
|
|
65
|
+
await fs_extra_1.default.writeFile(configPath, configContent, 'utf8');
|
|
66
|
+
console.log(chalk_1.default.green('📝 Created react-client.config.js'));
|
|
56
67
|
}
|
|
57
68
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
// 5️⃣ Initialize git repo
|
|
70
|
+
try {
|
|
71
|
+
(0, child_process_1.execSync)('git init', { cwd: projectDir, stdio: 'ignore' });
|
|
72
|
+
console.log(chalk_1.default.gray('🔧 Initialized Git repository.'));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
console.warn(chalk_1.default.yellow('⚠️ Git init failed (skipping).'));
|
|
76
|
+
}
|
|
77
|
+
// 6️⃣ Install dependencies
|
|
78
|
+
const pkgManager = /yarn/.test(process.env.npm_execpath || '') ? 'yarn' : 'npm';
|
|
79
|
+
console.log(chalk_1.default.gray(`\n📦 Installing dependencies using ${pkgManager}...`));
|
|
80
|
+
try {
|
|
81
|
+
(0, child_process_1.execSync)(`${pkgManager} install`, { cwd: projectDir, stdio: 'inherit' });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
console.warn(chalk_1.default.yellow('⚠️ Dependency installation failed, please run manually.'));
|
|
61
85
|
}
|
|
62
|
-
|
|
86
|
+
// 7️⃣ Completion message
|
|
87
|
+
console.log();
|
|
88
|
+
console.log(chalk_1.default.green('✅ Project setup complete!'));
|
|
89
|
+
console.log(chalk_1.default.cyan(`\nNext steps:`));
|
|
90
|
+
console.log(chalk_1.default.gray(` cd ${name}`));
|
|
91
|
+
console.log(chalk_1.default.gray(` ${pkgManager === 'yarn' ? 'yarn dev' : 'npm run dev'}`));
|
|
92
|
+
console.log();
|
|
93
|
+
console.log(chalk_1.default.dim('Happy coding! ⚡'));
|
|
63
94
|
}
|
|
@@ -4,27 +4,55 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.default = preview;
|
|
7
|
+
const connect_1 = __importDefault(require("connect"));
|
|
8
|
+
const serve_static_1 = __importDefault(require("serve-static"));
|
|
7
9
|
const http_1 = __importDefault(require("http"));
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const detect_port_1 = __importDefault(require("detect-port"));
|
|
12
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
13
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
14
|
+
const open_1 = __importDefault(require("open"));
|
|
15
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
16
|
+
const loadConfig_1 = require("../../utils/loadConfig");
|
|
10
17
|
async function preview() {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
const root = process.cwd();
|
|
19
|
+
const config = await (0, loadConfig_1.loadReactClientConfig)(root);
|
|
20
|
+
const appRoot = path_1.default.resolve(root, config.root || '.');
|
|
21
|
+
const outDir = path_1.default.join(appRoot, config.build?.outDir || '.react-client/build');
|
|
22
|
+
const defaultPort = config.server?.port || 5173;
|
|
23
|
+
if (!fs_extra_1.default.existsSync(outDir)) {
|
|
24
|
+
console.error(chalk_1.default.red(`❌ Build output not found at: ${outDir}`));
|
|
25
|
+
console.log(chalk_1.default.gray('Please run `react-client build` first.'));
|
|
14
26
|
process.exit(1);
|
|
15
27
|
}
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
const availablePort = await (0, detect_port_1.default)(defaultPort);
|
|
29
|
+
const port = availablePort;
|
|
30
|
+
if (availablePort !== defaultPort) {
|
|
31
|
+
const res = await (0, prompts_1.default)({
|
|
32
|
+
type: 'confirm',
|
|
33
|
+
name: 'useNewPort',
|
|
34
|
+
message: `Port ${defaultPort} is occupied. Use ${availablePort} instead?`,
|
|
35
|
+
initial: true,
|
|
36
|
+
});
|
|
37
|
+
if (!res.useNewPort) {
|
|
38
|
+
console.log(chalk_1.default.red('🛑 Preview server cancelled.'));
|
|
39
|
+
process.exit(0);
|
|
25
40
|
}
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
}
|
|
42
|
+
const app = (0, connect_1.default)();
|
|
43
|
+
app.use((0, serve_static_1.default)(outDir));
|
|
44
|
+
const server = http_1.default.createServer(app);
|
|
45
|
+
server.listen(port, async () => {
|
|
46
|
+
const url = `http://localhost:${port}`;
|
|
47
|
+
console.log(chalk_1.default.green(`\n🌐 Preview server running at ${url}`));
|
|
48
|
+
if (port !== defaultPort) {
|
|
49
|
+
console.log(chalk_1.default.yellow(`⚠️ Using alternate port (default ${defaultPort} was occupied).`));
|
|
50
|
+
}
|
|
51
|
+
await (0, open_1.default)(url, { newInstance: true });
|
|
52
|
+
});
|
|
53
|
+
process.on('SIGINT', () => {
|
|
54
|
+
console.log(chalk_1.default.red('\n🛑 Shutting down preview server...'));
|
|
55
|
+
server.close();
|
|
56
|
+
process.exit(0);
|
|
28
57
|
});
|
|
29
|
-
server.listen(5000, () => console.log('Preview running at http://localhost:5000'));
|
|
30
58
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,43 +5,129 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
11
|
const init_1 = __importDefault(require("./commands/init"));
|
|
9
12
|
const generate_1 = __importDefault(require("./commands/generate"));
|
|
10
13
|
const dev_1 = __importDefault(require("./commands/dev"));
|
|
11
14
|
const build_1 = __importDefault(require("./commands/build"));
|
|
12
15
|
const build_ssr_1 = __importDefault(require("./commands/build.ssr"));
|
|
13
16
|
const preview_1 = __importDefault(require("./commands/preview"));
|
|
17
|
+
// Load package.json version dynamically
|
|
18
|
+
const pkgPath = path_1.default.resolve(__dirname, '../../package.json');
|
|
19
|
+
const pkg = fs_extra_1.default.existsSync(pkgPath)
|
|
20
|
+
? JSON.parse(fs_extra_1.default.readFileSync(pkgPath, 'utf8'))
|
|
21
|
+
: { version: '0.0.0' };
|
|
22
|
+
// 🧠 Fancy startup banner
|
|
23
|
+
function showBanner(cmd) {
|
|
24
|
+
const title = chalk_1.default.bold.cyan('⚡ React Client');
|
|
25
|
+
const version = chalk_1.default.gray(`v${pkg.version}`);
|
|
26
|
+
const tagline = chalk_1.default.dim('Fast esbuild-based React CLI with HMR, SSR & Overlay');
|
|
27
|
+
const line = chalk_1.default.gray('──────────────────────────────────────────────────────────────');
|
|
28
|
+
console.log(`\n${title} ${version}`);
|
|
29
|
+
console.log(tagline);
|
|
30
|
+
console.log(line);
|
|
31
|
+
switch (cmd) {
|
|
32
|
+
case 'init':
|
|
33
|
+
console.log(chalk_1.default.cyan('📦 Initializing new React project...\n'));
|
|
34
|
+
break;
|
|
35
|
+
case 'generate':
|
|
36
|
+
console.log(chalk_1.default.white('✨ Generating boilerplate files...\n'));
|
|
37
|
+
break;
|
|
38
|
+
case 'dev':
|
|
39
|
+
console.log(chalk_1.default.green('🚀 Starting development server...\n'));
|
|
40
|
+
break;
|
|
41
|
+
case 'build':
|
|
42
|
+
console.log(chalk_1.default.yellow('🏗️ Building for production...\n'));
|
|
43
|
+
break;
|
|
44
|
+
case 'build:ssr':
|
|
45
|
+
console.log(chalk_1.default.magenta('🧱 Building for server-side rendering (SSR)...\n'));
|
|
46
|
+
break;
|
|
47
|
+
case 'preview':
|
|
48
|
+
console.log(chalk_1.default.blue('🌐 Starting production preview server...\n'));
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
console.log();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 🧩 Commander setup
|
|
14
55
|
const program = new commander_1.Command();
|
|
15
|
-
program
|
|
56
|
+
program
|
|
57
|
+
.name('react-client')
|
|
58
|
+
.description('react-client CLI – A lightweight React toolkit for fast builds & dev server')
|
|
59
|
+
.version(pkg.version, '-v, --version', 'display version information');
|
|
60
|
+
// ------------------------------------------------------
|
|
61
|
+
// CLI Commands
|
|
62
|
+
// ------------------------------------------------------
|
|
16
63
|
program
|
|
17
64
|
.command('init <name>')
|
|
18
|
-
.option('-t
|
|
19
|
-
.option('--with-config', 'create config')
|
|
20
|
-
.
|
|
65
|
+
.option('-t, --template <template>', 'choose a template', 'react-ts')
|
|
66
|
+
.option('--with-config', 'create a config file')
|
|
67
|
+
.description('initialize a new React project')
|
|
68
|
+
.action((name, opts) => {
|
|
69
|
+
showBanner('init');
|
|
70
|
+
(0, init_1.default)(name, opts);
|
|
71
|
+
});
|
|
21
72
|
program
|
|
22
73
|
.command('generate <kind> <name>')
|
|
23
|
-
.
|
|
24
|
-
.option('--
|
|
25
|
-
.option('-
|
|
26
|
-
.
|
|
74
|
+
.alias('g')
|
|
75
|
+
.option('-p, --path <path>', 'output path')
|
|
76
|
+
.option('--no-ts', 'generate JS instead of TS')
|
|
77
|
+
.option('-f, --force', 'overwrite if exists')
|
|
78
|
+
.description('generate components, pages, or stores')
|
|
79
|
+
.action((kind, name, opts) => {
|
|
80
|
+
showBanner('generate');
|
|
81
|
+
(0, generate_1.default)(kind, name, opts);
|
|
82
|
+
});
|
|
27
83
|
program
|
|
28
84
|
.command('dev')
|
|
29
|
-
.description('start dev server')
|
|
30
|
-
.action(() =>
|
|
85
|
+
.description('start dev server (with React Fast Refresh)')
|
|
86
|
+
.action(() => {
|
|
87
|
+
showBanner('dev');
|
|
88
|
+
(0, dev_1.default)();
|
|
89
|
+
});
|
|
31
90
|
program
|
|
32
91
|
.command('build')
|
|
33
|
-
.description('build
|
|
34
|
-
.action(() =>
|
|
92
|
+
.description('build production assets')
|
|
93
|
+
.action(() => {
|
|
94
|
+
showBanner('build');
|
|
95
|
+
(0, build_1.default)();
|
|
96
|
+
});
|
|
35
97
|
program
|
|
36
98
|
.command('build:ssr')
|
|
37
|
-
.description('build
|
|
38
|
-
.action(() =>
|
|
99
|
+
.description('build for server-side rendering (SSR)')
|
|
100
|
+
.action(() => {
|
|
101
|
+
showBanner('build:ssr');
|
|
102
|
+
(0, build_ssr_1.default)();
|
|
103
|
+
});
|
|
39
104
|
program
|
|
40
105
|
.command('preview')
|
|
41
|
-
.description('preview build')
|
|
42
|
-
.action(() =>
|
|
43
|
-
|
|
106
|
+
.description('preview production build')
|
|
107
|
+
.action(() => {
|
|
108
|
+
showBanner('preview');
|
|
109
|
+
(0, preview_1.default)();
|
|
110
|
+
});
|
|
111
|
+
// ------------------------------------------------------
|
|
112
|
+
// Default / Unknown command handling
|
|
113
|
+
// ------------------------------------------------------
|
|
114
|
+
program.on('command:*', () => {
|
|
115
|
+
console.error(chalk_1.default.red('❌ Invalid command:'), program.args.join(' '));
|
|
116
|
+
console.log();
|
|
117
|
+
program.outputHelp();
|
|
118
|
+
process.exit(1);
|
|
119
|
+
});
|
|
120
|
+
// ------------------------------------------------------
|
|
121
|
+
// Entry point
|
|
122
|
+
// ------------------------------------------------------
|
|
44
123
|
if (require.main === module) {
|
|
45
|
-
|
|
124
|
+
if (process.argv.length <= 2) {
|
|
125
|
+
console.clear();
|
|
126
|
+
showBanner();
|
|
127
|
+
program.outputHelp();
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
program.parse(process.argv);
|
|
131
|
+
}
|
|
46
132
|
}
|
|
47
133
|
exports.default = program;
|