@ynight/cli 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +251 -2
- package/config/client.json +14 -0
- package/config/server.json +16 -0
- package/dist/bin.d.ts +26 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +114 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands.d.ts +38 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +225 -0
- package/dist/commands.js.map +1 -0
- package/dist/dev-entry.d.ts +2 -0
- package/dist/dev-entry.d.ts.map +1 -0
- package/dist/dev-entry.js +348 -0
- package/dist/dev-entry.js.map +1 -0
- package/dist/dev-proxy.d.ts +21 -0
- package/dist/dev-proxy.d.ts.map +1 -0
- package/dist/dev-proxy.js +98 -0
- package/dist/dev-proxy.js.map +1 -0
- package/dist/dev-watcher.d.ts +24 -0
- package/dist/dev-watcher.d.ts.map +1 -0
- package/dist/dev-watcher.js +242 -0
- package/dist/dev-watcher.js.map +1 -0
- package/dist/generate-types.d.ts +14 -0
- package/dist/generate-types.d.ts.map +1 -0
- package/dist/generate-types.js +190 -0
- package/dist/generate-types.js.map +1 -0
- package/dist/output-safety.d.ts +47 -0
- package/dist/output-safety.d.ts.map +1 -0
- package/dist/output-safety.js +145 -0
- package/dist/output-safety.js.map +1 -0
- package/dist/project-config.d.ts +72 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +221 -0
- package/dist/project-config.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +84 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +176 -0
- package/dist/utils.js.map +1 -0
- package/dist/webpack-build.d.ts +87 -0
- package/dist/webpack-build.d.ts.map +1 -0
- package/dist/webpack-build.js +475 -0
- package/dist/webpack-build.js.map +1 -0
- package/package.json +64 -3
- package/tsconfig/client.json +3 -0
- package/tsconfig/server.json +3 -0
package/dist/commands.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HELP = exports.CLI_VERSION = void 0;
|
|
4
|
+
exports.typeCheck = typeCheck;
|
|
5
|
+
exports.build = build;
|
|
6
|
+
exports.dev = dev;
|
|
7
|
+
const tslib_1 = require("tslib");
|
|
8
|
+
const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
10
|
+
const node_child_process_1 = require("node:child_process");
|
|
11
|
+
const typescript_1 = tslib_1.__importDefault(require("typescript"));
|
|
12
|
+
const generate_types_1 = require("./generate-types");
|
|
13
|
+
const webpack_build_1 = require("./webpack-build");
|
|
14
|
+
const project_config_1 = require("./project-config");
|
|
15
|
+
const output_safety_1 = require("./output-safety");
|
|
16
|
+
const utils_1 = require("./utils");
|
|
17
|
+
/** CLI 自身的版本号,始终以发布包根目录的 package.json 为准。 */
|
|
18
|
+
const packageManifest = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.resolve(__dirname, '../package.json'), 'utf8'));
|
|
19
|
+
exports.CLI_VERSION = packageManifest.version;
|
|
20
|
+
/**
|
|
21
|
+
* CLI 帮助文本:列出所有子命令与参数;保持简洁,便于直接打印。
|
|
22
|
+
*
|
|
23
|
+
* 注意:每个子命令的实际解析在 `bin.ts::parseArgs` 中。
|
|
24
|
+
*/
|
|
25
|
+
exports.HELP = `Elpis 项目 CLI ${exports.CLI_VERSION}
|
|
26
|
+
|
|
27
|
+
用法:
|
|
28
|
+
elpis dev [--root 路径] [--port 端口] [--web-port 端口]
|
|
29
|
+
elpis build [--root 路径]
|
|
30
|
+
elpis type-check [--root 路径]
|
|
31
|
+
elpis generate:types [--check] [--root 路径]
|
|
32
|
+
elpis --help | --version`;
|
|
33
|
+
/**
|
|
34
|
+
* 在执行 type-check / dev 等命令前强制校验配置文件存在。
|
|
35
|
+
*
|
|
36
|
+
* `loadProjectConfig` 本身已会抛错,这里再包一层只是为了在错误信息中带上更明确的上下文。
|
|
37
|
+
*/
|
|
38
|
+
function requireConfig(root) {
|
|
39
|
+
(0, project_config_1.loadProjectConfig)(root, 'development');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 用 `ts` 直接解析并检查 `tsconfig.client.json` 配置是否合法(不实际跑 vue-tsc)。
|
|
43
|
+
*
|
|
44
|
+
* 解决的问题:
|
|
45
|
+
* - vue-tsc 启动较慢,先用 TS API 校验配置,避免在配置错误时仍然走完启动流程;
|
|
46
|
+
* - 过滤掉 `18003`("No inputs were found"),零扩展项目允许通过。
|
|
47
|
+
*/
|
|
48
|
+
function runVueTypeCheck(root) {
|
|
49
|
+
const config = node_path_1.default.resolve(root, 'tsconfig.client.json');
|
|
50
|
+
if (!node_fs_1.default.existsSync(config)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const read = typescript_1.default.readConfigFile(config, typescript_1.default.sys.readFile);
|
|
54
|
+
if (read.error) {
|
|
55
|
+
throw new Error(typescript_1.default.flattenDiagnosticMessageText(read.error.messageText, '\n'));
|
|
56
|
+
}
|
|
57
|
+
// 显式注册 .vue 为"deferred script"以便 TS 解析包含 SFC 的工程。
|
|
58
|
+
const parsed = typescript_1.default.parseJsonConfigFileContent(read.config, typescript_1.default.sys, root, undefined, config, undefined, [{ extension: '.vue', isMixedContent: true, scriptKind: typescript_1.default.ScriptKind.Deferred }]);
|
|
59
|
+
const errors = parsed.errors.filter(error => error.code !== 18003);
|
|
60
|
+
if (errors.length) {
|
|
61
|
+
throw new Error(errors.map(error => typescript_1.default.flattenDiagnosticMessageText(error.messageText, '\n')).join('\n'));
|
|
62
|
+
}
|
|
63
|
+
// 没有客户端源文件是有效的零扩展项目;有输入时始终交给 vue-tsc 检查。
|
|
64
|
+
if (parsed.fileNames.length === 0) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
(0, utils_1.runVueTsc)(root, config);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 同步执行完整类型检查:
|
|
71
|
+
* 1. 校验配置文件;
|
|
72
|
+
* 2. 重新生成业务模块声明文件;
|
|
73
|
+
* 3. 使用 `tsc --noEmit` 检查服务端代码;
|
|
74
|
+
* 4. 调用 `vue-tsc` 检查客户端代码。
|
|
75
|
+
*/
|
|
76
|
+
function typeCheck(options) {
|
|
77
|
+
requireConfig(options.root);
|
|
78
|
+
(0, generate_types_1.generateTypes)(options.root);
|
|
79
|
+
(0, utils_1.runTsc)(options.root, node_path_1.default.resolve(options.root, 'tsconfig.server.json'), ['--noEmit']);
|
|
80
|
+
runVueTypeCheck(options.root);
|
|
81
|
+
console.log('类型检查通过');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 计算"编译后"服务端入口的相对路径(`.ts` → `.js`),用于生成 production launcher。
|
|
85
|
+
*
|
|
86
|
+
* 与 `configForCompiledModules` 不同,这里返回的是相对路径字符串,便于硬编码进启动器。
|
|
87
|
+
*/
|
|
88
|
+
function compiledEntryRelative(root, config) {
|
|
89
|
+
const entry = (0, project_config_1.resolveServerEntry)(root, config);
|
|
90
|
+
const relative = node_path_1.default.relative(root, entry);
|
|
91
|
+
return (relative
|
|
92
|
+
.replace(/\.(?:tsx?|mts|cts)$/, '.js')
|
|
93
|
+
// 统一使用 POSIX 分隔符,方便跨平台 require 路径。
|
|
94
|
+
.split(node_path_1.default.sep)
|
|
95
|
+
.join('/'));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 生成 production launcher `dist/server/index.js`:
|
|
99
|
+
* 加载编译后的服务端入口、调用框架 `startApplication` 启动服务。
|
|
100
|
+
*
|
|
101
|
+
* 注意:运行时配置通过 `JSON.stringify` 内联,避免依赖外部配置文件。
|
|
102
|
+
*/
|
|
103
|
+
function writeProductionLauncher(stagingDist, entryRelative, config) {
|
|
104
|
+
const runtimeConfig = (0, project_config_1.configForCompiledModules)(config);
|
|
105
|
+
const launcher = `const path = require('node:path');
|
|
106
|
+
const { startApplication } = require('@ynight/framework/server');
|
|
107
|
+
const serverPath = path.resolve(__dirname, '../app', ${JSON.stringify(entryRelative)});
|
|
108
|
+
const loaded = require(serverPath);
|
|
109
|
+
const server = loaded.default || loaded;
|
|
110
|
+
startApplication({ rootDir: path.resolve(__dirname, '../app'), mode: 'production', moduleExtension: '.js', config: ${JSON.stringify(runtimeConfig)}, server, publicRoot: path.resolve(__dirname, '../public') })
|
|
111
|
+
.catch(error => { console.error(error instanceof Error ? error.message : error); process.exitCode = 1; });
|
|
112
|
+
`;
|
|
113
|
+
node_fs_1.default.mkdirSync(node_path_1.default.resolve(stagingDist, 'server'), { recursive: true });
|
|
114
|
+
node_fs_1.default.writeFileSync(node_path_1.default.resolve(stagingDist, 'server/index.js'), launcher);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 生产构建主流程:
|
|
118
|
+
* 1. 校验配置与输出目录安全;
|
|
119
|
+
* 2. 重新生成类型声明;
|
|
120
|
+
* 3. 在 `.elpis/build/{buildId}` 下构建 staging 产物;
|
|
121
|
+
* 4. 校验页面模板齐全;
|
|
122
|
+
* 5. 写入 manifest;
|
|
123
|
+
* 6. 原子替换到正式输出目录。
|
|
124
|
+
*
|
|
125
|
+
* 任何步骤失败都会在 catch 中打印消息并向上抛出,调用方决定是否回滚。
|
|
126
|
+
*/
|
|
127
|
+
async function build(options) {
|
|
128
|
+
const config = (0, project_config_1.loadEffectiveConfig)(options.root, 'production');
|
|
129
|
+
const outputRoot = (0, output_safety_1.safeOutputRoot)(options.root, config);
|
|
130
|
+
// 生产模式 force generateTypes,无需复用 dev 时可能存在的缓存。
|
|
131
|
+
(0, generate_types_1.generateTypes)(options.root, false, 'production');
|
|
132
|
+
// buildId 用于事务记录和备份目录命名,确保唯一可追溯。
|
|
133
|
+
const buildId = `${Date.now()}-${process.pid}`;
|
|
134
|
+
const buildRoot = (0, output_safety_1.safeGeneratedPath)(options.root, `.elpis/build/${buildId}`);
|
|
135
|
+
const compiledApp = node_path_1.default.resolve(buildRoot, 'app');
|
|
136
|
+
const stagingDist = node_path_1.default.resolve(buildRoot, 'dist');
|
|
137
|
+
const stagingPublic = node_path_1.default.resolve(stagingDist, 'public');
|
|
138
|
+
node_fs_1.default.mkdirSync(buildRoot, { recursive: true });
|
|
139
|
+
try {
|
|
140
|
+
// 先用 tsc 把服务端 TS 编译到 staging。
|
|
141
|
+
(0, utils_1.runTsc)(options.root, node_path_1.default.resolve(options.root, 'tsconfig.server.json'), [
|
|
142
|
+
'--outDir',
|
|
143
|
+
compiledApp,
|
|
144
|
+
]);
|
|
145
|
+
runVueTypeCheck(options.root);
|
|
146
|
+
(0, utils_1.copyDirectory)(compiledApp, node_path_1.default.resolve(stagingDist, 'app'));
|
|
147
|
+
(0, utils_1.copyDirectory)(node_path_1.default.resolve(options.root, 'public'), stagingPublic);
|
|
148
|
+
// 前端构建产物直接落到 staging/public,Koa 静态根就是 public。
|
|
149
|
+
await (0, webpack_build_1.buildFrontend)((0, webpack_build_1.createFrontendOptions)(options, 'production', stagingPublic, config));
|
|
150
|
+
const missingPage = (0, webpack_build_1.pageOutputPaths)(stagingPublic).find(page => !node_fs_1.default.existsSync(page));
|
|
151
|
+
if (missingPage) {
|
|
152
|
+
throw new Error(`前端构建缺少页面模板: ${missingPage}`);
|
|
153
|
+
}
|
|
154
|
+
// 写入 production launcher 与 manifest。
|
|
155
|
+
writeProductionLauncher(stagingDist, compiledEntryRelative(options.root, config), config);
|
|
156
|
+
const manifest = {
|
|
157
|
+
frameworkVersion: exports.CLI_VERSION,
|
|
158
|
+
configProtocolVersion: 1,
|
|
159
|
+
app: 'app',
|
|
160
|
+
public: 'public',
|
|
161
|
+
pages: ['project-list', 'dashboard', 'error'],
|
|
162
|
+
publicAssetPrefix: '/dist/',
|
|
163
|
+
};
|
|
164
|
+
node_fs_1.default.writeFileSync(node_path_1.default.resolve(stagingDist, 'elpis-manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
165
|
+
// 二次校验:避免配置在生成过程中被修改导致 output 不再合法。
|
|
166
|
+
(0, output_safety_1.safeOutputRoot)(options.root, config);
|
|
167
|
+
// 原子替换:先把 staging 整体 rename 到 output,失败时 backup 还原。
|
|
168
|
+
(0, output_safety_1.replaceDirectoryAtomically)(options.root, stagingDist, outputRoot, buildId);
|
|
169
|
+
console.log(`生产产物已生成: ${outputRoot}`);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error(error instanceof Error ? error.message : error);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 开发模式入口(spawn 当前 CLI 重新跑 dev-entry.ts)。
|
|
178
|
+
*
|
|
179
|
+
* 这里采用子进程模式的原因:
|
|
180
|
+
* - dev-entry 维护大量长生命周期资源(koa server / webpack-dev-server / tsc 子进程 / 定时器),
|
|
181
|
+
* 用独立子进程可借助 OS 进程隔离避免污染父进程;
|
|
182
|
+
* - 父进程仅负责把 SIGINT / SIGTERM 透传给子进程,简化资源回收。
|
|
183
|
+
*/
|
|
184
|
+
async function dev(options) {
|
|
185
|
+
requireConfig(options.root);
|
|
186
|
+
(0, generate_types_1.generateTypes)(options.root);
|
|
187
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [
|
|
188
|
+
// __dirname 指向编译后的目录,因此 dev-entry.js 与 dev-entry.ts 同名。
|
|
189
|
+
node_path_1.default.resolve(__dirname, 'dev-entry.js'),
|
|
190
|
+
'--root',
|
|
191
|
+
options.root,
|
|
192
|
+
...(options.port === undefined ? [] : ['--port', String(options.port)]),
|
|
193
|
+
...(options.webPort === undefined ? [] : ['--web-port', String(options.webPort)]),
|
|
194
|
+
], {
|
|
195
|
+
cwd: options.root,
|
|
196
|
+
// stdio: 'inherit':让子进程日志直接输出到父终端。
|
|
197
|
+
stdio: 'inherit',
|
|
198
|
+
env: { ...process.env },
|
|
199
|
+
});
|
|
200
|
+
// 把父进程收到的终止信号透传给子进程,由 dev-entry 自行按顺序关停资源。
|
|
201
|
+
await new Promise((resolve, reject) => {
|
|
202
|
+
const interrupt = () => child.kill('SIGINT');
|
|
203
|
+
const terminate = () => child.kill('SIGTERM');
|
|
204
|
+
const cleanup = () => {
|
|
205
|
+
process.removeListener('SIGINT', interrupt);
|
|
206
|
+
process.removeListener('SIGTERM', terminate);
|
|
207
|
+
};
|
|
208
|
+
process.on('SIGINT', interrupt);
|
|
209
|
+
process.on('SIGTERM', terminate);
|
|
210
|
+
child.once('error', error => {
|
|
211
|
+
cleanup();
|
|
212
|
+
reject(error);
|
|
213
|
+
});
|
|
214
|
+
child.once('exit', code => {
|
|
215
|
+
cleanup();
|
|
216
|
+
if (code === 0) {
|
|
217
|
+
resolve();
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
reject(new Error(`开发服务退出,退出码: ${code ?? 'unknown'}`));
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-entry.d.ts","sourceRoot":"","sources":["../src/dev-entry.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
|
|
5
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const server_1 = require("@ynight/framework/server");
|
|
8
|
+
const generate_types_1 = require("./generate-types");
|
|
9
|
+
const webpack_build_1 = require("./webpack-build");
|
|
10
|
+
const project_config_1 = require("./project-config");
|
|
11
|
+
const output_safety_1 = require("./output-safety");
|
|
12
|
+
const dev_proxy_1 = require("./dev-proxy");
|
|
13
|
+
const utils_1 = require("./utils");
|
|
14
|
+
const dev_watcher_1 = require("./dev-watcher");
|
|
15
|
+
/**
|
|
16
|
+
* 从 `process.argv` 中读取 `--name value` 形式的命令行参数。
|
|
17
|
+
*
|
|
18
|
+
* 相比 `process.argv.slice` 解析,这种按名查找更鲁棒,能正确跳过无关参数。
|
|
19
|
+
* @returns 找到时返回参数值(字符串),否则返回 `undefined`。
|
|
20
|
+
*/
|
|
21
|
+
function argument(name) {
|
|
22
|
+
const index = process.argv.indexOf(name);
|
|
23
|
+
return index === -1 ? undefined : process.argv[index + 1];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 清空 Node `require.cache` 中所有属于业务工程的缓存模块。
|
|
27
|
+
*
|
|
28
|
+
* 设计要点:
|
|
29
|
+
* - 仅清除项目根目录下且不在 `node_modules` 中的缓存文件;
|
|
30
|
+
* - 这样在重启循环里 `require(entry)` 就能加载到最新的编译产物;
|
|
31
|
+
* - 注意:开发期引入业务模块应经过编译目录(`.elpis/dev/app`),原文件缓存由 webpack 处理。
|
|
32
|
+
*/
|
|
33
|
+
function clearProjectModuleCache(root) {
|
|
34
|
+
const prefix = node_path_1.default.resolve(root) + node_path_1.default.sep;
|
|
35
|
+
for (const file of Object.keys(require.cache)) {
|
|
36
|
+
if (file.startsWith(prefix) && !file.includes(node_path_1.default.sep + 'node_modules' + node_path_1.default.sep)) {
|
|
37
|
+
delete require.cache[file];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 开发子进程主入口:
|
|
43
|
+
* 1. 解析 CLI 参数(根目录、端口覆盖);
|
|
44
|
+
* 2. 准备生成目录(public / app);
|
|
45
|
+
* 3. 启动 koa + 前端 dev server + dev-proxy;
|
|
46
|
+
* 4. 启动文件监听循环,业务代码变更时热重启;
|
|
47
|
+
* 5. 接收 SIGINT / SIGTERM 时按顺序安全关停所有资源。
|
|
48
|
+
*/
|
|
49
|
+
async function main() {
|
|
50
|
+
// 使用 realpathSync 处理工程目录本身就是符号链接的情况。
|
|
51
|
+
const root = node_fs_1.default.realpathSync(node_path_1.default.resolve(argument('--root') ?? process.cwd()));
|
|
52
|
+
const port = argument('--port');
|
|
53
|
+
const webPort = argument('--web-port');
|
|
54
|
+
const overrides = {
|
|
55
|
+
...(port === undefined ? {} : { port: Number(port) }),
|
|
56
|
+
...(webPort === undefined ? {} : { webPort: Number(webPort) }),
|
|
57
|
+
};
|
|
58
|
+
// 通过白名单环境变量传递端口覆盖,让框架 onReady 等也能读取到 CLI 优先级。
|
|
59
|
+
if (overrides.port !== undefined) {
|
|
60
|
+
process.env.ELPIS_PORT = String(overrides.port);
|
|
61
|
+
}
|
|
62
|
+
if (overrides.webPort !== undefined) {
|
|
63
|
+
process.env.ELPIS_WEB_DEV_PORT = String(overrides.webPort);
|
|
64
|
+
}
|
|
65
|
+
// 公共生成目录与编译后应用目录;都位于 `.elpis/dev/`,便于一次性清理。
|
|
66
|
+
const publicRoot = (0, output_safety_1.safeGeneratedPath)(root, '.elpis/dev/public');
|
|
67
|
+
const appRoot = (0, output_safety_1.safeGeneratedPath)(root, '.elpis/dev/app');
|
|
68
|
+
// 开发服务与生产构建保持相同的 public 语义:
|
|
69
|
+
// 仅清理 CLI 自有的生成目录,再叠加业务静态文件,最后由 webpack 写入页面模板与 HMR 资源。
|
|
70
|
+
node_fs_1.default.rmSync(publicRoot, { recursive: true, force: true });
|
|
71
|
+
node_fs_1.default.mkdirSync(publicRoot, { recursive: true });
|
|
72
|
+
(0, utils_1.copyDirectory)(node_path_1.default.resolve(root, 'public'), publicRoot);
|
|
73
|
+
let config = (0, project_config_1.loadEffectiveConfig)(root, 'development', overrides);
|
|
74
|
+
// 进程级状态:以下变量在重启 / 停止流程中共享,需要按闭包访问。
|
|
75
|
+
let managed;
|
|
76
|
+
let closeProxy;
|
|
77
|
+
let frontend;
|
|
78
|
+
let compiling;
|
|
79
|
+
let stopping = false;
|
|
80
|
+
let stopPromise;
|
|
81
|
+
let restarting;
|
|
82
|
+
let watcher;
|
|
83
|
+
let changeTimer;
|
|
84
|
+
const pendingChanges = new Set();
|
|
85
|
+
/**
|
|
86
|
+
* 编译业务工程到 staging 目录,返回 staging 路径。
|
|
87
|
+
*
|
|
88
|
+
* 使用一次性目录(带时间戳 + pid)实现"原子替换",避免热重启期间读写冲突。
|
|
89
|
+
* 同时禁用 incremental 与 emitOnError,确保每次都得到完整且无错误的产物。
|
|
90
|
+
*/
|
|
91
|
+
const compileServer = async () => {
|
|
92
|
+
const staging = (0, output_safety_1.safeGeneratedPath)(root, '.elpis/dev/compilations/' + Date.now() + '-' + process.pid + '/app');
|
|
93
|
+
node_fs_1.default.mkdirSync(staging, { recursive: true });
|
|
94
|
+
await new Promise((resolve, reject) => {
|
|
95
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [
|
|
96
|
+
(0, utils_1.resolveTsc)(),
|
|
97
|
+
'-p',
|
|
98
|
+
node_path_1.default.resolve(root, 'tsconfig.server.json'),
|
|
99
|
+
'--outDir',
|
|
100
|
+
staging,
|
|
101
|
+
'--rootDir',
|
|
102
|
+
root,
|
|
103
|
+
'--incremental',
|
|
104
|
+
'false',
|
|
105
|
+
'--noEmitOnError',
|
|
106
|
+
], { cwd: root, stdio: 'inherit' });
|
|
107
|
+
compiling = child;
|
|
108
|
+
child.once('error', reject);
|
|
109
|
+
child.once('exit', code => {
|
|
110
|
+
compiling = undefined;
|
|
111
|
+
if (code === 0) {
|
|
112
|
+
resolve();
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
reject(new Error('TypeScript 编译失败,退出码: ' + (code ?? 'unknown')));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
return staging;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* 根据最新配置创建 Koa `ManagedApplication`。
|
|
123
|
+
*
|
|
124
|
+
* 处理流程:
|
|
125
|
+
* - 把 TS 源码入口路径映射到编译产物(`.ts` → `.js`);
|
|
126
|
+
* - 清空 require 缓存后 `require` 编译产物;
|
|
127
|
+
* - 调用框架 `createApplication` 构造应用实例。
|
|
128
|
+
*/
|
|
129
|
+
const create = async (next) => {
|
|
130
|
+
const sourceEntry = (0, project_config_1.resolveServerEntry)(root, next);
|
|
131
|
+
const entry = node_path_1.default.join(appRoot, node_path_1.default.relative(root, sourceEntry).replace(/\.(?:tsx?|mts|cts)$/, '.js'));
|
|
132
|
+
if (!node_fs_1.default.existsSync(entry)) {
|
|
133
|
+
throw new Error('编译后的 server.entry 不存在: ' + entry);
|
|
134
|
+
}
|
|
135
|
+
clearProjectModuleCache(appRoot);
|
|
136
|
+
const loaded = require(entry);
|
|
137
|
+
const created = await (0, server_1.createApplication)({
|
|
138
|
+
rootDir: root,
|
|
139
|
+
sourceRoot: root,
|
|
140
|
+
moduleRoot: appRoot,
|
|
141
|
+
moduleExtension: '.js',
|
|
142
|
+
mode: 'development',
|
|
143
|
+
config: (0, project_config_1.configForCompiledModules)(next),
|
|
144
|
+
server: loaded.default ?? loaded,
|
|
145
|
+
publicRoot,
|
|
146
|
+
});
|
|
147
|
+
return created;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* 把一次"重启"完整执行:编译 → 替换 → 启动新实例 → 关停旧实例。
|
|
151
|
+
*
|
|
152
|
+
* 关键策略:
|
|
153
|
+
* - 备份旧 `app` 目录,便于新实例失败时回滚;
|
|
154
|
+
* - 必须先让新实例成功 `listen`,再关闭旧实例,期间请求仍由旧实例处理;
|
|
155
|
+
* - 新实例启动期间代理中间件保持不变;
|
|
156
|
+
* - 失败时把失败目录保留,并把旧版本恢复回 appRoot。
|
|
157
|
+
*
|
|
158
|
+
* @param next 最新的 `ElpisConfig`。
|
|
159
|
+
* @param restartFrontend 是否需要重启前端(仅在配置文件变化时为 true)。
|
|
160
|
+
*/
|
|
161
|
+
const apply = async (next, restartFrontend) => {
|
|
162
|
+
(0, generate_types_1.generateTypes)(root);
|
|
163
|
+
const staging = await compileServer();
|
|
164
|
+
// 编译期间若收到停止信号,则直接放弃此次重启。
|
|
165
|
+
if (stopping) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
(0, output_safety_1.safeGeneratedPath)(root, '.elpis/dev/app');
|
|
169
|
+
// 备份路径:保留上一次成功版本,新版本失败时可回滚。
|
|
170
|
+
const backup = (0, output_safety_1.safeGeneratedPath)(root, '.elpis/dev/previous/' + Date.now() + '-' + process.pid + '/app');
|
|
171
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(backup), { recursive: true });
|
|
172
|
+
if (node_fs_1.default.existsSync(appRoot)) {
|
|
173
|
+
node_fs_1.default.renameSync(appRoot, backup);
|
|
174
|
+
}
|
|
175
|
+
node_fs_1.default.renameSync(staging, appRoot);
|
|
176
|
+
let candidate;
|
|
177
|
+
let switched = false;
|
|
178
|
+
let frontendChanged = false;
|
|
179
|
+
try {
|
|
180
|
+
// 仅在首次启动或配置变化时重启前端,业务代码变更可热更新。
|
|
181
|
+
if (!frontend || restartFrontend) {
|
|
182
|
+
frontendChanged = true;
|
|
183
|
+
await frontend?.close();
|
|
184
|
+
frontend = (0, webpack_build_1.startDevFrontend)((0, webpack_build_1.createFrontendOptions)({ root }, 'development', publicRoot, next));
|
|
185
|
+
await frontend.result;
|
|
186
|
+
}
|
|
187
|
+
// 初次启动必须先交付真实 HTML,框架初始化会验证模板;
|
|
188
|
+
// 编译、模型验证和 onReady 成功前,旧后端继续监听并处理请求。
|
|
189
|
+
candidate = await create(next);
|
|
190
|
+
if (stopping) {
|
|
191
|
+
await candidate.close();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
closeProxy?.();
|
|
195
|
+
await managed?.close();
|
|
196
|
+
switched = true;
|
|
197
|
+
const candidateCloseProxy = (0, dev_proxy_1.installDevProxy)(candidate, next.dev?.webPort ?? 8000);
|
|
198
|
+
try {
|
|
199
|
+
await candidate.listen(next.server);
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
candidateCloseProxy();
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
managed = candidate;
|
|
206
|
+
closeProxy = candidateCloseProxy;
|
|
207
|
+
config = next;
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
await candidate?.close();
|
|
211
|
+
// 把失败目录单独保留供诊断,恢复旧模块目录后再恢复必要的监听。
|
|
212
|
+
const failed = (0, output_safety_1.safeGeneratedPath)(root, '.elpis/dev/failed/' + Date.now() + '-' + process.pid + '/app');
|
|
213
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(failed), { recursive: true });
|
|
214
|
+
node_fs_1.default.renameSync(appRoot, failed);
|
|
215
|
+
if (node_fs_1.default.existsSync(backup)) {
|
|
216
|
+
node_fs_1.default.renameSync(backup, appRoot);
|
|
217
|
+
}
|
|
218
|
+
// 前端只在被本次重启影响时才需要回滚,避免无谓重启。
|
|
219
|
+
if (frontendChanged && managed && !stopping) {
|
|
220
|
+
await frontend?.close();
|
|
221
|
+
frontend = (0, webpack_build_1.startDevFrontend)((0, webpack_build_1.createFrontendOptions)({ root }, 'development', publicRoot, config));
|
|
222
|
+
await frontend.result;
|
|
223
|
+
}
|
|
224
|
+
// 后端也需要回滚到上一个 working 版本。
|
|
225
|
+
if (switched && managed && !stopping) {
|
|
226
|
+
managed = await create(config);
|
|
227
|
+
closeProxy = (0, dev_proxy_1.installDevProxy)(managed, config.dev?.webPort ?? 8000);
|
|
228
|
+
await managed.listen(config.server);
|
|
229
|
+
}
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* 幂等地停止整个开发服务。
|
|
235
|
+
*
|
|
236
|
+
* 顺序:
|
|
237
|
+
* 1. 停掉定时器、tsc 子进程;
|
|
238
|
+
* 2. 等待上一次重启结束;
|
|
239
|
+
* 3. 先关 HMR,再关 dev-proxy,最后关后端;
|
|
240
|
+
* 倒序是为了避免 SSE 长连接阻塞 server.close。
|
|
241
|
+
*/
|
|
242
|
+
const stop = () => {
|
|
243
|
+
if (stopPromise) {
|
|
244
|
+
return stopPromise;
|
|
245
|
+
}
|
|
246
|
+
stopping = true;
|
|
247
|
+
if (changeTimer) {
|
|
248
|
+
clearTimeout(changeTimer);
|
|
249
|
+
}
|
|
250
|
+
// 终止正在运行的 tsc 子进程,否则它会一直占用文件句柄。
|
|
251
|
+
compiling?.kill('SIGTERM');
|
|
252
|
+
stopPromise = (async () => {
|
|
253
|
+
await restarting?.catch(() => undefined);
|
|
254
|
+
await watcher?.close();
|
|
255
|
+
// 先结束 HMR,再释放同源转发和后端监听,避免 SSE 阻塞 close。
|
|
256
|
+
await frontend?.close();
|
|
257
|
+
closeProxy?.();
|
|
258
|
+
await managed?.close();
|
|
259
|
+
})();
|
|
260
|
+
return stopPromise;
|
|
261
|
+
};
|
|
262
|
+
// SIGINT / SIGTERM 触发统一关停流程,失败时仅记录错误并标记非零退出码。
|
|
263
|
+
const shutdown = () => {
|
|
264
|
+
void stop().catch(error => {
|
|
265
|
+
console.error(error);
|
|
266
|
+
process.exitCode = 1;
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
process.on('SIGINT', shutdown);
|
|
270
|
+
process.on('SIGTERM', shutdown);
|
|
271
|
+
try {
|
|
272
|
+
// 首次启动:不需要重启前端(没有旧实例)。
|
|
273
|
+
restarting = apply(config, false);
|
|
274
|
+
await restarting;
|
|
275
|
+
restarting = undefined;
|
|
276
|
+
if (stopping) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
console.log('Elpis 开发服务已就绪: http://' +
|
|
280
|
+
config.server?.host +
|
|
281
|
+
':' +
|
|
282
|
+
config.server?.port +
|
|
283
|
+
'(前端 HMR 端口 ' +
|
|
284
|
+
config.dev?.webPort +
|
|
285
|
+
',同源 /__webpack_hmr)');
|
|
286
|
+
const restart = async () => {
|
|
287
|
+
if (stopping || restarting) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
restarting = (async () => {
|
|
291
|
+
while (pendingChanges.size > 0 && !stopping) {
|
|
292
|
+
const changes = [...pendingChanges];
|
|
293
|
+
pendingChanges.clear();
|
|
294
|
+
const configChanged = changes.some(file => ['elpis.config.ts', 'elpis.config.js', 'elpis.config.cjs'].includes(node_path_1.default.relative(root, file)));
|
|
295
|
+
try {
|
|
296
|
+
clearProjectModuleCache(root);
|
|
297
|
+
const next = (0, project_config_1.loadEffectiveConfig)(root, 'development', overrides);
|
|
298
|
+
await apply(next, configChanged);
|
|
299
|
+
if (configChanged) {
|
|
300
|
+
await watcher?.refresh(next);
|
|
301
|
+
}
|
|
302
|
+
if (!stopping) {
|
|
303
|
+
console.log('业务服务已根据源码或配置变更重启');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
console.error('业务服务重启失败,保留上一可用版本: ' +
|
|
308
|
+
(error instanceof Error ? error.message : error));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
})().finally(() => {
|
|
312
|
+
restarting = undefined;
|
|
313
|
+
if (pendingChanges.size > 0 && !stopping) {
|
|
314
|
+
scheduleRestart();
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
await restarting;
|
|
318
|
+
};
|
|
319
|
+
const scheduleRestart = () => {
|
|
320
|
+
if (stopping) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (changeTimer) {
|
|
324
|
+
clearTimeout(changeTimer);
|
|
325
|
+
}
|
|
326
|
+
changeTimer = setTimeout(() => {
|
|
327
|
+
changeTimer = undefined;
|
|
328
|
+
void restart();
|
|
329
|
+
}, 200);
|
|
330
|
+
};
|
|
331
|
+
const handleChange = (event) => {
|
|
332
|
+
pendingChanges.add(event.path);
|
|
333
|
+
scheduleRestart();
|
|
334
|
+
};
|
|
335
|
+
watcher = await (0, dev_watcher_1.watchProject)(root, config, handleChange, error => console.error('项目文件监听失败: ' + (error instanceof Error ? error.message : error)));
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
restarting = undefined;
|
|
339
|
+
await stop();
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// 顶层错误处理:打印友好提示并设置非零退出码。
|
|
344
|
+
main().catch(error => {
|
|
345
|
+
console.error('开发服务启动失败: ' + (error instanceof Error ? error.message : error));
|
|
346
|
+
process.exitCode = 1;
|
|
347
|
+
});
|
|
348
|
+
//# sourceMappingURL=dev-entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-entry.js","sourceRoot":"","sources":["../src/dev-entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ManagedApplication } from '@ynight/framework/server';
|
|
2
|
+
/**
|
|
3
|
+
* 在开发模式下,仅把前端 HMR 与开发产物相关的请求代理到 Webpack Dev Server,
|
|
4
|
+
* 其余请求(业务 API、页面 HTML、静态资源中需鉴权的部分)继续交给 Koa 框架处理。
|
|
5
|
+
*
|
|
6
|
+
* 路由策略(仅放行下列请求方法与路径前缀):
|
|
7
|
+
* - 仅处理 `GET` / `HEAD`;
|
|
8
|
+
* - `/__webpack_hmr` webpack-hot-middleware 长连接(SSE);
|
|
9
|
+
* - `/dist/dev/...` 开发模式下的 js/css/asset 产物;
|
|
10
|
+
* - `/assets/...` 静态资源(虽然通常会经过框架校验,但开发期以原始
|
|
11
|
+
* 文件形式由 webpack-dev-middleware 接管更高效)。
|
|
12
|
+
*
|
|
13
|
+
* 设计动机:保持单一域名,便于在登录态 Cookie / CSRF 等安全约束下做 HMR;
|
|
14
|
+
* 仅放行最关键的几个前缀,避免暴露完整 webpack-dev-server。
|
|
15
|
+
*
|
|
16
|
+
* @param managed 当前被管理的 Koa 应用(带 `app.middleware`)。
|
|
17
|
+
* @param port Webpack Dev Server 监听端口(来自 `config.dev.webPort`)。
|
|
18
|
+
* @returns 卸载代理中间件的清理函数;通常在切换前后端实例时调用。
|
|
19
|
+
*/
|
|
20
|
+
export declare function installDevProxy(managed: ManagedApplication, port: number): () => void;
|
|
21
|
+
//# sourceMappingURL=dev-proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-proxy.d.ts","sourceRoot":"","sources":["../src/dev-proxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAkFrF"}
|