@kezh/my-custom-js-converter 1.5.0 → 1.6.0
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/CLAUDE.md +1 -0
- package/TASKS.md +50 -1
- package/core/config/index.js +1 -0
- package/core/config/main_config.d.ts +5 -0
- package/core/context/map_context.d.ts +14 -0
- package/core/context/map_context.js +43 -10
- package/core/decode/index.js +144 -3
- package/core/scanf/index.d.ts +17 -0
- package/core/scanf/index.js +71 -0
- package/core/util/ast.d.ts +9 -0
- package/core/util/ast.js +39 -1
- package/package.json +42 -42
- package/test/fixtures/sample_project/config.json +18 -17
- package/test/fixtures/sample_project/util/drawer.js +15 -0
- package/test/fixtures/sample_project/util/form.js +11 -0
- package/test/manual_review.js +302 -288
- package/test/run_tests.js +515 -413
package/CLAUDE.md
CHANGED
|
@@ -48,3 +48,4 @@ When used as a dependency, the tool reads `config.json` from the parent project'
|
|
|
48
48
|
- `decode.file`: `"coded.js"`, `decode.output`: `"decoded.js"`, `decode.mount`: `"customEvent"`
|
|
49
49
|
- `settingDir`: `".setting"` (where `mod.map` is stored)
|
|
50
50
|
- `target`: directories to scan relative to `baseDir`
|
|
51
|
+
- `libs`: dependency library search directories (used by decode to supplement missing deps in `mod.map`). Supports `@/util` alias paths (`@` = project root) and `util` paths relative to `baseDir`. When decode finds unknown `__{mod}` suffixes in `coded.js` not present in `mod.map`, it searches these directories for `modName.js` / `modName/index.js` and auto-supplements `mod.map`. Duplicate names are resolved interactively after batch processing.
|
package/TASKS.md
CHANGED
|
@@ -1,2 +1,51 @@
|
|
|
1
1
|
# 任务
|
|
2
|
-
|
|
2
|
+
`npx decode`功能反编其他人的`coded.js`文件时,如果本地的`.setting/mod.map`文件没有及时更新依赖模块,会导致部分依赖无法识别,导致没有引入依赖模块:
|
|
3
|
+
1. 比如当前coded.js为:
|
|
4
|
+
```js
|
|
5
|
+
onClickPickLevel() {
|
|
6
|
+
this.pop__drawer({
|
|
7
|
+
title: "关联隐患信息",
|
|
8
|
+
size: "65%",
|
|
9
|
+
name: "storeOfHiddenDangers",
|
|
10
|
+
param: {}
|
|
11
|
+
}, resp => {
|
|
12
|
+
Object.keys(resp).forEach(key => {
|
|
13
|
+
this.setValue__form(`${key}`, resp[key]);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
2. 当时由于mod.map中没有定义`drawer`依赖,导致反编后的decoded.js为:
|
|
19
|
+
```js
|
|
20
|
+
onClickPickLevel() {
|
|
21
|
+
this.pop__drawer({
|
|
22
|
+
title: "关联隐患信息",
|
|
23
|
+
size: "65%",
|
|
24
|
+
name: "storeOfHiddenDangers",
|
|
25
|
+
param: {}
|
|
26
|
+
}, resp => {
|
|
27
|
+
Object.keys(resp).forEach(key => {
|
|
28
|
+
this.setValue__form(`${key}`, resp[key]);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
3. 但是我们期望的效果是:
|
|
34
|
+
```js
|
|
35
|
+
onClickPickLevel() {
|
|
36
|
+
drawer.pop({
|
|
37
|
+
title: "关联隐患信息",
|
|
38
|
+
size: "65%",
|
|
39
|
+
name: "storeOfHiddenDangers",
|
|
40
|
+
param: {}
|
|
41
|
+
}, resp => {
|
|
42
|
+
Object.keys(resp).forEach(key => {
|
|
43
|
+
this.setValue__form(`${key}`, resp[key]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
我想到的解决方案是:
|
|
49
|
+
1. `npx decode`时,先要将`.setting/mod.map`文件更新,此时需要先找到coded.js中可能的依赖`__{mod}`
|
|
50
|
+
2. 扫描匹配可能得文件名称,并更新mod.map文件
|
|
51
|
+
3. 依赖文件名可能存在重名问题,所以当出现重名时,需要交给用户指定依赖的js文件
|
package/core/config/index.js
CHANGED
|
@@ -5,6 +5,11 @@ export interface MainConfig {
|
|
|
5
5
|
target: string[];
|
|
6
6
|
settingDir: string;
|
|
7
7
|
customConfig: string;
|
|
8
|
+
/**
|
|
9
|
+
* 依赖库搜索目录数组,用于 decode 时补充缺失依赖。
|
|
10
|
+
* 支持 "@/util" 别名路径(@ 代表项目根目录)和 "util" 相对 baseDir 路径。
|
|
11
|
+
*/
|
|
12
|
+
libs: string[];
|
|
8
13
|
}
|
|
9
14
|
export interface EncodeConfig {
|
|
10
15
|
file: string;
|
|
@@ -13,6 +13,20 @@ export declare class MapContext {
|
|
|
13
13
|
getDependencyNameByMod(name: string): string[];
|
|
14
14
|
getSrcPathByMod(name: string): string | undefined;
|
|
15
15
|
getAbsoluteSrcPathByMod(name: string): string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* 补充缺失的依赖到 mod.map
|
|
18
|
+
* 在 self.dependencies 下新增该依赖(让 importMods 生成 require),
|
|
19
|
+
* 并新增独立的 mod 条目(让 getAbsoluteSrcPathByMod 拿到源文件路径)。
|
|
20
|
+
* methods 留空数组,后续 npx encode 会自动补全。
|
|
21
|
+
* @param depName - 依赖名(如 "drawer")
|
|
22
|
+
* @param absoluteSrcPath - 依赖文件的绝对路径
|
|
23
|
+
*/
|
|
24
|
+
appendMissingDep(depName: string, absoluteSrcPath: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* 将当前 map 写入指定目录的 mod.map 文件
|
|
27
|
+
* @param outDir - 输出目录
|
|
28
|
+
*/
|
|
29
|
+
writeToLocalDir(outDir: string): void;
|
|
16
30
|
static readFromLocal(inDir: string): MapContext;
|
|
17
31
|
private init;
|
|
18
32
|
private writeToLocal;
|
|
@@ -59,6 +59,48 @@ class MapContext {
|
|
|
59
59
|
? path_1.default.resolve(parentRootDir, srcPath)
|
|
60
60
|
: srcPath;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* 补充缺失的依赖到 mod.map
|
|
64
|
+
* 在 self.dependencies 下新增该依赖(让 importMods 生成 require),
|
|
65
|
+
* 并新增独立的 mod 条目(让 getAbsoluteSrcPathByMod 拿到源文件路径)。
|
|
66
|
+
* methods 留空数组,后续 npx encode 会自动补全。
|
|
67
|
+
* @param depName - 依赖名(如 "drawer")
|
|
68
|
+
* @param absoluteSrcPath - 依赖文件的绝对路径
|
|
69
|
+
*/
|
|
70
|
+
appendMissingDep(depName, absoluteSrcPath) {
|
|
71
|
+
// self.dependencies[depName] = { methods: [] }
|
|
72
|
+
const selfMod = this.getMod("self");
|
|
73
|
+
if (selfMod) {
|
|
74
|
+
selfMod.dependencies[depName] = new map_config_1.Dependency([]);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const mod = new map_config_1.Mod("", new map_config_1.Dependencies());
|
|
78
|
+
mod.dependencies[depName] = new map_config_1.Dependency([]);
|
|
79
|
+
this.appendModMap(mod, true);
|
|
80
|
+
}
|
|
81
|
+
// map[depName] = { src: <相对 parentRootDir 的路径>, dependencies: {} }
|
|
82
|
+
const parentRootDir = (0, parent_path_1.getParentRootDir)();
|
|
83
|
+
const src = parentRootDir
|
|
84
|
+
? path_1.default.relative(parentRootDir, absoluteSrcPath)
|
|
85
|
+
: absoluteSrcPath;
|
|
86
|
+
this.appendModMap(new map_config_1.Mod(src, new map_config_1.Dependencies()), false, depName);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 将当前 map 写入指定目录的 mod.map 文件
|
|
90
|
+
* @param outDir - 输出目录
|
|
91
|
+
*/
|
|
92
|
+
writeToLocalDir(outDir) {
|
|
93
|
+
const outPath = path_1.default.join(outDir, MapContext.SAVE_FILE);
|
|
94
|
+
logger.info(`write map: ${outPath}`);
|
|
95
|
+
const json = JSON.stringify(this.map, null, 4);
|
|
96
|
+
try {
|
|
97
|
+
fs_1.default.accessSync(outDir);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
fs_1.default.mkdirSync(outDir);
|
|
101
|
+
}
|
|
102
|
+
fs_1.default.writeFileSync(outPath, json.replace(/\n/gm, os_1.EOL), "utf-8");
|
|
103
|
+
}
|
|
62
104
|
static readFromLocal(inDir) {
|
|
63
105
|
const inPath = path_1.default.join(inDir, MapContext.SAVE_FILE);
|
|
64
106
|
logger.info(`read map: ${inPath}`);
|
|
@@ -82,16 +124,7 @@ class MapContext {
|
|
|
82
124
|
this.writeToLocal();
|
|
83
125
|
}
|
|
84
126
|
writeToLocal() {
|
|
85
|
-
|
|
86
|
-
logger.info(`write map: ${outPath}`);
|
|
87
|
-
const json = JSON.stringify(this.map, null, 4);
|
|
88
|
-
try {
|
|
89
|
-
fs_1.default.accessSync(this.outDir);
|
|
90
|
-
}
|
|
91
|
-
catch (err) {
|
|
92
|
-
fs_1.default.mkdirSync(this.outDir);
|
|
93
|
-
}
|
|
94
|
-
fs_1.default.writeFileSync(outPath, json.replace(/\n/gm, os_1.EOL), "utf-8");
|
|
127
|
+
this.writeToLocalDir(this.outDir);
|
|
95
128
|
}
|
|
96
129
|
buildMapContext(inPath, entry, root) {
|
|
97
130
|
const srcAst = (0, ast_1.parseSrcAst)(inPath);
|
package/core/decode/index.js
CHANGED
|
@@ -21,12 +21,14 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
21
21
|
const os_1 = require("os");
|
|
22
22
|
const path_1 = __importDefault(require("path"));
|
|
23
23
|
const prettier_1 = __importDefault(require("prettier"));
|
|
24
|
+
const readline_1 = __importDefault(require("readline"));
|
|
24
25
|
const config_1 = require("../config");
|
|
25
26
|
const map_context_1 = require("../context/map_context");
|
|
26
27
|
const scanf_1 = require("../scanf");
|
|
27
28
|
const ast_1 = require("../util/ast");
|
|
28
29
|
const version_1 = require("../util/version");
|
|
29
30
|
const logger_1 = require("../util/logger");
|
|
31
|
+
const parent_path_1 = require("../util/parent_path");
|
|
30
32
|
const logger = (0, logger_1.createLogger)("decode");
|
|
31
33
|
(function decoding(config) {
|
|
32
34
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -35,15 +37,35 @@ const logger = (0, logger_1.createLogger)("decode");
|
|
|
35
37
|
logger.info(`目标目录: ${targetDirs.join(", ")}`);
|
|
36
38
|
const codeFiles = (0, scanf_1.scanfCodeFiles)(targetDirs, config.decode.file);
|
|
37
39
|
logger.info(`找到 ${codeFiles.length} 个编码文件 (${config.decode.file})`);
|
|
40
|
+
// 解析 libs 目录(用于补充缺失依赖)
|
|
41
|
+
const rootDir = (0, parent_path_1.getParentRootDir)() || config.baseDir;
|
|
42
|
+
const libDirs = (0, scanf_1.resolveLibDirs)(config.libs, config.baseDir, rootDir);
|
|
43
|
+
if (libDirs.length) {
|
|
44
|
+
logger.info(`依赖库搜索目录: ${libDirs.join(", ")}`);
|
|
45
|
+
}
|
|
46
|
+
const conflicts = [];
|
|
38
47
|
for (let i = 0; i < codeFiles.length; i++) {
|
|
39
48
|
const inPath = codeFiles[i];
|
|
40
49
|
const outPath = path_1.default.join(path_1.default.dirname(inPath), config.decode.output);
|
|
41
50
|
const settingDir = path_1.default.join(path_1.default.dirname(inPath), config.settingDir);
|
|
42
51
|
logger.step(`解码处理 [${i + 1}/${codeFiles.length}]`);
|
|
43
52
|
logger.info(`源文件: ${inPath}`);
|
|
44
|
-
yield decoding$0(inPath, outPath, config, settingDir);
|
|
53
|
+
yield decoding$0(inPath, outPath, config, settingDir, libDirs, conflicts);
|
|
45
54
|
logger.info(`输出: ${outPath}`);
|
|
46
55
|
}
|
|
56
|
+
// 批处理结束后处理重名冲突
|
|
57
|
+
if (conflicts.length) {
|
|
58
|
+
logger.step("处理重名依赖冲突");
|
|
59
|
+
const conflictFiles = yield resolveConflicts(conflicts, config);
|
|
60
|
+
// 重新 decode 已解决冲突的文件
|
|
61
|
+
for (const inPath of conflictFiles) {
|
|
62
|
+
const outPath = path_1.default.join(path_1.default.dirname(inPath), config.decode.output);
|
|
63
|
+
const settingDir = path_1.default.join(path_1.default.dirname(inPath), config.settingDir);
|
|
64
|
+
logger.step(`重新解码: ${inPath}`);
|
|
65
|
+
yield decoding$0(inPath, outPath, config, settingDir, libDirs, []);
|
|
66
|
+
logger.info(`输出: ${outPath}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
47
69
|
logger.step("完成");
|
|
48
70
|
logger.info(`共处理 ${codeFiles.length} 个文件`);
|
|
49
71
|
});
|
|
@@ -54,10 +76,12 @@ const logger = (0, logger_1.createLogger)("decode");
|
|
|
54
76
|
* @param outPath - 输入文件路径
|
|
55
77
|
* @param config - 配置
|
|
56
78
|
* @param settingDir - setting目录路径
|
|
79
|
+
* @param libDirs - 依赖库搜索目录(绝对路径)
|
|
80
|
+
* @param conflicts - 重名冲突收集列表(传入 null/空数组表示不记录冲突,如重跑时)
|
|
57
81
|
*/
|
|
58
|
-
function decoding$0(inPath, outPath, config, settingDir) {
|
|
82
|
+
function decoding$0(inPath, outPath, config, settingDir, libDirs, conflicts) {
|
|
59
83
|
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
-
const mapContext = map_context_1.MapContext.readFromLocal(
|
|
84
|
+
const mapContext = map_context_1.MapContext.readFromLocal(settingDir);
|
|
61
85
|
let src = fs_1.default.readFileSync(inPath, {
|
|
62
86
|
encoding: "utf-8"
|
|
63
87
|
});
|
|
@@ -66,6 +90,12 @@ function decoding$0(inPath, outPath, config, settingDir) {
|
|
|
66
90
|
if (!core_1.types.isNode(srcAst)) {
|
|
67
91
|
return;
|
|
68
92
|
}
|
|
93
|
+
// 补充缺失依赖:从 coded.js 提取未知 __{mod} 后缀,在 libs 目录搜索文件
|
|
94
|
+
const supplemented = supplementMissingDeps(srcAst, mapContext, libDirs, inPath, conflicts);
|
|
95
|
+
if (supplemented) {
|
|
96
|
+
// 有补充则先写回 mod.map,确保后续 importMods/deleteModMethods 能用到
|
|
97
|
+
mapContext.writeToLocalDir(settingDir);
|
|
98
|
+
}
|
|
69
99
|
(0, ast_1.importMods)(path_1.default.dirname(outPath), srcAst, mapContext, config.encode.useAlias);
|
|
70
100
|
(0, ast_1.deleteModMethods)(srcAst, mapContext);
|
|
71
101
|
// 从onFormReady方法中提取版本日志并移除
|
|
@@ -82,6 +112,117 @@ function decoding$0(inPath, outPath, config, settingDir) {
|
|
|
82
112
|
fs_1.default.writeFileSync(outPath, dist, "utf-8");
|
|
83
113
|
});
|
|
84
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* 补充缺失依赖到 mapContext
|
|
117
|
+
* 扫描 coded.js 中未知的 __{mod} 后缀,在 libs 目录搜索对应文件并补充到 mod.map。
|
|
118
|
+
* @returns 是否有补充(需要写回 mod.map)
|
|
119
|
+
*/
|
|
120
|
+
function supplementMissingDeps(srcAst, mapContext, libDirs, inPath, conflicts) {
|
|
121
|
+
if (!libDirs.length) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
// 收集已知依赖名(所有 mod 名 + 所有 dependency 名)
|
|
125
|
+
const knownDeps = new Set();
|
|
126
|
+
mapContext.getModNames().forEach((modName) => {
|
|
127
|
+
knownDeps.add(modName);
|
|
128
|
+
mapContext.getDependencyNameByMod(modName).forEach((dep) => knownDeps.add(dep));
|
|
129
|
+
});
|
|
130
|
+
const unknownDeps = (0, ast_1.getUnknownDepNames)(srcAst, knownDeps);
|
|
131
|
+
if (!unknownDeps.length) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
let supplemented = false;
|
|
135
|
+
unknownDeps.forEach((depName) => {
|
|
136
|
+
const candidates = (0, scanf_1.scanfLibMod)(libDirs, depName);
|
|
137
|
+
if (candidates.length === 0) {
|
|
138
|
+
logger.warn(`未找到依赖文件: ${depName}(在 libs 目录中无匹配)`);
|
|
139
|
+
}
|
|
140
|
+
else if (candidates.length === 1) {
|
|
141
|
+
mapContext.appendMissingDep(depName, candidates[0]);
|
|
142
|
+
logger.info(`补充依赖: ${depName} -> ${candidates[0]}`);
|
|
143
|
+
supplemented = true;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
conflicts.push({ inPath, depName, candidates });
|
|
147
|
+
logger.warn(`依赖重名: ${depName},找到 ${candidates.length} 个候选,稍后处理`);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
return supplemented;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 交互式解决重名依赖冲突
|
|
154
|
+
* 逐个让用户选择依赖文件,更新对应 mod.map。
|
|
155
|
+
* @returns 需要重新 decode 的文件路径列表(去重)
|
|
156
|
+
*/
|
|
157
|
+
function resolveConflicts(conflicts, config) {
|
|
158
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
+
// 按 inPath 分组
|
|
160
|
+
const byFile = new Map();
|
|
161
|
+
conflicts.forEach((c) => {
|
|
162
|
+
if (!byFile.has(c.inPath)) {
|
|
163
|
+
byFile.set(c.inPath, []);
|
|
164
|
+
}
|
|
165
|
+
byFile.get(c.inPath).push(c);
|
|
166
|
+
});
|
|
167
|
+
const redecodeFiles = [];
|
|
168
|
+
for (const [inPath, fileConflicts] of byFile) {
|
|
169
|
+
const settingDir = path_1.default.join(path_1.default.dirname(inPath), config.settingDir);
|
|
170
|
+
const mapContext = map_context_1.MapContext.readFromLocal(settingDir);
|
|
171
|
+
let resolved = false;
|
|
172
|
+
logger.info(`文件: ${inPath}`);
|
|
173
|
+
for (const { depName, candidates } of fileConflicts) {
|
|
174
|
+
const choice = yield askConflictChoice(depName, candidates);
|
|
175
|
+
if (choice === null) {
|
|
176
|
+
// 用户跳过
|
|
177
|
+
logger.warn(`跳过依赖: ${depName}`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
mapContext.appendMissingDep(depName, candidates[choice]);
|
|
181
|
+
logger.info(`已选择: ${depName} -> ${candidates[choice]}`);
|
|
182
|
+
resolved = true;
|
|
183
|
+
}
|
|
184
|
+
if (resolved) {
|
|
185
|
+
mapContext.writeToLocalDir(settingDir);
|
|
186
|
+
if (!redecodeFiles.includes(inPath)) {
|
|
187
|
+
redecodeFiles.push(inPath);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return redecodeFiles;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 交互式询问用户选择重名依赖的候选文件
|
|
196
|
+
* @returns 选择的候选序号,null 表示跳过
|
|
197
|
+
*/
|
|
198
|
+
function askConflictChoice(depName, candidates) {
|
|
199
|
+
return new Promise((resolve) => {
|
|
200
|
+
const rl = readline_1.default.createInterface({
|
|
201
|
+
input: process.stdin,
|
|
202
|
+
output: process.stdout
|
|
203
|
+
});
|
|
204
|
+
console.log(`\n 依赖 "${depName}" 找到 ${candidates.length} 个候选文件:`);
|
|
205
|
+
candidates.forEach((c, i) => {
|
|
206
|
+
console.log(` [${i + 1}] ${c}`);
|
|
207
|
+
});
|
|
208
|
+
console.log(` [s] 跳过该依赖`);
|
|
209
|
+
rl.question(` 请选择 [1-${candidates.length} / s]: `, (answer) => {
|
|
210
|
+
rl.close();
|
|
211
|
+
const trimmed = answer.trim().toLowerCase();
|
|
212
|
+
if (trimmed === "s") {
|
|
213
|
+
resolve(null);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const num = parseInt(trimmed, 10);
|
|
217
|
+
if (isNaN(num) || num < 1 || num > candidates.length) {
|
|
218
|
+
logger.warn(`无效输入,已跳过: ${depName}`);
|
|
219
|
+
resolve(null);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
resolve(num - 1);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
}
|
|
85
226
|
/**
|
|
86
227
|
* 从onFormReady方法中提取版本日志并移除,将版本号写入version文件
|
|
87
228
|
* @param srcAst - 源码AST
|
package/core/scanf/index.d.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
export declare function scanfCodeFiles(dirPaths: string | string[], filterName?: string): string[];
|
|
2
2
|
export declare function scanfCodeDirs(baseDir: string, targetDirs: string[]): string[];
|
|
3
|
+
/**
|
|
4
|
+
* 将 libs 配置解析为绝对路径数组
|
|
5
|
+
* 支持 "@/util" 别名路径(@ 代表项目根目录 rootDir)和 "util" 相对 baseDir 路径
|
|
6
|
+
* @param libs - libs 配置数组
|
|
7
|
+
* @param baseDir - 基础目录(相对路径的基准)
|
|
8
|
+
* @param rootDir - 项目根目录(@ 别名的基准,未提供时 @ 路径会被跳过并警告)
|
|
9
|
+
* @returns 绝对路径数组
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveLibDirs(libs: string[], baseDir: string, rootDir?: string): string[];
|
|
12
|
+
/**
|
|
13
|
+
* 在 libs 目录下搜索依赖模块文件
|
|
14
|
+
* 查找 modName.js(文件)和 modName/index.js(目录下入口)
|
|
15
|
+
* @param libDirs - 已解析的绝对路径目录数组
|
|
16
|
+
* @param modName - 依赖模块名(如 "drawer")
|
|
17
|
+
* @returns 所有匹配的绝对路径数组
|
|
18
|
+
*/
|
|
19
|
+
export declare function scanfLibMod(libDirs: string[], modName: string): string[];
|
|
3
20
|
/**
|
|
4
21
|
* 扫描引入路径
|
|
5
22
|
* require('../../util')
|
package/core/scanf/index.js
CHANGED
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.scanfCodeFiles = scanfCodeFiles;
|
|
7
7
|
exports.scanfCodeDirs = scanfCodeDirs;
|
|
8
|
+
exports.resolveLibDirs = resolveLibDirs;
|
|
9
|
+
exports.scanfLibMod = scanfLibMod;
|
|
8
10
|
exports.scanfRequieMod = scanfRequieMod;
|
|
9
11
|
const fs_1 = __importDefault(require("fs"));
|
|
10
12
|
const path_1 = __importDefault(require("path"));
|
|
@@ -46,6 +48,75 @@ function scanfCodeDirs(baseDir, targetDirs) {
|
|
|
46
48
|
const dirs = new Set(targetDirs);
|
|
47
49
|
return [...dirs].map((dir) => `${path_1.default.join(baseDir, dir.replace(/\/|\\/, path_1.default.sep))}`);
|
|
48
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* 将 libs 配置解析为绝对路径数组
|
|
53
|
+
* 支持 "@/util" 别名路径(@ 代表项目根目录 rootDir)和 "util" 相对 baseDir 路径
|
|
54
|
+
* @param libs - libs 配置数组
|
|
55
|
+
* @param baseDir - 基础目录(相对路径的基准)
|
|
56
|
+
* @param rootDir - 项目根目录(@ 别名的基准,未提供时 @ 路径会被跳过并警告)
|
|
57
|
+
* @returns 绝对路径数组
|
|
58
|
+
*/
|
|
59
|
+
function resolveLibDirs(libs, baseDir, rootDir) {
|
|
60
|
+
return libs
|
|
61
|
+
.map((lib) => {
|
|
62
|
+
if (lib.startsWith("@/") || lib.startsWith("@\\")) {
|
|
63
|
+
if (!rootDir) {
|
|
64
|
+
logger.warn(`@ 别名路径需要 rootDir,已跳过: ${lib}`);
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
const sub = lib.replace(/^@[/\\]/, "");
|
|
68
|
+
return path_1.default.join(rootDir.replace(/[\\/]$/, ""), sub);
|
|
69
|
+
}
|
|
70
|
+
return path_1.default.join(baseDir, lib.replace(/\/|\\/, path_1.default.sep));
|
|
71
|
+
})
|
|
72
|
+
.filter((dir) => dir !== "");
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 在 libs 目录下搜索依赖模块文件
|
|
76
|
+
* 查找 modName.js(文件)和 modName/index.js(目录下入口)
|
|
77
|
+
* @param libDirs - 已解析的绝对路径目录数组
|
|
78
|
+
* @param modName - 依赖模块名(如 "drawer")
|
|
79
|
+
* @returns 所有匹配的绝对路径数组
|
|
80
|
+
*/
|
|
81
|
+
function scanfLibMod(libDirs, modName) {
|
|
82
|
+
const results = [];
|
|
83
|
+
const seen = new Set();
|
|
84
|
+
libDirs.forEach((libDir) => {
|
|
85
|
+
if (!fs_1.default.existsSync(libDir)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// 1. libDir/modName.js
|
|
89
|
+
const filePath = path_1.default.join(libDir, `${modName}.js`);
|
|
90
|
+
if (fs_1.default.existsSync(filePath) && !seen.has(filePath)) {
|
|
91
|
+
seen.add(filePath);
|
|
92
|
+
results.push(filePath);
|
|
93
|
+
}
|
|
94
|
+
// 2. libDir/modName/index.js(modName 为子目录)
|
|
95
|
+
const subDir = path_1.default.join(libDir, modName);
|
|
96
|
+
if (fs_1.default.existsSync(subDir) && fs_1.default.statSync(subDir).isDirectory()) {
|
|
97
|
+
const indexFile = path_1.default.join(subDir, "index.js");
|
|
98
|
+
if (fs_1.default.existsSync(indexFile) && !seen.has(indexFile)) {
|
|
99
|
+
seen.add(indexFile);
|
|
100
|
+
results.push(indexFile);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// 3. 递归搜索子目录(通用方法可能嵌套在子目录下)
|
|
104
|
+
const subDirs = fs_1.default
|
|
105
|
+
.readdirSync(libDir, { withFileTypes: true })
|
|
106
|
+
.filter((d) => d.isDirectory())
|
|
107
|
+
.map((d) => path_1.default.join(libDir, d.name));
|
|
108
|
+
subDirs.forEach((sub) => {
|
|
109
|
+
const nested = scanfLibMod([sub], modName);
|
|
110
|
+
nested.forEach((p) => {
|
|
111
|
+
if (!seen.has(p)) {
|
|
112
|
+
seen.add(p);
|
|
113
|
+
results.push(p);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
return results;
|
|
119
|
+
}
|
|
49
120
|
/**
|
|
50
121
|
* 扫描引入路径
|
|
51
122
|
* require('../../util')
|
package/core/util/ast.d.ts
CHANGED
|
@@ -82,3 +82,12 @@ export declare function deleteModMethods(srcAst: AstType, mapContext: MapContext
|
|
|
82
82
|
* @param mapContext
|
|
83
83
|
*/
|
|
84
84
|
export declare function getInlineMethodsByMethodName(srcAst: AstType, entry: string, methodName: string, useMethodNameMap: Map<string, string[]>): string[];
|
|
85
|
+
/**
|
|
86
|
+
* 从 coded.js 的 AST 中提取未知的依赖名(__{mod} 后缀中的 mod)
|
|
87
|
+
* 扫描 ObjectMethod 的 key 和 MemberExpression 的 property,提取 xxx__yyy 中的 yyy,
|
|
88
|
+
* 排除已知依赖名(mod.map 中已有的依赖)。
|
|
89
|
+
* @param srcAst - 源码AST
|
|
90
|
+
* @param knownDeps - 已知依赖名集合
|
|
91
|
+
* @returns 未知依赖名数组(去重)
|
|
92
|
+
*/
|
|
93
|
+
export declare function getUnknownDepNames(srcAst: AstType, knownDeps: Set<string>): string[];
|
package/core/util/ast.js
CHANGED
|
@@ -15,6 +15,7 @@ exports.getDependentMethodNames = getDependentMethodNames;
|
|
|
15
15
|
exports.importMods = importMods;
|
|
16
16
|
exports.deleteModMethods = deleteModMethods;
|
|
17
17
|
exports.getInlineMethodsByMethodName = getInlineMethodsByMethodName;
|
|
18
|
+
exports.getUnknownDepNames = getUnknownDepNames;
|
|
18
19
|
const fs_1 = __importDefault(require("fs"));
|
|
19
20
|
const parser_1 = require("@babel/parser");
|
|
20
21
|
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
@@ -346,7 +347,8 @@ function importMods(outDir, srcAst, mapContext, useAlias = false) {
|
|
|
346
347
|
.replace(/\\/g, "/")
|
|
347
348
|
.replace(/\/index\.js$/, "")
|
|
348
349
|
.replace(/\.js$/, "");
|
|
349
|
-
|
|
350
|
+
// 确保 @ 别名路径格式为 @/xxx(@ 与路径之间必须有 /)
|
|
351
|
+
requireFrom = aliasPath.startsWith("/") ? "@" + aliasPath : "@/" + aliasPath;
|
|
350
352
|
}
|
|
351
353
|
else {
|
|
352
354
|
requireFrom = (0, path_1.relative)(outDir, srcPath.replace(/\/index\.js$/, "").replace(/\\index\.js$/, "").replace(/\.js$/, ""))
|
|
@@ -454,3 +456,39 @@ function getInlineMethodsByMethodName(srcAst, entry, methodName, useMethodNameMa
|
|
|
454
456
|
});
|
|
455
457
|
return [...inlineMethods];
|
|
456
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* 从 coded.js 的 AST 中提取未知的依赖名(__{mod} 后缀中的 mod)
|
|
461
|
+
* 扫描 ObjectMethod 的 key 和 MemberExpression 的 property,提取 xxx__yyy 中的 yyy,
|
|
462
|
+
* 排除已知依赖名(mod.map 中已有的依赖)。
|
|
463
|
+
* @param srcAst - 源码AST
|
|
464
|
+
* @param knownDeps - 已知依赖名集合
|
|
465
|
+
* @returns 未知依赖名数组(去重)
|
|
466
|
+
*/
|
|
467
|
+
function getUnknownDepNames(srcAst, knownDeps) {
|
|
468
|
+
const depNames = new Set();
|
|
469
|
+
const collect = (name) => {
|
|
470
|
+
const split = name.split("__");
|
|
471
|
+
if (split.length < 2) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
// 取最后一段作为依赖名(与 deleteModMethods 的解析逻辑一致)
|
|
475
|
+
const depName = split[split.length - 1];
|
|
476
|
+
if (!depName || knownDeps.has(depName)) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
depNames.add(depName);
|
|
480
|
+
};
|
|
481
|
+
(0, traverse_1.default)(srcAst, {
|
|
482
|
+
ObjectMethod(path) {
|
|
483
|
+
if (core_1.types.isIdentifier(path.node.key)) {
|
|
484
|
+
collect(path.node.key.name);
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
MemberExpression(path) {
|
|
488
|
+
if (core_1.types.isIdentifier(path.node.property)) {
|
|
489
|
+
collect(path.node.property.name);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
return [...depNames];
|
|
494
|
+
}
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@kezh/my-custom-js-converter",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "自定义代码转换器",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"encode": "core/encode/index.js",
|
|
8
|
-
"decode": "core/decode/index.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "npx tsc --build",
|
|
12
|
-
"test": "npm run build && node test/run_tests.js",
|
|
13
|
-
"test:manual": "npm run build && node test/manual_review.js"
|
|
14
|
-
},
|
|
15
|
-
"engines": {
|
|
16
|
-
"node": ">=24.15.0"
|
|
17
|
-
},
|
|
18
|
-
"author": "shahow",
|
|
19
|
-
"license": "MIT",
|
|
20
|
-
"keywords": [
|
|
21
|
-
"babel"
|
|
22
|
-
],
|
|
23
|
-
"dependencies": {
|
|
24
|
-
"@babel/cli": "^7.29.7",
|
|
25
|
-
"@babel/core": "^7.29.7",
|
|
26
|
-
"@babel/generator": "^7.29.7",
|
|
27
|
-
"@babel/parser": "^7.29.7",
|
|
28
|
-
"@babel/traverse": "^7.29.7",
|
|
29
|
-
"@babel/types": "^7.29.7",
|
|
30
|
-
"prettier": "^3.8.4"
|
|
31
|
-
},
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@types/babel__core": "^7.20.5",
|
|
34
|
-
"@types/babel__generator": "^7.27.0",
|
|
35
|
-
"@types/babel__traverse": "^7.28.0",
|
|
36
|
-
"@types/node": "^25.9.3",
|
|
37
|
-
"typescript": "^6.0.3"
|
|
38
|
-
},
|
|
39
|
-
"volta": {
|
|
40
|
-
"node": "24.15.0"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@kezh/my-custom-js-converter",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"description": "自定义代码转换器",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"encode": "core/encode/index.js",
|
|
8
|
+
"decode": "core/decode/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "npx tsc --build",
|
|
12
|
+
"test": "npm run build && node test/run_tests.js",
|
|
13
|
+
"test:manual": "npm run build && node test/manual_review.js"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=24.15.0"
|
|
17
|
+
},
|
|
18
|
+
"author": "shahow",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"babel"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@babel/cli": "^7.29.7",
|
|
25
|
+
"@babel/core": "^7.29.7",
|
|
26
|
+
"@babel/generator": "^7.29.7",
|
|
27
|
+
"@babel/parser": "^7.29.7",
|
|
28
|
+
"@babel/traverse": "^7.29.7",
|
|
29
|
+
"@babel/types": "^7.29.7",
|
|
30
|
+
"prettier": "^3.8.4"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/babel__core": "^7.20.5",
|
|
34
|
+
"@types/babel__generator": "^7.27.0",
|
|
35
|
+
"@types/babel__traverse": "^7.28.0",
|
|
36
|
+
"@types/node": "^25.9.3",
|
|
37
|
+
"typescript": "^6.0.3"
|
|
38
|
+
},
|
|
39
|
+
"volta": {
|
|
40
|
+
"node": "24.15.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
{
|
|
2
|
-
"encode": {
|
|
3
|
-
"file": "index.js",
|
|
4
|
-
"output": "coded.js",
|
|
5
|
-
"entry": "customEvent",
|
|
6
|
-
"ignoreMod": [],
|
|
7
|
-
"useAlias": true
|
|
8
|
-
},
|
|
9
|
-
"decode": {
|
|
10
|
-
"file": "coded.js",
|
|
11
|
-
"output": "decoded.js",
|
|
12
|
-
"mount": "customEvent"
|
|
13
|
-
},
|
|
14
|
-
"target": ["src"],
|
|
15
|
-
"settingDir": ".setting",
|
|
16
|
-
"customConfig": "config.json"
|
|
17
|
-
|
|
1
|
+
{
|
|
2
|
+
"encode": {
|
|
3
|
+
"file": "index.js",
|
|
4
|
+
"output": "coded.js",
|
|
5
|
+
"entry": "customEvent",
|
|
6
|
+
"ignoreMod": [],
|
|
7
|
+
"useAlias": true
|
|
8
|
+
},
|
|
9
|
+
"decode": {
|
|
10
|
+
"file": "coded.js",
|
|
11
|
+
"output": "decoded.js",
|
|
12
|
+
"mount": "customEvent"
|
|
13
|
+
},
|
|
14
|
+
"target": ["src"],
|
|
15
|
+
"settingDir": ".setting",
|
|
16
|
+
"customConfig": "config.json",
|
|
17
|
+
"libs": ["@/util"]
|
|
18
|
+
}
|