外观
开发
本地开发时执行下列命令即可。
全量开发(不含 SSG 着陆页/文档站开发):
bash
npm run dev1
SSG 着陆页/文档站开发:
bash
# 将 `blog` 换成实际的目录名
cross-env TASKS=ssg:blog node --run dev1
2
2
支持 TASKS 参数
该命令支持通过 TASKS 来指定具体执行哪些任务(不传的话默认执行全部任务)。
该参数支持白名单和黑名单模式,并且支持 * 通配符。注意,白名单和黑名单模式不可混用,以避免歧义。
bash
# 白名单:执行所有 spa: 开头的任务
cross-env TASKS=spa:* npm run <example-command>
# 白名单:混合精确匹配和通配符
cross-env TASKS=cleanAllBuildFiles,ssg:* npm run <example-command>
# 黑名单:排除所有 spa: 开头的任务
cross-env TASKS=-spa:* npm run <example-command>1
2
3
4
5
6
2
3
4
5
6
发布正式环境应尽量使用内网地址
在正式环境,能通过内网连接的服务(比如数据库),应尽可能都使用内网地址来访问。
scripts/dev.mts 源码
typescript
import { logInfo, joinPath, isPathExists } from "nsuite";
import open from "open";
import {
APP_FRONT_PORT,
HEALTH_CHECK_PATH,
OPEN_PATH_IN_DEV,
PATH_APPS_HOME,
PATH_PUBLIC,
PATH_ROOT,
SERVER_HOST,
SERVER_PORT,
STANDALONE_APPS_NAMES,
STANDALONE_APPS_PORT,
SUB_APPS,
} from "#utils/ConstantUtils";
import { cleanAllBuildFiles } from "#scripts/utils/clean-files";
import { runCommandInChildProcess } from "#scripts/utils/child-process";
import { buildPublic, checkServerHealth, reloadOnFilesChange } from "#scripts/utils/public-builder";
import { useTask } from "#scripts/utils/use-task";
const allowTasks = process.env.TASKS || "";
const { killAllChildProcessWhenSuitable, addTask, runTasksSequentially, printTaskStatisticsInfo } =
useTask({ allowTasks });
/**
* 异常时关闭所有子进程
*/
killAllChildProcessWhenSuitable();
/**
* 编译开始前先清空所有可能遗留的编译产物
*/
addTask("cleanAllBuildFiles", async () => {
await cleanAllBuildFiles();
});
/**
* 启动主服务
*/
addTask("server", async (payload) => {
const { progress, taskName } = payload;
const { promise } = runCommandInChildProcess({
appName: `${progress.current}/${progress.total}-${taskName}`,
command: "node",
args: `--watch --watch-preserve-output ./src/bin/www.mts`,
env: process.env,
onStdout: (data: string, resolve) => {
if (data.includes("Server is running on port")) {
resolve(0);
}
},
});
const exitCode = await promise;
if (exitCode) {
throw new Error(`Task ${taskName} failed: exitCode=${exitCode}`);
}
});
/**
* 启动消费者服务
*/
addTask("consumer-bugs", async (payload) => {
const { taskName, progress } = payload;
const { promise } = runCommandInChildProcess({
appName: `${progress.current}/${progress.total}-${taskName}`,
command: "node",
args: `--watch --watch-preserve-output ./consumers/bugs.mts`,
env: process.env,
onStdout: (data: string, resolve) => {
if (data.includes("成功启动bugs相关队列消费脚本")) {
resolve(0);
}
},
});
const exitCode = await promise;
if (exitCode) {
throw new Error(`Task ${taskName} failed: exitCode=${exitCode}`);
}
});
/**
* 顺序启动 SPA apps
*/
for (const app of SUB_APPS) {
const { appName, appPort } = app;
addTask(appName, async (payload) => {
const { progress, taskName } = payload;
const { promise } = runCommandInChildProcess({
appName: `${progress.current}/${progress.total}-SPA-${appName}`,
command: "npx",
args: `cross-env APP_NAME=${appName} PORT=${appPort} vite serve ./apps/${appName}`,
env: process.env,
onStdout: (data: string, resolve) => {
if (data.includes("to show help")) {
resolve(0);
}
},
});
const exitCode = await promise;
if (exitCode) {
throw new Error(`Task ${taskName} failed: exitCode=${exitCode}`);
}
});
}
if (allowTasks) {
const allAllowTasks = allowTasks.split(",");
const ssgTask = allAllowTasks.find((taskName) => taskName.startsWith("ssg:"));
if (ssgTask) {
const appName = ssgTask.split(":")[1] || "";
if (STANDALONE_APPS_NAMES.includes(appName)) {
addTask(ssgTask, async (payload) => {
const { progress, taskName } = payload;
// 区分应用类型:Nuxt 4 应用、VitePress 2 应用
const pathApp = joinPath(PATH_APPS_HOME, appName);
const pathNuxtConfig = joinPath(pathApp, "nuxt.config.ts");
const isNuxt = await isPathExists(pathNuxtConfig);
const args = isNuxt
? `cross-env APP_NAME=${appName} PORT=${STANDALONE_APPS_PORT} nuxt dev --cwd=./apps-home/${appName}`
: `cross-env PORT=${STANDALONE_APPS_PORT} vitepress dev ./apps-home/${appName}/config`;
const { promise } = runCommandInChildProcess({
appName: `${progress.current}/${progress.total}-${taskName}`,
command: "npx",
args,
env: process.env,
});
const exitCode = await promise;
if (exitCode) {
throw new Error(`Task ${taskName} failed: exitCode=${exitCode}`);
}
});
}
}
}
/**
* 启动 public 目录下 js/*.mts 和 css/*.scss 文件的实时编译
*/
addTask("browerSync", async () => {
await checkServerHealth({
healthCheckUrl: `http://${SERVER_HOST}:${SERVER_PORT}${HEALTH_CHECK_PATH}`,
conditionCallback: (res) => res.data.code === 200,
interval: 1000,
});
const browserSync = reloadOnFilesChange([
joinPath(PATH_PUBLIC, "js/**/*.min.js"),
joinPath(PATH_PUBLIC, "css/**/*.min.css"),
joinPath(PATH_ROOT, "views/**/*.ejs"),
]);
await new Promise((resolve) => {
browserSync.init(
{
port: APP_FRONT_PORT,
host: SERVER_HOST,
online: true,
open: false,
notify: false,
// @ts-ignore
ws: true,
proxy: `${SERVER_HOST}:${SERVER_PORT}`,
},
resolve,
);
});
await buildPublic({
enableWatch: true,
});
});
await runTasksSequentially();
printTaskStatisticsInfo();
if (OPEN_PATH_IN_DEV) {
const openUrl = `http://${SERVER_HOST}:${APP_FRONT_PORT}${OPEN_PATH_IN_DEV}`;
const localUrl = `http://localhost:${APP_FRONT_PORT}${OPEN_PATH_IN_DEV}`;
await open(openUrl);
logInfo(`\n🚀 启动成功,请访问: ${openUrl} 或 ${localUrl}`);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182