@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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ElpisConfig } from '@ynight/framework/server';
|
|
2
|
+
/**
|
|
3
|
+
* 在项目根目录下解析一个"生成路径"并校验其安全性。
|
|
4
|
+
*
|
|
5
|
+
* 防止以下风险:
|
|
6
|
+
* 1. 路径逃逸项目根目录(例如相对路径中含 `..`)。
|
|
7
|
+
* 2. 经过悬空或指向源码目录外的符号链接(避免覆盖源码、注入替换等)。
|
|
8
|
+
*
|
|
9
|
+
* 注意:必须逐级检查路径上的每一段,不能只检查最终目标,否则攻击者可通过
|
|
10
|
+
* 中间符号链接绕过 `realpathSync` 校验。
|
|
11
|
+
*
|
|
12
|
+
* @param root 业务工程根目录。
|
|
13
|
+
* @param relativePath 相对于根目录的子路径(生成类目录通常以 `.elpis/` 开头)。
|
|
14
|
+
* @returns 经 `realpathSync` 处理过的目标绝对路径。
|
|
15
|
+
* @throws 当路径等于根目录、逃逸根目录或经过符号链接时抛出。
|
|
16
|
+
*/
|
|
17
|
+
export declare function safeGeneratedPath(root: string, relativePath: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* 计算并校验生产产物的输出根目录。
|
|
20
|
+
*
|
|
21
|
+
* 保护目标:
|
|
22
|
+
* - 不允许 `build.outDir` 落在源码、依赖、`git`、`.elpis` 等关键目录内;
|
|
23
|
+
* - 也不允许覆盖关键目录的子目录或反过来包含关键目录。
|
|
24
|
+
* - 默认输出目录为 `dist`,可通过 `config.build.outDir` 覆盖。
|
|
25
|
+
*
|
|
26
|
+
* @throws 当输出目录命中受保护路径时抛出,避免误删源码。
|
|
27
|
+
*/
|
|
28
|
+
export declare function safeOutputRoot(root: string, config: ElpisConfig): string;
|
|
29
|
+
/**
|
|
30
|
+
* 原子地将暂存目录替换为正式输出目录。
|
|
31
|
+
*
|
|
32
|
+
* 实现策略:
|
|
33
|
+
* 1. 先把已有的输出目录改名为 `backup`(用于回滚);
|
|
34
|
+
* 2. 再把暂存目录改名为正式输出(POSIX `rename` 是原子的);
|
|
35
|
+
* 3. 写事务记录文件,状态包含 `prepared` / `complete` / `rolled-back`。
|
|
36
|
+
*
|
|
37
|
+
* 失败兜底:
|
|
38
|
+
* - 若 `rename(staging → output)` 失败且 backup 存在,立即把 backup 改回 output;
|
|
39
|
+
* - 永远不删除 backup,便于事后诊断与回滚。
|
|
40
|
+
*
|
|
41
|
+
* @param root 业务工程根目录。
|
|
42
|
+
* @param staging 暂存目录(一般位于 `.elpis/build/{buildId}/dist`)。
|
|
43
|
+
* @param output 最终输出目录(已通过 `safeOutputRoot` 校验)。
|
|
44
|
+
* @param buildId 本次构建的唯一标识,用于命名 backup / 事务记录。
|
|
45
|
+
*/
|
|
46
|
+
export declare function replaceDirectoryAtomically(root: string, staging: string, output: string, buildId: string): void;
|
|
47
|
+
//# sourceMappingURL=output-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-safety.d.ts","sourceRoot":"","sources":["../src/output-safety.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAgB5D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAkB5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAkCxE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,IAAI,CA6BN"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safeGeneratedPath = safeGeneratedPath;
|
|
4
|
+
exports.safeOutputRoot = safeOutputRoot;
|
|
5
|
+
exports.replaceDirectoryAtomically = replaceDirectoryAtomically;
|
|
6
|
+
const tslib_1 = require("tslib");
|
|
7
|
+
const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
9
|
+
/**
|
|
10
|
+
* 判断 `target` 是否位于 `root` 内(不解析符号链接)。
|
|
11
|
+
*
|
|
12
|
+
* 等价于 `target` 解析后路径不以 `..` 起始,也不是绝对路径。
|
|
13
|
+
* 同时兼容 `target === root` 的情况(视为"在内部")。
|
|
14
|
+
*/
|
|
15
|
+
function within(root, target) {
|
|
16
|
+
const relative = node_path_1.default.relative(root, target);
|
|
17
|
+
return (relative === '' ||
|
|
18
|
+
(relative !== '..' && !relative.startsWith(`..${node_path_1.default.sep}`) && !node_path_1.default.isAbsolute(relative)));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 在项目根目录下解析一个"生成路径"并校验其安全性。
|
|
22
|
+
*
|
|
23
|
+
* 防止以下风险:
|
|
24
|
+
* 1. 路径逃逸项目根目录(例如相对路径中含 `..`)。
|
|
25
|
+
* 2. 经过悬空或指向源码目录外的符号链接(避免覆盖源码、注入替换等)。
|
|
26
|
+
*
|
|
27
|
+
* 注意:必须逐级检查路径上的每一段,不能只检查最终目标,否则攻击者可通过
|
|
28
|
+
* 中间符号链接绕过 `realpathSync` 校验。
|
|
29
|
+
*
|
|
30
|
+
* @param root 业务工程根目录。
|
|
31
|
+
* @param relativePath 相对于根目录的子路径(生成类目录通常以 `.elpis/` 开头)。
|
|
32
|
+
* @returns 经 `realpathSync` 处理过的目标绝对路径。
|
|
33
|
+
* @throws 当路径等于根目录、逃逸根目录或经过符号链接时抛出。
|
|
34
|
+
*/
|
|
35
|
+
function safeGeneratedPath(root, relativePath) {
|
|
36
|
+
// 解析真实路径,处理源码工程本身就是符号链接的情况。
|
|
37
|
+
const rootReal = node_fs_1.default.realpathSync(root);
|
|
38
|
+
const target = node_path_1.default.resolve(rootReal, relativePath);
|
|
39
|
+
if (target === rootReal || !within(rootReal, target)) {
|
|
40
|
+
throw new Error(`生成路径必须位于项目根目录内: ${target}`);
|
|
41
|
+
}
|
|
42
|
+
let current = rootReal;
|
|
43
|
+
// 逐段检查:哪怕最终路径合法,中途遇到符号链接也要拒绝。
|
|
44
|
+
for (const part of node_path_1.default.relative(rootReal, target).split(node_path_1.default.sep)) {
|
|
45
|
+
current = node_path_1.default.join(current, part);
|
|
46
|
+
// `throwIfNoEntry: false` 让路径不存在时也不抛错(首次创建场景正常)。
|
|
47
|
+
const stat = node_fs_1.default.lstatSync(current, { throwIfNoEntry: false });
|
|
48
|
+
if (stat?.isSymbolicLink()) {
|
|
49
|
+
throw new Error(`生成路径不能经过符号链接: ${current}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return target;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 计算并校验生产产物的输出根目录。
|
|
56
|
+
*
|
|
57
|
+
* 保护目标:
|
|
58
|
+
* - 不允许 `build.outDir` 落在源码、依赖、`git`、`.elpis` 等关键目录内;
|
|
59
|
+
* - 也不允许覆盖关键目录的子目录或反过来包含关键目录。
|
|
60
|
+
* - 默认输出目录为 `dist`,可通过 `config.build.outDir` 覆盖。
|
|
61
|
+
*
|
|
62
|
+
* @throws 当输出目录命中受保护路径时抛出,避免误删源码。
|
|
63
|
+
*/
|
|
64
|
+
function safeOutputRoot(root, config) {
|
|
65
|
+
root = node_fs_1.default.realpathSync(root);
|
|
66
|
+
const output = safeGeneratedPath(root, config.build?.outDir ?? 'dist');
|
|
67
|
+
// 受保护的目录集合:覆盖源码、依赖、构建产物目录都视作不安全。
|
|
68
|
+
const protectedPaths = [
|
|
69
|
+
'node_modules',
|
|
70
|
+
'.git',
|
|
71
|
+
'.elpis',
|
|
72
|
+
'server',
|
|
73
|
+
'model',
|
|
74
|
+
'client',
|
|
75
|
+
'shared',
|
|
76
|
+
'public',
|
|
77
|
+
'types',
|
|
78
|
+
'src',
|
|
79
|
+
'config',
|
|
80
|
+
config.server?.businessDir ?? 'server',
|
|
81
|
+
config.model?.dir ?? 'model',
|
|
82
|
+
config.server?.entry ?? 'server/app.ts',
|
|
83
|
+
// 客户端扩展入口若声明了,也要纳入保护。
|
|
84
|
+
...(config.client?.extensions ? [config.client.extensions] : []),
|
|
85
|
+
].map(folder => node_path_1.default.resolve(root, folder));
|
|
86
|
+
if (
|
|
87
|
+
// 输出路径中含 `node_modules` 或 `.git` 段必然不安全。
|
|
88
|
+
node_path_1.default
|
|
89
|
+
.relative(node_fs_1.default.realpathSync(root), output)
|
|
90
|
+
.split(node_path_1.default.sep)
|
|
91
|
+
.some(part => ['node_modules', '.git'].includes(part)) ||
|
|
92
|
+
// 互为父子关系都禁止:避免输出目录污染源码,也避免源码目录吞掉构建产物。
|
|
93
|
+
protectedPaths.some(folder => within(folder, output) || within(output, folder))) {
|
|
94
|
+
throw new Error(`build.outDir 不能覆盖源码、依赖或生成目录: ${output}`);
|
|
95
|
+
}
|
|
96
|
+
return output;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 原子地将暂存目录替换为正式输出目录。
|
|
100
|
+
*
|
|
101
|
+
* 实现策略:
|
|
102
|
+
* 1. 先把已有的输出目录改名为 `backup`(用于回滚);
|
|
103
|
+
* 2. 再把暂存目录改名为正式输出(POSIX `rename` 是原子的);
|
|
104
|
+
* 3. 写事务记录文件,状态包含 `prepared` / `complete` / `rolled-back`。
|
|
105
|
+
*
|
|
106
|
+
* 失败兜底:
|
|
107
|
+
* - 若 `rename(staging → output)` 失败且 backup 存在,立即把 backup 改回 output;
|
|
108
|
+
* - 永远不删除 backup,便于事后诊断与回滚。
|
|
109
|
+
*
|
|
110
|
+
* @param root 业务工程根目录。
|
|
111
|
+
* @param staging 暂存目录(一般位于 `.elpis/build/{buildId}/dist`)。
|
|
112
|
+
* @param output 最终输出目录(已通过 `safeOutputRoot` 校验)。
|
|
113
|
+
* @param buildId 本次构建的唯一标识,用于命名 backup / 事务记录。
|
|
114
|
+
*/
|
|
115
|
+
function replaceDirectoryAtomically(root, staging, output, buildId) {
|
|
116
|
+
// 路径校验:确保 staging / output / backup 都位于项目内。
|
|
117
|
+
safeGeneratedPath(root, node_path_1.default.relative(root, staging));
|
|
118
|
+
safeGeneratedPath(root, node_path_1.default.relative(root, output));
|
|
119
|
+
// backup 目录用于回滚;事务记录 JSON 用于审计与排障。
|
|
120
|
+
const backup = safeGeneratedPath(root, `.elpis/backup/${buildId}`);
|
|
121
|
+
const record = safeGeneratedPath(root, `.elpis/transactions/${buildId}.json`);
|
|
122
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(backup), { recursive: true });
|
|
123
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(record), { recursive: true });
|
|
124
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(output), { recursive: true });
|
|
125
|
+
// 事务记录写入函数:始终带 `status` 字段,便于后续脚本识别状态。
|
|
126
|
+
const writeRecord = (status) => node_fs_1.default.writeFileSync(record, `${JSON.stringify({ staging, output, backup, status }, null, 2)}\n`);
|
|
127
|
+
writeRecord('prepared');
|
|
128
|
+
// 仅在目标已存在时才需要 backup;首次构建无需多一次 rename。
|
|
129
|
+
if (node_fs_1.default.existsSync(output)) {
|
|
130
|
+
node_fs_1.default.renameSync(output, backup);
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
node_fs_1.default.renameSync(staging, output);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// 关键失败:把 backup 还原回 output,绝不删除 backup。
|
|
137
|
+
if (node_fs_1.default.existsSync(backup) && !node_fs_1.default.existsSync(output)) {
|
|
138
|
+
node_fs_1.default.renameSync(backup, output);
|
|
139
|
+
}
|
|
140
|
+
writeRecord('rolled-back');
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
writeRecord('complete');
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=output-safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-safety.js","sourceRoot":"","sources":["../src/output-safety.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ElpisConfig, ElpisMode } from '@ynight/framework/server';
|
|
2
|
+
/**
|
|
3
|
+
* 在项目根目录下解析并返回配置文件绝对路径。
|
|
4
|
+
*
|
|
5
|
+
* 优先匹配 `elpis.config.ts` / `.js` / `.cjs`;调用 `resolveProjectPath` 校验
|
|
6
|
+
* 路径安全性(不允许符号链接逃出根目录)。
|
|
7
|
+
*
|
|
8
|
+
* @throws 找不到任一候选文件或路径不安全时抛出。
|
|
9
|
+
*/
|
|
10
|
+
export declare function configFile(root: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* 加载并返回业务工程的 `ElpisConfig`。
|
|
13
|
+
*
|
|
14
|
+
* 处理流程:
|
|
15
|
+
* 1. 解析配置文件路径;若为 `.ts` 则注册 ts-node。
|
|
16
|
+
* 2. 清缓存(关键:配置变更后下次执行能拿到最新值)。
|
|
17
|
+
* 3. `require` 加载并解开 `default`。
|
|
18
|
+
* 4. 支持配置是函数:`function ({ mode }) => config`,便于按 dev/prod 区分配置。
|
|
19
|
+
*
|
|
20
|
+
* @throws 配置文件类型不是对象/工厂函数时抛出。
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadProjectConfig(root: string, mode: ElpisMode): ElpisConfig;
|
|
23
|
+
/**
|
|
24
|
+
* 将 CLI 命令行覆盖项合并到配置上。
|
|
25
|
+
*
|
|
26
|
+
* 仅当 `overrides.port` / `overrides.webPort` 实际传入时才覆盖,
|
|
27
|
+
* 这样配置文件本身的缺省值不会被 CLI 的 `undefined` 错误覆盖。
|
|
28
|
+
*/
|
|
29
|
+
export declare function mergeCliOverrides(config: ElpisConfig, overrides: {
|
|
30
|
+
port?: number;
|
|
31
|
+
webPort?: number;
|
|
32
|
+
}): ElpisConfig;
|
|
33
|
+
/**
|
|
34
|
+
* 加载"完整生效"的配置:依次合并 CLI 参数、配置文件、`resolveConfig` 默认值。
|
|
35
|
+
*
|
|
36
|
+
* 同时校验端口号范围(1-65535),避免无效端口拖到运行时才报错。
|
|
37
|
+
*
|
|
38
|
+
* @throws 端口不在合法范围内时抛出。
|
|
39
|
+
*/
|
|
40
|
+
export declare function loadEffectiveConfig(root: string, mode: ElpisMode, overrides?: {
|
|
41
|
+
port?: number;
|
|
42
|
+
webPort?: number;
|
|
43
|
+
}): ElpisConfig;
|
|
44
|
+
/**
|
|
45
|
+
* 解析并校验一个相对项目根目录的文件路径。
|
|
46
|
+
*
|
|
47
|
+
* 安全约束:
|
|
48
|
+
* - 必须落在项目根目录内(拒绝 `..` 逃逸);
|
|
49
|
+
* - 路径上每一段都不能是符号链接(防止链接到根目录外);
|
|
50
|
+
* - 最终目标必须是文件而非目录。
|
|
51
|
+
*
|
|
52
|
+
* @throws 不在根目录内、经过符号链接、目标不存在或不是文件时抛出。
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveProjectPath(root: string, configuredPath: string, label: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* 解析并校验服务端入口文件路径。
|
|
57
|
+
*
|
|
58
|
+
* 默认入口为 `./server/app.ts`(兼容 TypeScript 写法)。
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveServerEntry(root: string, config: ElpisConfig): string;
|
|
61
|
+
/**
|
|
62
|
+
* 解析并校验客户端扩展入口路径;若未配置则返回 `undefined`。
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveClientExtensions(root: string, config: ElpisConfig): string | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* 把配置中指向 TypeScript 源文件的路径切换为编译后的 JavaScript 路径。
|
|
67
|
+
*
|
|
68
|
+
* 应用场景:CLI 编译后将 `.ts` / `.mts` / `.cts` 重命名为 `.js`,但运行时配置仍然
|
|
69
|
+
* 引用源文件路径——这里在运行时统一替换,避免每个业务模块自己处理扩展名。
|
|
70
|
+
*/
|
|
71
|
+
export declare function configForCompiledModules(config: ElpisConfig): ElpisConfig;
|
|
72
|
+
//# sourceMappingURL=project-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAmCvE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU/C;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,WAAW,CAiB5E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,SAAS,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,WAAW,CAWb;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,EACf,SAAS,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,WAAW,CAgBb;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAqB9F;AAcD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAG5E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAK7F;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAezE"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configFile = configFile;
|
|
4
|
+
exports.loadProjectConfig = loadProjectConfig;
|
|
5
|
+
exports.mergeCliOverrides = mergeCliOverrides;
|
|
6
|
+
exports.loadEffectiveConfig = loadEffectiveConfig;
|
|
7
|
+
exports.resolveProjectPath = resolveProjectPath;
|
|
8
|
+
exports.resolveServerEntry = resolveServerEntry;
|
|
9
|
+
exports.resolveClientExtensions = resolveClientExtensions;
|
|
10
|
+
exports.configForCompiledModules = configForCompiledModules;
|
|
11
|
+
const tslib_1 = require("tslib");
|
|
12
|
+
const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
|
|
13
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
14
|
+
const server_1 = require("@ynight/framework/server");
|
|
15
|
+
/**
|
|
16
|
+
* 按优先级排列的配置文件名。
|
|
17
|
+
*
|
|
18
|
+
* 业务工程应使用 `.ts` 以享受类型提示;CLI 同时兼容 `.js` / `.cjs` 以便发布到 npm 的
|
|
19
|
+
* 预编译产物(如 `dist` 形式分发时)继续工作。
|
|
20
|
+
*/
|
|
21
|
+
const CONFIG_FILES = ['elpis.config.ts', 'elpis.config.js', 'elpis.config.cjs'];
|
|
22
|
+
/**
|
|
23
|
+
* 注册 ts-node 以加载 TypeScript 形式的配置文件。
|
|
24
|
+
*
|
|
25
|
+
* `transpileOnly: true` 跳过类型检查——CLI 入口需要的是"读取配置"而非"完整类型校验",
|
|
26
|
+
* 真正的类型校验在 `type-check` 子命令里通过 `tsc` 完成。
|
|
27
|
+
*
|
|
28
|
+
* @throws 当 `@ynight/cli` 没有把 ts-node 打包进来时给出可读的安装提示。
|
|
29
|
+
*/
|
|
30
|
+
function ensureTypeScriptRuntime(root) {
|
|
31
|
+
try {
|
|
32
|
+
// 优先使用业务工程独立的 `tsconfig.server.json`,否则回退到 `tsconfig.json`。
|
|
33
|
+
const projectConfig = node_fs_1.default.existsSync(node_path_1.default.resolve(root, 'tsconfig.server.json'))
|
|
34
|
+
? node_path_1.default.resolve(root, 'tsconfig.server.json')
|
|
35
|
+
: node_path_1.default.resolve(root, 'tsconfig.json');
|
|
36
|
+
require('ts-node').register({ transpileOnly: true, project: projectConfig });
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw new Error(`无法加载 TypeScript 配置,请确认 @ynight/cli 的 ts-node 依赖可用: ${error instanceof Error ? error.message : String(error)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 在项目根目录下解析并返回配置文件绝对路径。
|
|
44
|
+
*
|
|
45
|
+
* 优先匹配 `elpis.config.ts` / `.js` / `.cjs`;调用 `resolveProjectPath` 校验
|
|
46
|
+
* 路径安全性(不允许符号链接逃出根目录)。
|
|
47
|
+
*
|
|
48
|
+
* @throws 找不到任一候选文件或路径不安全时抛出。
|
|
49
|
+
*/
|
|
50
|
+
function configFile(root) {
|
|
51
|
+
const projectRoot = node_fs_1.default.realpathSync(root);
|
|
52
|
+
const file = CONFIG_FILES.map(name => node_path_1.default.resolve(projectRoot, name)).find(candidate => node_fs_1.default.existsSync(candidate));
|
|
53
|
+
if (!file) {
|
|
54
|
+
throw new Error(`未找到 elpis.config.ts、elpis.config.js 或 elpis.config.cjs: ${root}`);
|
|
55
|
+
}
|
|
56
|
+
resolveProjectPath(projectRoot, node_path_1.default.relative(projectRoot, file), '配置文件');
|
|
57
|
+
return file;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 提取 ES Module / CommonJS 混用场景下 `default` 导出。
|
|
61
|
+
*
|
|
62
|
+
* 业务工程可能写 `module.exports = { ... }`(CJS,无 default),
|
|
63
|
+
* 也可能写 `export default { ... }`(ESM,Node 看到 `{ default: ... }`)。
|
|
64
|
+
* 此函数统一暴露给上层的总是"实际配置对象"。
|
|
65
|
+
*/
|
|
66
|
+
function unwrapDefault(value) {
|
|
67
|
+
if (value && typeof value === 'object' && 'default' in value) {
|
|
68
|
+
return value.default;
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 加载并返回业务工程的 `ElpisConfig`。
|
|
74
|
+
*
|
|
75
|
+
* 处理流程:
|
|
76
|
+
* 1. 解析配置文件路径;若为 `.ts` 则注册 ts-node。
|
|
77
|
+
* 2. 清缓存(关键:配置变更后下次执行能拿到最新值)。
|
|
78
|
+
* 3. `require` 加载并解开 `default`。
|
|
79
|
+
* 4. 支持配置是函数:`function ({ mode }) => config`,便于按 dev/prod 区分配置。
|
|
80
|
+
*
|
|
81
|
+
* @throws 配置文件类型不是对象/工厂函数时抛出。
|
|
82
|
+
*/
|
|
83
|
+
function loadProjectConfig(root, mode) {
|
|
84
|
+
const file = configFile(root);
|
|
85
|
+
if (file.endsWith('.ts')) {
|
|
86
|
+
ensureTypeScriptRuntime(root);
|
|
87
|
+
}
|
|
88
|
+
// 清缓存确保每次调用都读到磁盘最新版本。
|
|
89
|
+
delete require.cache[require.resolve(file)];
|
|
90
|
+
const loaded = unwrapDefault(require(file));
|
|
91
|
+
// 支持配置工厂:传入当前运行模式,便于做差异化配置。
|
|
92
|
+
const value = typeof loaded === 'function'
|
|
93
|
+
? loaded({ mode })
|
|
94
|
+
: loaded;
|
|
95
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
96
|
+
throw new Error(`配置文件必须导出对象或配置工厂: ${file}`);
|
|
97
|
+
}
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 将 CLI 命令行覆盖项合并到配置上。
|
|
102
|
+
*
|
|
103
|
+
* 仅当 `overrides.port` / `overrides.webPort` 实际传入时才覆盖,
|
|
104
|
+
* 这样配置文件本身的缺省值不会被 CLI 的 `undefined` 错误覆盖。
|
|
105
|
+
*/
|
|
106
|
+
function mergeCliOverrides(config, overrides) {
|
|
107
|
+
return {
|
|
108
|
+
...config,
|
|
109
|
+
// 注意:只在端口显式传入时才追加,避免把 `server` 整个替换为空对象。
|
|
110
|
+
...(overrides.port === undefined
|
|
111
|
+
? {}
|
|
112
|
+
: { server: { ...(config.server ?? {}), port: overrides.port } }),
|
|
113
|
+
...(overrides.webPort === undefined
|
|
114
|
+
? {}
|
|
115
|
+
: { dev: { ...(config.dev ?? {}), webPort: overrides.webPort } }),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 加载"完整生效"的配置:依次合并 CLI 参数、配置文件、`resolveConfig` 默认值。
|
|
120
|
+
*
|
|
121
|
+
* 同时校验端口号范围(1-65535),避免无效端口拖到运行时才报错。
|
|
122
|
+
*
|
|
123
|
+
* @throws 端口不在合法范围内时抛出。
|
|
124
|
+
*/
|
|
125
|
+
function loadEffectiveConfig(root, mode, overrides = {}) {
|
|
126
|
+
const config = mergeCliOverrides(
|
|
127
|
+
// resolveConfig 负责把 ElpisConfig 协议下的默认值补齐(例如 dev.webPort 默认 8000)。
|
|
128
|
+
(0, server_1.resolveConfig)(root, mode, loadProjectConfig(root, mode)).config, overrides);
|
|
129
|
+
// 端口号范围统一校验:CLI 入口已经校验一次,这里再校验配置文件中的端口。
|
|
130
|
+
for (const [label, port] of [
|
|
131
|
+
['server.port', config.server?.port],
|
|
132
|
+
['dev.webPort', config.dev?.webPort],
|
|
133
|
+
]) {
|
|
134
|
+
if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) {
|
|
135
|
+
throw new Error(`${label} 必须是 1-65535 的整数`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return config;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 解析并校验一个相对项目根目录的文件路径。
|
|
142
|
+
*
|
|
143
|
+
* 安全约束:
|
|
144
|
+
* - 必须落在项目根目录内(拒绝 `..` 逃逸);
|
|
145
|
+
* - 路径上每一段都不能是符号链接(防止链接到根目录外);
|
|
146
|
+
* - 最终目标必须是文件而非目录。
|
|
147
|
+
*
|
|
148
|
+
* @throws 不在根目录内、经过符号链接、目标不存在或不是文件时抛出。
|
|
149
|
+
*/
|
|
150
|
+
function resolveProjectPath(root, configuredPath, label) {
|
|
151
|
+
const projectRoot = node_fs_1.default.realpathSync(root);
|
|
152
|
+
const resolved = node_path_1.default.resolve(projectRoot, configuredPath);
|
|
153
|
+
if (!within(projectRoot, resolved)) {
|
|
154
|
+
throw new Error(`${label} 必须位于项目根目录内: ${resolved}`);
|
|
155
|
+
}
|
|
156
|
+
if (!node_fs_1.default.existsSync(resolved)) {
|
|
157
|
+
throw new Error(`${label} 不存在: ${resolved}`);
|
|
158
|
+
}
|
|
159
|
+
// 逐段检测符号链接:哪怕最终目标在根目录内,中途被替换到外部也是不安全的。
|
|
160
|
+
let current = projectRoot;
|
|
161
|
+
for (const part of node_path_1.default.relative(projectRoot, resolved).split(node_path_1.default.sep)) {
|
|
162
|
+
current = node_path_1.default.join(current, part);
|
|
163
|
+
if (node_fs_1.default.lstatSync(current).isSymbolicLink()) {
|
|
164
|
+
throw new Error(`${label} 不能通过符号链接: ${current}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!node_fs_1.default.statSync(resolved).isFile()) {
|
|
168
|
+
throw new Error(`${label} 必须是文件: ${resolved}`);
|
|
169
|
+
}
|
|
170
|
+
return resolved;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 局部 `within` 实现,与 `output-safety.ts` 同名函数逻辑一致。
|
|
174
|
+
* 这里重复一份是为了让 `resolveProjectPath` 单文件自包含,便于单元测试。
|
|
175
|
+
*/
|
|
176
|
+
function within(root, target) {
|
|
177
|
+
const relative = node_path_1.default.relative(root, target);
|
|
178
|
+
return (relative === '' ||
|
|
179
|
+
(relative !== '..' && !relative.startsWith(`..${node_path_1.default.sep}`) && !node_path_1.default.isAbsolute(relative)));
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 解析并校验服务端入口文件路径。
|
|
183
|
+
*
|
|
184
|
+
* 默认入口为 `./server/app.ts`(兼容 TypeScript 写法)。
|
|
185
|
+
*/
|
|
186
|
+
function resolveServerEntry(root, config) {
|
|
187
|
+
const configured = config.server?.entry ?? './server/app.ts';
|
|
188
|
+
return resolveProjectPath(root, configured, 'server.entry');
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 解析并校验客户端扩展入口路径;若未配置则返回 `undefined`。
|
|
192
|
+
*/
|
|
193
|
+
function resolveClientExtensions(root, config) {
|
|
194
|
+
const configured = config.client?.extensions;
|
|
195
|
+
return configured === undefined
|
|
196
|
+
? undefined
|
|
197
|
+
: resolveProjectPath(root, configured, 'client.extensions');
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* 把配置中指向 TypeScript 源文件的路径切换为编译后的 JavaScript 路径。
|
|
201
|
+
*
|
|
202
|
+
* 应用场景:CLI 编译后将 `.ts` / `.mts` / `.cts` 重命名为 `.js`,但运行时配置仍然
|
|
203
|
+
* 引用源文件路径——这里在运行时统一替换,避免每个业务模块自己处理扩展名。
|
|
204
|
+
*/
|
|
205
|
+
function configForCompiledModules(config) {
|
|
206
|
+
const server = config.server ? { ...config.server } : undefined;
|
|
207
|
+
const model = config.model ? { ...config.model } : undefined;
|
|
208
|
+
if (server?.entry !== undefined) {
|
|
209
|
+
server.entry = server.entry.replace(/\.(?:tsx?|mts|cts)$/, '.js');
|
|
210
|
+
}
|
|
211
|
+
if (model?.controlContracts !== undefined) {
|
|
212
|
+
model.controlContracts = model.controlContracts.replace(/\.(?:tsx?|mts|cts)$/, '.js');
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
...config,
|
|
216
|
+
// 注意:仅在原本存在 server / model 时才覆盖,避免把整个对象设为 undefined。
|
|
217
|
+
...(server ? { server } : {}),
|
|
218
|
+
...(model ? { model } : {}),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=project-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-config.js","sourceRoot":"","sources":["../src/project-config.ts"],"names":[],"mappings":";;AA6CA,gCAUC;AA2BD,8CAiBC;AAQD,8CAcC;AASD,kDAoBC;AAYD,gDAqBC;AAmBD,gDAGC;AAKD,0DAKC;AAQD,4DAeC;;AA9OD,8DAAyB;AACzB,kEAA6B;AAE7B,qDAAyD;AAEzD;;;;;GAKG;AACH,MAAM,YAAY,GAAG,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;AAEhF;;;;;;;GAOG;AACH,SAAS,uBAAuB,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,4DAA4D;QAC5D,MAAM,aAAa,GAAG,iBAAE,CAAC,UAAU,CAAC,mBAAI,CAAC,OAAO,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;YAC7E,CAAC,CAAC,mBAAI,CAAC,OAAO,CAAC,IAAI,EAAE,sBAAsB,CAAC;YAC5C,CAAC,CAAC,mBAAI,CAAC,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QACxC,OAAO,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IAC/E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,sDACE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,MAAM,WAAW,GAAG,iBAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,mBAAI,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CACtF,iBAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CACzB,CAAC;IACF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,2DAA2D,IAAI,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,kBAAkB,CAAC,WAAW,EAAE,mBAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;QAC7D,OAAQ,KAA8B,CAAC,OAAO,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,iBAAiB,CAAC,IAAY,EAAE,IAAe;IAC7D,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,uBAAuB,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,sBAAsB;IACtB,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAY,CAAC;IACvD,4BAA4B;IAC5B,MAAM,KAAK,GACT,OAAO,MAAM,KAAK,UAAU;QAC1B,CAAC,CAAE,MAAoD,CAAC,EAAE,IAAI,EAAE,CAAC;QACjE,CAAC,CAAC,MAAM,CAAC;IACb,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,KAAoB,CAAC;AAC9B,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAC/B,MAAmB,EACnB,SAA8C;IAE9C,OAAO;QACL,GAAG,MAAM;QACT,yCAAyC;QACzC,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,SAAS;YAC9B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;QACnE,GAAG,CAAC,SAAS,CAAC,OAAO,KAAK,SAAS;YACjC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;KACpE,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,mBAAmB,CACjC,IAAY,EACZ,IAAe,EACf,YAAiD,EAAE;IAEnD,MAAM,MAAM,GAAG,iBAAiB;IAC9B,mEAAmE;IACnE,IAAA,sBAAa,EAAC,IAAI,EAAE,IAAI,EAAE,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,EAC/D,SAAS,CACV,CAAC;IACF,wCAAwC;IACxC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI;QAC1B,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC;QACpC,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC;KAC5B,EAAE,CAAC;QACX,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;YAChF,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,kBAAkB,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,kBAAkB,CAAC,IAAY,EAAE,cAAsB,EAAE,KAAa;IACpF,MAAM,WAAW,GAAG,iBAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,mBAAI,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,gBAAgB,QAAQ,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,QAAQ,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,uCAAuC;IACvC,IAAI,OAAO,GAAG,WAAW,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,mBAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,mBAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACxE,OAAO,GAAG,mBAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,iBAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,cAAc,OAAO,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IACD,IAAI,CAAC,iBAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,QAAQ,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,SAAS,MAAM,CAAC,IAAY,EAAE,MAAc;IAC1C,MAAM,QAAQ,GAAG,mBAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7C,OAAO,CACL,QAAQ,KAAK,EAAE;QACf,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,mBAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAC3F,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,IAAY,EAAE,MAAmB;IAClE,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,iBAAiB,CAAC;IAC7D,OAAO,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,SAAgB,uBAAuB,CAAC,IAAY,EAAE,MAAmB;IACvE,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;IAC7C,OAAO,UAAU,KAAK,SAAS;QAC7B,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,mBAAmB,CAAC,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,wBAAwB,CAAC,MAAmB;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,KAAK,EAAE,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC1C,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IACxF,CAAC;IACD,OAAO;QACL,GAAG,MAAM;QACT,qDAAqD;QACrD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5B,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI 命令的通用选项。
|
|
3
|
+
*
|
|
4
|
+
* - `root`:业务工程根目录,所有相对路径都会基于此目录解析;未显式传入时取 `process.cwd()`。
|
|
5
|
+
* - `port`:后端 Koa 服务监听端口,仅 `dev` 命令使用,会被注入到 `ELPIS_PORT` 环境变量并覆盖配置。
|
|
6
|
+
* - `webPort`:前端 Webpack 开发服务(带 HMR)的端口,仅 `dev` 命令使用,注入 `ELPIS_WEB_DEV_PORT`。
|
|
7
|
+
* - `check`:仅用于 `generate:types` 子命令,表示只做"类型新鲜度校验"——若生成的 `.d.ts` 与当前扫描结果不一致则报错,但不写回文件。常用于 CI 阻断过期产物。
|
|
8
|
+
*/
|
|
9
|
+
export interface CliOptions {
|
|
10
|
+
/** 业务工程根目录(绝对路径或相对于当前工作目录)。 */
|
|
11
|
+
root: string;
|
|
12
|
+
/** 后端 HTTP 服务端口,覆盖配置文件中的 `server.port`。 */
|
|
13
|
+
port?: number;
|
|
14
|
+
/** 前端开发服务(HMR)端口,覆盖 `dev.webPort`。 */
|
|
15
|
+
webPort?: number;
|
|
16
|
+
/** `generate:types` 子命令专用开关,开启后仅校验、不写文件。 */
|
|
17
|
+
check?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 解析后的命令行参数。
|
|
21
|
+
*
|
|
22
|
+
* `command` 为已识别的子命令名(dev / build / type-check / generate:types / help / version);
|
|
23
|
+
* `options` 携带该命令对应的具体选项。
|
|
24
|
+
*/
|
|
25
|
+
export interface ParsedArgs {
|
|
26
|
+
command: string;
|
|
27
|
+
options: CliOptions;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;CACrB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 递归遍历目录,收集所有 `.ts` / `.js` / `.cjs` 后缀的源文件(自动跳过 `.d.ts`)。
|
|
3
|
+
*
|
|
4
|
+
* 设计要点:
|
|
5
|
+
* - 不跟随符号链接(依赖 `fs.readdirSync` 的默认行为)。
|
|
6
|
+
* - 跳过 `.d.ts` 以免类型声明文件被当作业务模块纳入生成逻辑。
|
|
7
|
+
* - 字典序排序保证跨平台稳定的输出顺序,方便 `generate:types` 做差分校验。
|
|
8
|
+
*
|
|
9
|
+
* @param root 起始目录;若不存在则直接返回空数组。
|
|
10
|
+
* @returns 收集到的所有源文件的绝对路径。
|
|
11
|
+
*/
|
|
12
|
+
export declare function findFiles(root: string): string[];
|
|
13
|
+
/**
|
|
14
|
+
* 判断指定目录下是否存在匹配给定扩展名的源文件。
|
|
15
|
+
*
|
|
16
|
+
* 用于在执行 vue-tsc 等耗时工具前快速判断是否有必要调用:
|
|
17
|
+
* 零扩展项目的空目录是合法的,应当跳过校验。
|
|
18
|
+
*
|
|
19
|
+
* 跳过 `node_modules` 与 `dist`,避免扫描外部依赖或历史构建产物。
|
|
20
|
+
*
|
|
21
|
+
* @param root 待扫描目录;不存在时返回 `false`。
|
|
22
|
+
* @param extensions 需要匹配的扩展名列表(如 `['.vue']`)。
|
|
23
|
+
*/
|
|
24
|
+
export declare function hasSourceFiles(root: string, extensions: string[]): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 将 kebab-case / snake_case 命名转换为 camelCase。
|
|
27
|
+
*
|
|
28
|
+
* 例:`user-profile` → `userProfile`、`api_client` → `apiClient`。
|
|
29
|
+
*
|
|
30
|
+
* 用于把文件名映射为生成类型对象的属性名(参见 `generate-types.ts`)。
|
|
31
|
+
*/
|
|
32
|
+
export declare function camelCase(value: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* 同步执行一个 Node 脚本,失败时抛出错误(用于把控制权交回调用方)。
|
|
35
|
+
*
|
|
36
|
+
* 与 `runTsc` 的区别:此函数面向任意 Node 脚本;后者针对 `tsc` 提供了更友好的报错文案。
|
|
37
|
+
*
|
|
38
|
+
* @param script 脚本绝对路径(通常是 `require.resolve` 的产物)。
|
|
39
|
+
* @param args 透传给脚本的命令行参数。
|
|
40
|
+
* @param cwd 子进程工作目录。
|
|
41
|
+
*/
|
|
42
|
+
export declare function runNodeScript(script: string, args: string[], cwd: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* 解析当前 `node_modules/typescript` 中的 `tsc` 入口绝对路径。
|
|
45
|
+
*
|
|
46
|
+
* 不直接硬编码路径,使得业务工程即便使用了不同版本的 TypeScript 也能正确执行。
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveTsc(): string;
|
|
49
|
+
/**
|
|
50
|
+
* 解析 `vue-tsc` 入口绝对路径,与 `resolveTsc` 同一思路。
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveVueTsc(): string;
|
|
53
|
+
/**
|
|
54
|
+
* 调用 `tsc` 同步执行 TypeScript 编译/检查。
|
|
55
|
+
*
|
|
56
|
+
* 失败时抛出包含退出码的错误,便于上层统一报告;不接管 stdin/stdout,
|
|
57
|
+
* 让 `tsc` 直接打印到父进程终端,保留原始诊断信息。
|
|
58
|
+
*
|
|
59
|
+
* @param root 工作目录。
|
|
60
|
+
* @param config tsconfig 路径(绝对路径)。
|
|
61
|
+
* @param extraArgs 透传给 `tsc` 的额外参数,例如 `['--noEmit']` 或 `['--outDir', dir]`。
|
|
62
|
+
*/
|
|
63
|
+
export declare function runTsc(root: string, config: string, extraArgs?: string[]): void;
|
|
64
|
+
/**
|
|
65
|
+
* 调用 `vue-tsc` 同步执行 Vue SFC 的 TypeScript 检查。
|
|
66
|
+
*
|
|
67
|
+
* 通过 `--noEmit` 避免产出 `.d.ts`,仅做类型检查。
|
|
68
|
+
* 与 `runTsc` 错误处理逻辑保持一致。
|
|
69
|
+
*
|
|
70
|
+
* @param root 工作目录。
|
|
71
|
+
* @param config tsconfig 路径(通常是 `tsconfig.client.json`)。
|
|
72
|
+
*/
|
|
73
|
+
export declare function runVueTsc(root: string, config: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* 递归复制整个目录到目标位置。
|
|
76
|
+
*
|
|
77
|
+
* 使用 `fs.cpSync` 的 `recursive` 选项,等价于 `cp -r` 语义。
|
|
78
|
+
* 若源目录不存在则视为无操作(避免业务工程未提供 `public/` 时报错)。
|
|
79
|
+
*
|
|
80
|
+
* @param source 源目录绝对路径。
|
|
81
|
+
* @param destination 目标目录绝对路径。
|
|
82
|
+
*/
|
|
83
|
+
export declare function copyDirectory(source: string, destination: string): void;
|
|
84
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBhD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAe1E;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAQ/E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO,GAAG,IAAI,CAWnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAW5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAKvE"}
|