@voltx/cli 0.3.4 → 0.3.5
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 +54 -22
- package/dist/build.d.mts +4 -3
- package/dist/build.d.ts +4 -3
- package/dist/build.js +56 -27
- package/dist/build.mjs +1 -2
- package/dist/chunk-2LHDOMF2.mjs +680 -0
- package/dist/chunk-5DVBIJXU.mjs +84 -0
- package/dist/chunk-65PVXS4D.mjs +894 -0
- package/dist/chunk-7JVIEGSA.mjs +141 -0
- package/dist/chunk-A4NA4AJN.mjs +786 -0
- package/dist/chunk-AAAHANST.mjs +839 -0
- package/dist/chunk-AD3WMFZF.mjs +205 -0
- package/dist/chunk-CSSHLVYS.mjs +83 -0
- package/dist/chunk-CWOSNV5O.mjs +150 -0
- package/dist/chunk-EI6XBYKB.mjs +84 -0
- package/dist/chunk-FI2W4L4S.mjs +205 -0
- package/dist/chunk-G2INQCCJ.mjs +907 -0
- package/dist/chunk-H2DTIOEO.mjs +150 -0
- package/dist/chunk-IS2WTE3C.mjs +138 -0
- package/dist/chunk-JECCDBYI.mjs +730 -0
- package/dist/chunk-KX2MRJUO.mjs +795 -0
- package/dist/chunk-LTGMHAZS.mjs +147 -0
- package/dist/chunk-OPO6RUFP.mjs +698 -0
- package/dist/chunk-PWQSKYAM.mjs +682 -0
- package/dist/chunk-Q5XCFN7L.mjs +1026 -0
- package/dist/chunk-QSU6FZC7.mjs +497 -0
- package/dist/chunk-RYWRFHEC.mjs +83 -0
- package/dist/chunk-SU4Q3PTH.mjs +201 -0
- package/dist/chunk-UXI3QSDN.mjs +121 -0
- package/dist/chunk-VD3CNPNP.mjs +123 -0
- package/dist/chunk-X6VOAPRJ.mjs +756 -0
- package/dist/cli.js +932 -307
- package/dist/cli.mjs +7 -6
- package/dist/create.d.mts +1 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +723 -191
- package/dist/create.mjs +1 -2
- package/dist/dev.d.mts +6 -4
- package/dist/dev.d.ts +6 -4
- package/dist/dev.js +119 -46
- package/dist/dev.mjs +1 -2
- package/dist/generate.js +13 -13
- package/dist/generate.mjs +1 -2
- package/dist/index.js +919 -295
- package/dist/index.mjs +5 -6
- package/dist/start.js +7 -17
- package/dist/start.mjs +1 -2
- package/dist/welcome.mjs +0 -1
- package/package.json +11 -3
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// src/dev.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { resolve, join } from "path";
|
|
4
|
+
import { existsSync, writeFileSync, unlinkSync } from "fs";
|
|
5
|
+
import { loadEnv } from "@voltx/core";
|
|
6
|
+
async function runDev(options = {}) {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const {
|
|
9
|
+
port,
|
|
10
|
+
entry = findEntryPoint(cwd),
|
|
11
|
+
clearScreen = true
|
|
12
|
+
} = options;
|
|
13
|
+
if (!entry) {
|
|
14
|
+
console.error("[voltx] Could not find entry point. Expected server.ts or src/index.ts");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const entryPath = resolve(cwd, entry);
|
|
18
|
+
if (!existsSync(entryPath)) {
|
|
19
|
+
console.error(`[voltx] Entry file not found: ${entry}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
loadEnv("development", cwd);
|
|
23
|
+
const hasViteConfig = existsSync(resolve(cwd, "vite.config.ts"));
|
|
24
|
+
const hasServerEntry = existsSync(resolve(cwd, "server.ts"));
|
|
25
|
+
const isFullStack = hasViteConfig || hasServerEntry;
|
|
26
|
+
if (isFullStack) {
|
|
27
|
+
await startViteDevServer(cwd, port, entry, options);
|
|
28
|
+
} else {
|
|
29
|
+
await startTsxWatch(cwd, port, entry, options);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function startViteDevServer(cwd, port, entry, options) {
|
|
33
|
+
const resolvedPort = port ?? (Number(process.env.PORT) || 3e3);
|
|
34
|
+
printDevBanner(entry, resolvedPort, true);
|
|
35
|
+
const userViteConfig = resolve(cwd, "vite.config.ts");
|
|
36
|
+
const hasUserViteConfig = existsSync(userViteConfig);
|
|
37
|
+
let tempConfigPath = null;
|
|
38
|
+
if (!hasUserViteConfig) {
|
|
39
|
+
tempConfigPath = resolve(cwd, "vite.config.voltx.ts");
|
|
40
|
+
const viteConfigContent = generateViteConfig(entry, resolvedPort);
|
|
41
|
+
writeFileSync(tempConfigPath, viteConfigContent, "utf-8");
|
|
42
|
+
}
|
|
43
|
+
const env = {
|
|
44
|
+
...process.env,
|
|
45
|
+
NODE_ENV: "development"
|
|
46
|
+
};
|
|
47
|
+
if (port) {
|
|
48
|
+
env.PORT = String(port);
|
|
49
|
+
}
|
|
50
|
+
const viteBin = findBin(cwd, "vite");
|
|
51
|
+
const viteArgs = ["dev"];
|
|
52
|
+
if (tempConfigPath) {
|
|
53
|
+
viteArgs.push("--config", tempConfigPath);
|
|
54
|
+
}
|
|
55
|
+
viteArgs.push("--port", String(resolvedPort));
|
|
56
|
+
let child;
|
|
57
|
+
if (viteBin) {
|
|
58
|
+
child = spawn(viteBin, viteArgs, { cwd, env, stdio: "inherit" });
|
|
59
|
+
} else {
|
|
60
|
+
child = spawn("npx", ["vite", ...viteArgs], { cwd, env, stdio: "inherit" });
|
|
61
|
+
}
|
|
62
|
+
const cleanup = () => {
|
|
63
|
+
if (tempConfigPath && existsSync(tempConfigPath)) {
|
|
64
|
+
try {
|
|
65
|
+
unlinkSync(tempConfigPath);
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
71
|
+
for (const signal of signals) {
|
|
72
|
+
process.on(signal, () => {
|
|
73
|
+
cleanup();
|
|
74
|
+
child.kill(signal);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
child.on("error", (err) => {
|
|
78
|
+
cleanup();
|
|
79
|
+
if (err.code === "ENOENT") {
|
|
80
|
+
console.error("[voltx] vite not found. Install it with: npm install -D vite @hono/vite-dev-server");
|
|
81
|
+
} else {
|
|
82
|
+
console.error("[voltx] Dev server error:", err.message);
|
|
83
|
+
}
|
|
84
|
+
process.exit(1);
|
|
85
|
+
});
|
|
86
|
+
child.on("exit", (code) => {
|
|
87
|
+
cleanup();
|
|
88
|
+
process.exit(code ?? 0);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function generateViteConfig(entry, port) {
|
|
92
|
+
return `// Auto-generated by VoltX \u2014 do not commit this file
|
|
93
|
+
import { defineConfig } from "vite";
|
|
94
|
+
import devServer from "@hono/vite-dev-server";
|
|
95
|
+
|
|
96
|
+
export default defineConfig({
|
|
97
|
+
server: {
|
|
98
|
+
port: ${port},
|
|
99
|
+
},
|
|
100
|
+
plugins: [
|
|
101
|
+
devServer({
|
|
102
|
+
entry: "${entry}",
|
|
103
|
+
exclude: [
|
|
104
|
+
/.*\\.tsx?($|\\?)/,
|
|
105
|
+
/.*\\.(s?css|less)($|\\?)/,
|
|
106
|
+
/.*\\.(svg|png|jpg|jpeg|gif|webp|ico)($|\\?)/,
|
|
107
|
+
/^\\/@.+$/,
|
|
108
|
+
/^\\/favicon\\.svg$/,
|
|
109
|
+
/^\\/node_modules\\/.*/,
|
|
110
|
+
/^\\/src\\/.*/,
|
|
111
|
+
],
|
|
112
|
+
injectClientScript: false,
|
|
113
|
+
}),
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
async function startTsxWatch(cwd, port, entry, options) {
|
|
119
|
+
const resolvedPort = port ?? (Number(process.env.PORT) || 3e3);
|
|
120
|
+
printDevBanner(entry, resolvedPort, false);
|
|
121
|
+
const env = {
|
|
122
|
+
...process.env,
|
|
123
|
+
NODE_ENV: "development"
|
|
124
|
+
};
|
|
125
|
+
if (port) {
|
|
126
|
+
env.PORT = String(port);
|
|
127
|
+
}
|
|
128
|
+
const tsxArgs = ["watch"];
|
|
129
|
+
if (options.clearScreen ?? true) {
|
|
130
|
+
tsxArgs.push("--clear-screen=false");
|
|
131
|
+
}
|
|
132
|
+
tsxArgs.push("--ignore=node_modules", "--ignore=dist", "--ignore=.turbo");
|
|
133
|
+
tsxArgs.push(entry);
|
|
134
|
+
const tsxBin = findBin(cwd, "tsx");
|
|
135
|
+
let child;
|
|
136
|
+
if (tsxBin) {
|
|
137
|
+
child = spawn(tsxBin, tsxArgs, { cwd, env, stdio: "inherit" });
|
|
138
|
+
} else {
|
|
139
|
+
child = spawn("npx", ["tsx", ...tsxArgs], { cwd, env, stdio: "inherit" });
|
|
140
|
+
}
|
|
141
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
142
|
+
for (const signal of signals) {
|
|
143
|
+
process.on(signal, () => {
|
|
144
|
+
child.kill(signal);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
child.on("error", (err) => {
|
|
148
|
+
if (err.code === "ENOENT") {
|
|
149
|
+
console.error("[voltx] tsx not found. Install it with: npm install -D tsx");
|
|
150
|
+
} else {
|
|
151
|
+
console.error("[voltx] Dev server error:", err.message);
|
|
152
|
+
}
|
|
153
|
+
process.exit(1);
|
|
154
|
+
});
|
|
155
|
+
child.on("exit", (code) => {
|
|
156
|
+
process.exit(code ?? 0);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function findEntryPoint(cwd) {
|
|
160
|
+
const candidates = [
|
|
161
|
+
"server.ts",
|
|
162
|
+
"server.js",
|
|
163
|
+
"src/index.ts",
|
|
164
|
+
"src/index.js",
|
|
165
|
+
"src/index.mts",
|
|
166
|
+
"src/main.ts",
|
|
167
|
+
"src/main.js",
|
|
168
|
+
"index.ts",
|
|
169
|
+
"index.js"
|
|
170
|
+
];
|
|
171
|
+
for (const candidate of candidates) {
|
|
172
|
+
if (existsSync(join(cwd, candidate))) {
|
|
173
|
+
return candidate;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
function findBin(cwd, name) {
|
|
179
|
+
const paths = [
|
|
180
|
+
join(cwd, "node_modules", ".bin", name),
|
|
181
|
+
join(cwd, "..", "node_modules", ".bin", name),
|
|
182
|
+
join(cwd, "..", "..", "node_modules", ".bin", name)
|
|
183
|
+
];
|
|
184
|
+
for (const p of paths) {
|
|
185
|
+
if (existsSync(p)) return p;
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
function printDevBanner(entry, port, fullStack) {
|
|
190
|
+
console.log("");
|
|
191
|
+
console.log(" \u26A1 VoltX Dev Server");
|
|
192
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
193
|
+
console.log(` Entry: ${entry}`);
|
|
194
|
+
console.log(` Port: ${port}`);
|
|
195
|
+
console.log(` Mode: ${fullStack ? "full-stack (Vite + Hono)" : "API-only (tsx watch)"}`);
|
|
196
|
+
if (fullStack) {
|
|
197
|
+
console.log(` HMR: enabled (frontend + backend)`);
|
|
198
|
+
}
|
|
199
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
200
|
+
console.log("");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export {
|
|
204
|
+
runDev
|
|
205
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/start.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { resolve, join } from "path";
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { loadEnv } from "@voltx/core";
|
|
6
|
+
async function runStart(options = {}) {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const { port, outDir = "dist" } = options;
|
|
9
|
+
const distDir = resolve(cwd, outDir);
|
|
10
|
+
if (!existsSync(distDir)) {
|
|
11
|
+
console.error(`[voltx] Build output not found at ${outDir}/`);
|
|
12
|
+
console.error("[voltx] Run `voltx build` first to create a production build.");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const entry = options.entry ?? findDistEntry(distDir);
|
|
16
|
+
if (!entry) {
|
|
17
|
+
console.error(`[voltx] No entry file found in ${outDir}/`);
|
|
18
|
+
console.error("[voltx] Expected index.js, index.mjs, or main.js");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const entryPath = resolve(distDir, entry);
|
|
22
|
+
if (!existsSync(entryPath)) {
|
|
23
|
+
console.error(`[voltx] Entry file not found: ${outDir}/${entry}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
loadEnv("production", cwd);
|
|
27
|
+
const env = {
|
|
28
|
+
...process.env,
|
|
29
|
+
NODE_ENV: "production"
|
|
30
|
+
};
|
|
31
|
+
if (port) {
|
|
32
|
+
env.PORT = String(port);
|
|
33
|
+
}
|
|
34
|
+
console.log("");
|
|
35
|
+
console.log(" \u26A1 VoltX Production Server");
|
|
36
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
37
|
+
console.log(` Entry: ${outDir}/${entry}`);
|
|
38
|
+
if (port) {
|
|
39
|
+
console.log(` Port: ${port}`);
|
|
40
|
+
}
|
|
41
|
+
console.log(` Mode: production`);
|
|
42
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
43
|
+
console.log("");
|
|
44
|
+
const child = spawn("node", [entryPath], {
|
|
45
|
+
cwd,
|
|
46
|
+
env,
|
|
47
|
+
stdio: "inherit"
|
|
48
|
+
});
|
|
49
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
50
|
+
for (const signal of signals) {
|
|
51
|
+
process.on(signal, () => {
|
|
52
|
+
child.kill(signal);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
child.on("error", (err) => {
|
|
56
|
+
console.error("[voltx] Failed to start production server:", err.message);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
child.on("exit", (code) => {
|
|
60
|
+
process.exit(code ?? 0);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function findDistEntry(distDir) {
|
|
64
|
+
const candidates = [
|
|
65
|
+
"index.mjs",
|
|
66
|
+
"index.js",
|
|
67
|
+
"index.cjs",
|
|
68
|
+
"main.mjs",
|
|
69
|
+
"main.js",
|
|
70
|
+
"src/index.mjs",
|
|
71
|
+
"src/index.js"
|
|
72
|
+
];
|
|
73
|
+
for (const candidate of candidates) {
|
|
74
|
+
if (existsSync(join(distDir, candidate))) {
|
|
75
|
+
return candidate;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
runStart
|
|
83
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// src/build.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { resolve, join } from "path";
|
|
4
|
+
import { existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { loadEnv } from "@voltx/core";
|
|
6
|
+
async function runBuild(options = {}) {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const {
|
|
9
|
+
entry = findEntryPoint(cwd),
|
|
10
|
+
outDir = "dist",
|
|
11
|
+
minify = true,
|
|
12
|
+
sourcemap = false
|
|
13
|
+
} = options;
|
|
14
|
+
if (!entry) {
|
|
15
|
+
console.error("[voltx] Could not find entry point. Expected server.ts or src/index.ts");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const entryPath = resolve(cwd, entry);
|
|
19
|
+
if (!existsSync(entryPath)) {
|
|
20
|
+
console.error(`[voltx] Entry file not found: ${entry}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
loadEnv("production", cwd);
|
|
24
|
+
const hasViteConfig = existsSync(resolve(cwd, "vite.config.ts"));
|
|
25
|
+
const hasServerEntry = existsSync(resolve(cwd, "server.ts"));
|
|
26
|
+
const isFullStack = hasViteConfig || hasServerEntry;
|
|
27
|
+
console.log("");
|
|
28
|
+
console.log(" \u26A1 VoltX Build");
|
|
29
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
30
|
+
console.log(` Entry: ${entry}`);
|
|
31
|
+
console.log(` Output: ${outDir}/`);
|
|
32
|
+
console.log(` Mode: ${isFullStack ? "full-stack" : "API-only"}`);
|
|
33
|
+
console.log(` Minify: ${minify}`);
|
|
34
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
35
|
+
console.log("");
|
|
36
|
+
mkdirSync(resolve(cwd, outDir), { recursive: true });
|
|
37
|
+
if (isFullStack) {
|
|
38
|
+
await buildFullStack(cwd, entry, outDir, minify, sourcemap);
|
|
39
|
+
} else {
|
|
40
|
+
await buildApiOnly(cwd, entry, outDir, minify, sourcemap);
|
|
41
|
+
}
|
|
42
|
+
console.log("");
|
|
43
|
+
console.log(" \u26A1 Build complete!");
|
|
44
|
+
console.log(` Run \`voltx start\` to start the production server.`);
|
|
45
|
+
console.log("");
|
|
46
|
+
}
|
|
47
|
+
async function buildFullStack(cwd, entry, outDir, minify, sourcemap) {
|
|
48
|
+
const ssrEntry = existsSync(join(cwd, "src", "entry-server.tsx")) ? "src/entry-server.tsx" : null;
|
|
49
|
+
const totalSteps = ssrEntry ? 3 : 2;
|
|
50
|
+
console.log(` [1/${totalSteps}] Building client...`);
|
|
51
|
+
const viteBin = findBin(cwd, "vite");
|
|
52
|
+
const clientOutDir = join(outDir, "client");
|
|
53
|
+
await runCommand(
|
|
54
|
+
viteBin ?? "npx",
|
|
55
|
+
viteBin ? ["build", "--outDir", clientOutDir] : ["vite", "build", "--outDir", clientOutDir],
|
|
56
|
+
cwd
|
|
57
|
+
);
|
|
58
|
+
console.log(" \u2713 Client built");
|
|
59
|
+
if (ssrEntry) {
|
|
60
|
+
console.log(` [2/${totalSteps}] Building SSR bundle...`);
|
|
61
|
+
const serverOutDir = join(outDir, "server");
|
|
62
|
+
await runCommand(
|
|
63
|
+
viteBin ?? "npx",
|
|
64
|
+
viteBin ? ["build", "--ssr", ssrEntry, "--outDir", serverOutDir] : ["vite", "build", "--ssr", ssrEntry, "--outDir", serverOutDir],
|
|
65
|
+
cwd
|
|
66
|
+
);
|
|
67
|
+
console.log(" \u2713 SSR bundle built");
|
|
68
|
+
}
|
|
69
|
+
const serverStep = ssrEntry ? 3 : 2;
|
|
70
|
+
console.log(` [${serverStep}/${totalSteps}] Building server...`);
|
|
71
|
+
await buildServer(cwd, entry, outDir, minify, sourcemap);
|
|
72
|
+
console.log(" \u2713 Server built");
|
|
73
|
+
}
|
|
74
|
+
async function buildApiOnly(cwd, entry, outDir, minify, sourcemap) {
|
|
75
|
+
console.log(" [1/1] Building server...");
|
|
76
|
+
await buildServer(cwd, entry, outDir, minify, sourcemap);
|
|
77
|
+
console.log(" \u2713 Server built");
|
|
78
|
+
}
|
|
79
|
+
async function buildServer(cwd, entry, outDir, minify, sourcemap) {
|
|
80
|
+
const tsupBin = findBin(cwd, "tsup");
|
|
81
|
+
const tsupArgs = [
|
|
82
|
+
entry,
|
|
83
|
+
"--format",
|
|
84
|
+
"esm",
|
|
85
|
+
"--out-dir",
|
|
86
|
+
outDir,
|
|
87
|
+
"--clean",
|
|
88
|
+
"--target",
|
|
89
|
+
"node20",
|
|
90
|
+
"--no-splitting"
|
|
91
|
+
];
|
|
92
|
+
if (minify) tsupArgs.push("--minify");
|
|
93
|
+
if (sourcemap) tsupArgs.push("--sourcemap");
|
|
94
|
+
await runCommand(
|
|
95
|
+
tsupBin ?? "npx",
|
|
96
|
+
tsupBin ? tsupArgs : ["tsup", ...tsupArgs],
|
|
97
|
+
cwd
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
function findEntryPoint(cwd) {
|
|
101
|
+
const candidates = [
|
|
102
|
+
"server.ts",
|
|
103
|
+
"server.js",
|
|
104
|
+
"src/index.ts",
|
|
105
|
+
"src/index.js",
|
|
106
|
+
"src/index.mts",
|
|
107
|
+
"src/main.ts",
|
|
108
|
+
"src/main.js"
|
|
109
|
+
];
|
|
110
|
+
for (const candidate of candidates) {
|
|
111
|
+
if (existsSync(join(cwd, candidate))) {
|
|
112
|
+
return candidate;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
function findBin(cwd, name) {
|
|
118
|
+
const paths = [
|
|
119
|
+
join(cwd, "node_modules", ".bin", name),
|
|
120
|
+
join(cwd, "..", "node_modules", ".bin", name),
|
|
121
|
+
join(cwd, "..", "..", "node_modules", ".bin", name)
|
|
122
|
+
];
|
|
123
|
+
for (const p of paths) {
|
|
124
|
+
if (existsSync(p)) return p;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
function runCommand(cmd, args, cwd) {
|
|
129
|
+
return new Promise((resolve2, reject) => {
|
|
130
|
+
const child = spawn(cmd, args, {
|
|
131
|
+
cwd,
|
|
132
|
+
stdio: "inherit",
|
|
133
|
+
env: { ...process.env, NODE_ENV: "production" }
|
|
134
|
+
});
|
|
135
|
+
child.on("error", (err) => {
|
|
136
|
+
if (err.code === "ENOENT") {
|
|
137
|
+
console.error(`[voltx] ${cmd} not found. Install it with: npm install -D ${cmd}`);
|
|
138
|
+
}
|
|
139
|
+
reject(err);
|
|
140
|
+
});
|
|
141
|
+
child.on("exit", (code) => {
|
|
142
|
+
if (code === 0) resolve2();
|
|
143
|
+
else reject(new Error(`${cmd} exited with code ${code}`));
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export {
|
|
149
|
+
runBuild
|
|
150
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// src/start.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { resolve, join } from "path";
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { loadEnv } from "@voltx/core";
|
|
6
|
+
async function runStart(options = {}) {
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
const { port, outDir = "dist" } = options;
|
|
9
|
+
const distDir = resolve(cwd, outDir);
|
|
10
|
+
if (!existsSync(distDir)) {
|
|
11
|
+
console.error(`[voltx] Build output not found at ${outDir}/`);
|
|
12
|
+
console.error("[voltx] Run `voltx build` first to create a production build.");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const entry = options.entry ?? findDistEntry(distDir);
|
|
16
|
+
if (!entry) {
|
|
17
|
+
console.error(`[voltx] No entry file found in ${outDir}/`);
|
|
18
|
+
console.error("[voltx] Expected index.js, index.mjs, or main.js");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const entryPath = resolve(distDir, entry);
|
|
22
|
+
if (!existsSync(entryPath)) {
|
|
23
|
+
console.error(`[voltx] Entry file not found: ${outDir}/${entry}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
loadEnv("production", cwd);
|
|
27
|
+
const env = {
|
|
28
|
+
...process.env,
|
|
29
|
+
NODE_ENV: "production"
|
|
30
|
+
};
|
|
31
|
+
if (port) {
|
|
32
|
+
env.PORT = String(port);
|
|
33
|
+
}
|
|
34
|
+
const hasClientBuild = existsSync(resolve(distDir, "client"));
|
|
35
|
+
console.log("");
|
|
36
|
+
console.log(" \u26A1 VoltX Production Server");
|
|
37
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
38
|
+
console.log(` Entry: ${outDir}/${entry}`);
|
|
39
|
+
if (port) {
|
|
40
|
+
console.log(` Port: ${port}`);
|
|
41
|
+
}
|
|
42
|
+
console.log(` Mode: ${hasClientBuild ? "full-stack" : "API-only"}`);
|
|
43
|
+
console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
44
|
+
console.log("");
|
|
45
|
+
const child = spawn("node", [entryPath], {
|
|
46
|
+
cwd,
|
|
47
|
+
env,
|
|
48
|
+
stdio: "inherit"
|
|
49
|
+
});
|
|
50
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
51
|
+
for (const signal of signals) {
|
|
52
|
+
process.on(signal, () => {
|
|
53
|
+
child.kill(signal);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
child.on("error", (err) => {
|
|
57
|
+
console.error("[voltx] Failed to start production server:", err.message);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
});
|
|
60
|
+
child.on("exit", (code) => {
|
|
61
|
+
process.exit(code ?? 0);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function findDistEntry(distDir) {
|
|
65
|
+
const candidates = [
|
|
66
|
+
"server.mjs",
|
|
67
|
+
"server.js",
|
|
68
|
+
"index.mjs",
|
|
69
|
+
"index.js",
|
|
70
|
+
"index.cjs",
|
|
71
|
+
"main.mjs",
|
|
72
|
+
"main.js"
|
|
73
|
+
];
|
|
74
|
+
for (const candidate of candidates) {
|
|
75
|
+
if (existsSync(join(distDir, candidate))) {
|
|
76
|
+
return candidate;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
runStart
|
|
84
|
+
};
|