react-client 1.0.30 ā 1.0.31
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/build.js +32 -44
- package/dist/cli/commands/dev.js +431 -456
- package/dist/cli/commands/init.js +60 -71
- package/dist/cli/commands/preview.js +110 -122
- package/dist/cli/index.js +33 -34
- package/dist/server/broadcastManager.js +1 -1
- package/dist/utils/loadConfig.js +55 -66
- package/package.json +2 -2
|
@@ -1,52 +1,42 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import path from 'path';
|
|
11
2
|
import fs from 'fs-extra';
|
|
12
3
|
import prompts from 'prompts';
|
|
13
4
|
import chalk from 'chalk';
|
|
14
5
|
import { execSync } from 'child_process';
|
|
15
|
-
export default function initCmd(name, opts) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
console.log(chalk.red('ā Operation cancelled.'));
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
yield fs.remove(projectDir);
|
|
34
|
-
}
|
|
35
|
-
yield fs.ensureDir(projectDir);
|
|
36
|
-
// 2ļøā£ Locate template
|
|
37
|
-
const templateDir = path.resolve(__dirname, '../../../templates', template);
|
|
38
|
-
if (!fs.existsSync(templateDir)) {
|
|
39
|
-
console.error(chalk.red(`ā Template not found: ${template}`));
|
|
6
|
+
export default async function initCmd(name, opts) {
|
|
7
|
+
const root = process.cwd();
|
|
8
|
+
const projectDir = path.resolve(root, name);
|
|
9
|
+
const template = opts.template || 'react-ts';
|
|
10
|
+
console.log(chalk.cyan(`\nš¦ Creating new React Client app: ${chalk.bold(name)}`));
|
|
11
|
+
// 1ļøā£ Check if directory exists
|
|
12
|
+
if (fs.existsSync(projectDir)) {
|
|
13
|
+
const res = await prompts({
|
|
14
|
+
type: 'confirm',
|
|
15
|
+
name: 'overwrite',
|
|
16
|
+
message: chalk.yellow(`Directory "${name}" already exists. Overwrite?`),
|
|
17
|
+
initial: false,
|
|
18
|
+
});
|
|
19
|
+
if (!res.overwrite) {
|
|
20
|
+
console.log(chalk.red('ā Operation cancelled.'));
|
|
40
21
|
process.exit(1);
|
|
41
22
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
await fs.remove(projectDir);
|
|
24
|
+
}
|
|
25
|
+
await fs.ensureDir(projectDir);
|
|
26
|
+
// 2ļøā£ Locate template
|
|
27
|
+
const templateDir = path.resolve(__dirname, '../../../templates', template);
|
|
28
|
+
if (!fs.existsSync(templateDir)) {
|
|
29
|
+
console.error(chalk.red(`ā Template not found: ${template}`));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
// 3ļøā£ Copy template
|
|
33
|
+
console.log(chalk.gray(`\nš Copying template: ${template}...`));
|
|
34
|
+
await fs.copy(templateDir, projectDir);
|
|
35
|
+
// 4ļøā£ Optionally create react-client.config.js (not .ts)
|
|
36
|
+
if (opts.withConfig) {
|
|
37
|
+
const configPath = path.join(projectDir, 'react-client.config.js');
|
|
38
|
+
if (!fs.existsSync(configPath)) {
|
|
39
|
+
const configContent = `// react-client.config.js
|
|
50
40
|
import { defineConfig } from 'react-client/config';
|
|
51
41
|
|
|
52
42
|
export default defineConfig({
|
|
@@ -66,34 +56,33 @@ export default defineConfig({
|
|
|
66
56
|
// š” Add plugins, aliases, etc.
|
|
67
57
|
});
|
|
68
58
|
`;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// 5ļøā£ Initialize git repo
|
|
74
|
-
try {
|
|
75
|
-
execSync('git init', { cwd: projectDir, stdio: 'ignore' });
|
|
76
|
-
console.log(chalk.gray('š§ Initialized Git repository.'));
|
|
77
|
-
}
|
|
78
|
-
catch (_a) {
|
|
79
|
-
console.warn(chalk.yellow('ā ļø Git init failed (skipping).'));
|
|
80
|
-
}
|
|
81
|
-
// 6ļøā£ Install dependencies
|
|
82
|
-
const pkgManager = /yarn/.test(process.env.npm_execpath || '') ? 'yarn' : 'npm';
|
|
83
|
-
console.log(chalk.gray(`\nš¦ Installing dependencies using ${pkgManager}...`));
|
|
84
|
-
try {
|
|
85
|
-
execSync(`${pkgManager} install`, { cwd: projectDir, stdio: 'inherit' });
|
|
86
|
-
}
|
|
87
|
-
catch (_b) {
|
|
88
|
-
console.warn(chalk.yellow('ā ļø Dependency installation failed, please run manually.'));
|
|
59
|
+
await fs.writeFile(configPath, configContent, 'utf8');
|
|
60
|
+
console.log(chalk.green('š Created react-client.config.js'));
|
|
89
61
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(chalk.gray(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
console.
|
|
98
|
-
}
|
|
62
|
+
}
|
|
63
|
+
// 5ļøā£ Initialize git repo
|
|
64
|
+
try {
|
|
65
|
+
execSync('git init', { cwd: projectDir, stdio: 'ignore' });
|
|
66
|
+
console.log(chalk.gray('š§ Initialized Git repository.'));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
console.warn(chalk.yellow('ā ļø Git init failed (skipping).'));
|
|
70
|
+
}
|
|
71
|
+
// 6ļøā£ Install dependencies
|
|
72
|
+
const pkgManager = /yarn/.test(process.env.npm_execpath || '') ? 'yarn' : 'npm';
|
|
73
|
+
console.log(chalk.gray(`\nš¦ Installing dependencies using ${pkgManager}...`));
|
|
74
|
+
try {
|
|
75
|
+
execSync(`${pkgManager} install`, { cwd: projectDir, stdio: 'inherit' });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
console.warn(chalk.yellow('ā ļø Dependency installation failed, please run manually.'));
|
|
79
|
+
}
|
|
80
|
+
// 7ļøā£ Completion message
|
|
81
|
+
console.log();
|
|
82
|
+
console.log(chalk.green('ā
Project setup complete!'));
|
|
83
|
+
console.log(chalk.cyan(`\nNext steps:`));
|
|
84
|
+
console.log(chalk.gray(` cd ${name}`));
|
|
85
|
+
console.log(chalk.gray(` ${pkgManager === 'yarn' ? 'yarn dev' : 'npm run dev'}`));
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(chalk.dim('Happy coding! ā”'));
|
|
99
88
|
}
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import http from 'http';
|
|
11
2
|
import path from 'path';
|
|
12
3
|
import fs from 'fs-extra';
|
|
@@ -43,129 +34,126 @@ function setCachingHeaders(res, stat) {
|
|
|
43
34
|
res.setHeader('Last-Modified', stat.mtime.toUTCString());
|
|
44
35
|
res.setHeader('Cache-Control', 'public, max-age=0, must-revalidate');
|
|
45
36
|
}
|
|
46
|
-
export default function preview() {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
37
|
+
export default async function preview() {
|
|
38
|
+
const cwd = process.cwd();
|
|
39
|
+
const config = await loadReactClientConfig(cwd);
|
|
40
|
+
const appRoot = path.resolve(cwd, config.root || '.');
|
|
41
|
+
const outDir = path.join(appRoot, config.build?.outDir || 'dist');
|
|
42
|
+
const indexHtml = path.join(outDir, 'index.html');
|
|
43
|
+
if (!(await fs.pathExists(outDir))) {
|
|
44
|
+
console.error(chalk.red(`ā Preview directory not found: ${outDir}`));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (!(await fs.pathExists(indexHtml))) {
|
|
48
|
+
console.warn(chalk.yellow(`ā ļø index.html not found in ${outDir}. SPA fallback will be disabled.`));
|
|
49
|
+
}
|
|
50
|
+
const defaultPort = config.server?.port || 4173;
|
|
51
|
+
const port = await detectPort(defaultPort);
|
|
52
|
+
if (port !== defaultPort) {
|
|
53
|
+
const r = await prompts({
|
|
54
|
+
type: 'confirm',
|
|
55
|
+
name: 'useNewPort',
|
|
56
|
+
initial: true,
|
|
57
|
+
message: `Port ${defaultPort} is occupied. Use ${port} instead?`,
|
|
58
|
+
});
|
|
59
|
+
if (!r.useNewPort) {
|
|
60
|
+
console.log('š Preview cancelled.');
|
|
61
|
+
process.exit(0);
|
|
60
62
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (!r.useNewPort) {
|
|
71
|
-
console.log('š Preview cancelled.');
|
|
72
|
-
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
const server = http.createServer(async (req, res) => {
|
|
65
|
+
try {
|
|
66
|
+
const url = req.url || '/';
|
|
67
|
+
// normalize and protect
|
|
68
|
+
const relPath = decodeURIComponent(url.split('?')[0]);
|
|
69
|
+
if (relPath.includes('..')) {
|
|
70
|
+
res.writeHead(400);
|
|
71
|
+
return res.end('Invalid request');
|
|
73
72
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return res.end('Invalid request');
|
|
73
|
+
// handle root -> index.html
|
|
74
|
+
let filePath = path.join(outDir, relPath);
|
|
75
|
+
const tryIndexFallback = async () => {
|
|
76
|
+
if (await fs.pathExists(indexHtml)) {
|
|
77
|
+
const stat = await fs.stat(indexHtml);
|
|
78
|
+
setCachingHeaders(res, stat);
|
|
79
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
80
|
+
return fs.createReadStream(indexHtml).pipe(res);
|
|
83
81
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (yield fs.pathExists(indexHtml)) {
|
|
88
|
-
const stat = yield fs.stat(indexHtml);
|
|
89
|
-
setCachingHeaders(res, stat);
|
|
90
|
-
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
91
|
-
return fs.createReadStream(indexHtml).pipe(res);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
res.writeHead(404);
|
|
95
|
-
return res.end('Not found');
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
// If the request path is a directory, try index.html inside it
|
|
99
|
-
if (relPath.endsWith('/')) {
|
|
100
|
-
const candidate = path.join(filePath, 'index.html');
|
|
101
|
-
if (yield fs.pathExists(candidate)) {
|
|
102
|
-
filePath = candidate;
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
return tryIndexFallback();
|
|
106
|
-
}
|
|
82
|
+
else {
|
|
83
|
+
res.writeHead(404);
|
|
84
|
+
return res.end('Not found');
|
|
107
85
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
return tryIndexFallback();
|
|
86
|
+
};
|
|
87
|
+
// If the request path is a directory, try index.html inside it
|
|
88
|
+
if (relPath.endsWith('/')) {
|
|
89
|
+
const candidate = path.join(filePath, 'index.html');
|
|
90
|
+
if (await fs.pathExists(candidate)) {
|
|
91
|
+
filePath = candidate;
|
|
116
92
|
}
|
|
117
|
-
|
|
118
|
-
if (!stat.isFile()) {
|
|
93
|
+
else {
|
|
119
94
|
return tryIndexFallback();
|
|
120
95
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
96
|
+
}
|
|
97
|
+
// If file doesn't exist, fallback to index.html for SPA routes
|
|
98
|
+
if (!(await fs.pathExists(filePath))) {
|
|
99
|
+
// If request appears to be a static asset (has extension), return 404
|
|
100
|
+
if (path.extname(filePath)) {
|
|
101
|
+
res.writeHead(404);
|
|
102
|
+
return res.end('Not found');
|
|
103
|
+
}
|
|
104
|
+
return tryIndexFallback();
|
|
105
|
+
}
|
|
106
|
+
const stat = await fs.stat(filePath);
|
|
107
|
+
if (!stat.isFile()) {
|
|
108
|
+
return tryIndexFallback();
|
|
109
|
+
}
|
|
110
|
+
// Compression/Precompressed support: prefer brotli -> gzip -> raw
|
|
111
|
+
const accept = (req.headers['accept-encoding'] || '');
|
|
112
|
+
const tryPrecompressed = async () => {
|
|
113
|
+
if (accept.includes('br') && (await fs.pathExists(filePath + '.br'))) {
|
|
114
|
+
res.setHeader('Content-Encoding', 'br');
|
|
115
|
+
res.setHeader('Content-Type', contentType(filePath));
|
|
116
|
+
setCachingHeaders(res, stat);
|
|
117
|
+
return fs.createReadStream(filePath + '.br').pipe(res);
|
|
118
|
+
}
|
|
119
|
+
if (accept.includes('gzip') && (await fs.pathExists(filePath + '.gz'))) {
|
|
120
|
+
res.setHeader('Content-Encoding', 'gzip');
|
|
137
121
|
res.setHeader('Content-Type', contentType(filePath));
|
|
138
122
|
setCachingHeaders(res, stat);
|
|
139
|
-
return fs.createReadStream(filePath).pipe(res);
|
|
140
|
-
});
|
|
141
|
-
// ETag / If-None-Match handling
|
|
142
|
-
const etag = `${stat.size}-${Date.parse(stat.mtime.toString())}`;
|
|
143
|
-
const inm = req.headers['if-none-match'];
|
|
144
|
-
if (inm && inm.toString() === etag) {
|
|
145
|
-
res.writeHead(304);
|
|
146
|
-
return res.end();
|
|
123
|
+
return fs.createReadStream(filePath + '.gz').pipe(res);
|
|
147
124
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
125
|
+
// default
|
|
126
|
+
res.setHeader('Content-Type', contentType(filePath));
|
|
127
|
+
setCachingHeaders(res, stat);
|
|
128
|
+
return fs.createReadStream(filePath).pipe(res);
|
|
129
|
+
};
|
|
130
|
+
// ETag / If-None-Match handling
|
|
131
|
+
const etag = `${stat.size}-${Date.parse(stat.mtime.toString())}`;
|
|
132
|
+
const inm = req.headers['if-none-match'];
|
|
133
|
+
if (inm && inm.toString() === etag) {
|
|
134
|
+
res.writeHead(304);
|
|
135
|
+
return res.end();
|
|
155
136
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
console.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
});
|
|
137
|
+
return tryPrecompressed();
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const e = err;
|
|
141
|
+
console.error('Preview server error:', e);
|
|
142
|
+
res.writeHead(500);
|
|
143
|
+
res.end('Internal Server Error');
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
server.listen(port, async () => {
|
|
147
|
+
const url = `http://localhost:${port}`;
|
|
148
|
+
console.log(chalk.cyan.bold('\nš react-client preview'));
|
|
149
|
+
console.log(chalk.gray('āāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
150
|
+
console.log(chalk.green(`Serving: ${outDir}`));
|
|
151
|
+
console.log(chalk.green(`Open: ${url}`));
|
|
152
|
+
await open(url, { newInstance: true });
|
|
153
|
+
});
|
|
154
|
+
process.on('SIGINT', () => {
|
|
155
|
+
console.log(chalk.red('\nš Shutting down preview...'));
|
|
156
|
+
server.close();
|
|
157
|
+
process.exit(0);
|
|
170
158
|
});
|
|
171
159
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* React Client CLI (Vite-like)
|
|
4
|
+
* ---------------------------------------
|
|
5
|
+
* Supports commands: init, dev, build, preview
|
|
6
|
+
* ESM-safe, works with NodeNext and global installs.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import path from 'path';
|
|
3
10
|
import fs from 'fs-extra';
|
|
4
11
|
import chalk from 'chalk';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
// Polyfill for __dirname in ESM
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import initCmd from './commands/init.js';
|
|
14
|
+
import devCmd from './commands/dev.js';
|
|
15
|
+
import buildCmd from './commands/build.js';
|
|
16
|
+
import previewCmd from './commands/preview.js';
|
|
17
|
+
// Resolve __dirname safely in ESM
|
|
12
18
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = dirname(__filename);
|
|
14
|
-
//
|
|
15
|
-
const pkgPath = resolve(__dirname, '../../package.json');
|
|
16
|
-
const isMainModule = process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url;
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
// Dynamically load version from package.json
|
|
21
|
+
const pkgPath = path.resolve(__dirname, '../../package.json');
|
|
17
22
|
const pkg = fs.existsSync(pkgPath)
|
|
18
23
|
? JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
19
24
|
: { version: '0.0.0' };
|
|
20
|
-
// š§
|
|
25
|
+
// š§ Banner Display
|
|
21
26
|
function showBanner(cmd) {
|
|
22
27
|
const title = chalk.bold.cyan('ā” React Client');
|
|
23
28
|
const version = chalk.gray(`v${pkg.version}`);
|
|
@@ -43,58 +48,52 @@ function showBanner(cmd) {
|
|
|
43
48
|
console.log();
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
|
-
//
|
|
51
|
+
// CLI Setup
|
|
47
52
|
const program = new Command();
|
|
48
53
|
program
|
|
49
54
|
.name('react-client')
|
|
50
|
-
.description('react-client CLI ā
|
|
55
|
+
.description('react-client CLI ā lightweight React toolkit for fast builds & dev server')
|
|
51
56
|
.version(pkg.version, '-v, --version', 'display version information');
|
|
52
|
-
//
|
|
53
|
-
// CLI Commands
|
|
54
|
-
// ------------------------------------------------------
|
|
57
|
+
// Commands
|
|
55
58
|
program
|
|
56
59
|
.command('init <name>')
|
|
57
60
|
.option('-t, --template <template>', 'choose a template', 'react-ts')
|
|
58
61
|
.option('--with-config', 'create a config file')
|
|
59
62
|
.description('initialize a new React project')
|
|
60
|
-
.action((name, opts) => {
|
|
63
|
+
.action(async (name, opts) => {
|
|
61
64
|
showBanner('init');
|
|
62
|
-
initCmd(name, opts);
|
|
65
|
+
await initCmd(name, opts);
|
|
63
66
|
});
|
|
64
67
|
program
|
|
65
68
|
.command('dev')
|
|
66
|
-
.description('start
|
|
67
|
-
.action(() => {
|
|
69
|
+
.description('start development server (with React Fast Refresh)')
|
|
70
|
+
.action(async () => {
|
|
68
71
|
showBanner('dev');
|
|
69
|
-
devCmd();
|
|
72
|
+
await devCmd();
|
|
70
73
|
});
|
|
71
74
|
program
|
|
72
75
|
.command('build')
|
|
73
76
|
.description('build production assets')
|
|
74
|
-
.action(() => {
|
|
77
|
+
.action(async () => {
|
|
75
78
|
showBanner('build');
|
|
76
|
-
buildCmd();
|
|
79
|
+
await buildCmd();
|
|
77
80
|
});
|
|
78
81
|
program
|
|
79
82
|
.command('preview')
|
|
80
83
|
.description('preview production build')
|
|
81
|
-
.action(() => {
|
|
84
|
+
.action(async () => {
|
|
82
85
|
showBanner('preview');
|
|
83
|
-
previewCmd();
|
|
86
|
+
await previewCmd();
|
|
84
87
|
});
|
|
85
|
-
//
|
|
86
|
-
// Default / Unknown command handling
|
|
87
|
-
// ------------------------------------------------------
|
|
88
|
+
// Handle unknown commands
|
|
88
89
|
program.on('command:*', () => {
|
|
89
90
|
console.error(chalk.red('ā Invalid command:'), program.args.join(' '));
|
|
90
91
|
console.log();
|
|
91
92
|
program.outputHelp();
|
|
92
93
|
process.exit(1);
|
|
93
94
|
});
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
// ------------------------------------------------------
|
|
97
|
-
if (isMainModule) {
|
|
95
|
+
// Entry Point (ESM-safe)
|
|
96
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
98
97
|
if (process.argv.length <= 2) {
|
|
99
98
|
console.clear();
|
|
100
99
|
showBanner();
|