feat(tools): 生成跨模块别名表并提供导入查询 CLI
This commit is contained in:
1 parent
b8fb22f401
commit
f29ad2caaa
4 files changed
+62510
No files matched your search
@@ -488,6 +488,19 @@
|
|||||||
—— 即车库 / 商店的物品配置容器,不是用户属性。
|
—— 即车库 / 商店的物品配置容器,不是用户属性。
|
||||||
- 下一步:给这 8 个 CC 写「CC → 动作」适配器(原包在 module_47243 的模型层里聚合后派发),
|
- 下一步:给这 8 个 CC 写「CC → 动作」适配器(原包在 module_47243 的模型层里聚合后派发),
|
||||||
用户属性那条链路(`UserPropertiesCC` → `SetUserPropertiesAction`)已经可用。
|
用户属性那条链路(`UserPropertiesCC` → `SetUserPropertiesAction`)已经可用。
|
||||||
|
- 跨模块别名表(`tools/gen_module_imports.js`):
|
||||||
|
- 模块两种包装都解析:简单模块 `var x = i(<id>)`,核心模块
|
||||||
|
`!(function (p0…pN) { … })(t, n, i, i(<id1>), …)`(内层参数 p3… 对应尾部 require 列表)。
|
||||||
|
- 扫描 `alias = <depVar>.$_$.<exportName>`,产出 **126 模块 / 8159 条别名**,
|
||||||
|
并带 `ByExport` 反向索引(某个导出被哪些模块以什么别名消费)。
|
||||||
|
产物:`names/module_imports.json`。
|
||||||
|
- 查询 CLI `tools/find_imports.js`:
|
||||||
|
`node find_imports.js 10032::b1q`(谁导入了它)/ `--module 18334` / `--alias 18334 B_`。
|
||||||
|
实测 `10032::b1q` → 只有 `18334::B_`,即 `SetUserPropertiesAction` 的唯一消费点。
|
||||||
|
- 用途:定位「CC → 动作」的转换点。当前已知 CC 访问器
|
||||||
|
(`ju`/`Su`/`Eu`/`Iu`,对应 `BuyableCC.buyable`/`currencyId`、`ItemCategoryCC.category` 等)
|
||||||
|
是 module_85196 的内部函数、不导出,18334 也不直接读 CC 槽位,
|
||||||
|
说明转换经 DI 服务接口完成;下一步用别名表追该接口的实现方法。
|
||||||
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
- 委派表里的已知形态:`shop` / `home` / `localBattleUserState` / `partner` 是内联 IIFE,
|
||||||
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
已按字段具名为 `(inline@<field>)` 并从函数体抓到顶层动作名(69 / 1 / 2 / 11 个);
|
||||||
`launchParams` 是纯透传(`(pass-through)`,无 reducer),已把字段收口为 `Map<String, String>`。
|
`launchParams` 是纯透传(`(pass-through)`,无 reducer),已把字段收口为 `Map<String, String>`。
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,49 @@
|
|||||||
|
// find_imports.js —— 查询跨模块别名表
|
||||||
|
//
|
||||||
|
// 用法:
|
||||||
|
// node find_imports.js 10032::b1q # 谁导入了某个导出(<depModule>::<exportName>)
|
||||||
|
// node find_imports.js --module 18334 # 某模块导入了什么
|
||||||
|
// node find_imports.js --alias 18334 B_ # 某模块里某个别名的来源
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const P = path.resolve(__dirname, '..');
|
||||||
|
const Data = JSON.parse(fs.readFileSync(path.join(P, 'names', 'module_imports.json'), 'utf8'));
|
||||||
|
const Args = process.argv.slice(2);
|
||||||
|
|
||||||
|
if (Args[0] === '--module') {
|
||||||
|
const Id = Args[1];
|
||||||
|
const M = Data.Modules[Id];
|
||||||
|
if (!M) {
|
||||||
|
console.log('没有模块 ' + Id);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log(`module_${Id}(${M.Style}),导入 ${M.Imports.length} 条:`);
|
||||||
|
for (const I of M.Imports) console.log(` ${I.Alias} = ${I.Dep}.$_$.${I.Export}`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Args[0] === '--alias') {
|
||||||
|
const Id = Args[1];
|
||||||
|
const Alias = Args[2];
|
||||||
|
const M = Data.Modules[Id];
|
||||||
|
if (!M) {
|
||||||
|
console.log('没有模块 ' + Id);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const Hits = M.Imports.filter((I) => I.Alias === Alias);
|
||||||
|
console.log(Hits.length ? JSON.stringify(Hits, null, 1) : '没找到别名 ' + Alias);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Args[0]) {
|
||||||
|
console.log('用法:node find_imports.js <depModule>::<exportName> | --module <id> | --alias <id> <alias>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Key = Args[0];
|
||||||
|
const Consumers = Data.ByExport[Key] || [];
|
||||||
|
console.log(`${Key} 被 ${Consumers.length} 处导入:`);
|
||||||
|
for (const C of Consumers) console.log(' ' + C);
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
// gen_module_imports.js —— 跨模块别名表:module → {alias → (depModule, exportName)}
|
||||||
|
//
|
||||||
|
// 模块文件两种包装(`window.__modules__[id] = (t, n, i) => { … }`,`i` 是 require 函数):
|
||||||
|
// A. 简单模块:体内 `var x = i(<id>)` 直接拿到依赖模块对象;
|
||||||
|
// B. 核心模块:`!(function (p0…pN) { … })(t, n, i, i(<id1>), i(<id2>), …)`,
|
||||||
|
// 内层参数 p3…pN 依次对应尾部的 `i(<id>)` 调用。
|
||||||
|
// 两种风格的「导入」都写作 `alias = <depVar>.$_$.<exportName>`。
|
||||||
|
//
|
||||||
|
// 输出:names/module_imports.json
|
||||||
|
// { Note, Modules: { <id>: { Imports: [{ Alias, Dep, Export }] } },
|
||||||
|
// ByExport: { "<dep>::<export>": [ "<module>::<alias>" ] } }
|
||||||
|
//
|
||||||
|
// 用法:cd 3DTank/tools && node gen_module_imports.js
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const P = path.resolve(__dirname, '..');
|
||||||
|
const SrcDir = path.join(P, 'src');
|
||||||
|
const OutPath = path.join(P, 'names', 'module_imports.json');
|
||||||
|
|
||||||
|
function ReadTopArgs(S) {
|
||||||
|
// 尾部:…})(t, n, i, i(85183), …);\n};
|
||||||
|
const T = S.replace(/\r/g, '');
|
||||||
|
const T2 = T.replace(/};\s*$/, '').replace(/\);\s*$/, '');
|
||||||
|
const Open = T2.lastIndexOf('})(');
|
||||||
|
if (Open < 0) return null;
|
||||||
|
const Raw = T2.slice(Open + 3);
|
||||||
|
const Args = [];
|
||||||
|
let Depth = 0, Cur = '';
|
||||||
|
for (let I = 0; I < Raw.length; I++) {
|
||||||
|
const C = Raw[I];
|
||||||
|
if (C === '(') Depth++;
|
||||||
|
else if (C === ')') Depth--;
|
||||||
|
if (C === ',' && Depth === 0) { Args.push(Cur.trim()); Cur = ''; continue; }
|
||||||
|
Cur += C;
|
||||||
|
}
|
||||||
|
if (Cur.trim()) Args.push(Cur.trim());
|
||||||
|
return Args;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Modules = {};
|
||||||
|
const Stats = { Files: 0, StyleIIFE: 0, StylePlain: 0, NoImports: 0, Aliases: 0 };
|
||||||
|
|
||||||
|
const Files = fs.readdirSync(SrcDir).filter((F) => /^module_\d+\.js$/.test(F)).sort();
|
||||||
|
for (const F of Files) {
|
||||||
|
Stats.Files++;
|
||||||
|
const S = fs.readFileSync(path.join(SrcDir, F), 'utf8').replace(/\r/g, '');
|
||||||
|
const IdMatch = /^;\(window\.__modules__[^)]*\)\[(\d+)\]/.exec(S);
|
||||||
|
const Id = IdMatch ? IdMatch[1] : F.match(/(\d+)/)[1];
|
||||||
|
|
||||||
|
// 依赖变量 → 模块 id
|
||||||
|
const DepVar = new Map();
|
||||||
|
// 内层参数风格(B)
|
||||||
|
const Head = /=\s*\(\w+,\s*\w+,\s*\w+\)\s*=>\s*\{\s*!\(function\s*\(([^)]*)\)/.exec(S);
|
||||||
|
if (Head) {
|
||||||
|
const Params = Head[1].split(',').map((x) => x.trim()).filter(Boolean);
|
||||||
|
const Args = ReadTopArgs(S);
|
||||||
|
if (Args && Args.length === Params.length) {
|
||||||
|
for (let I = 3; I < Args.length; I++) {
|
||||||
|
const M = /^\w+\(\s*(\d+)\s*\)$/.exec(Args[I]);
|
||||||
|
if (M) DepVar.set(Params[I], M[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 体内 inline require(A 与 B 通用):var/let x = i(<id>)
|
||||||
|
const InlineRe = /(?:var|let|const|,)\s+([\w$]+)\s*=\s*\w+\(\s*(\d+)\s*\)/g;
|
||||||
|
let M;
|
||||||
|
while ((M = InlineRe.exec(S))) DepVar.set(M[1], M[2]);
|
||||||
|
|
||||||
|
// alias = depVar.$_$.export
|
||||||
|
const Imports = [];
|
||||||
|
const DepVars = [...DepVar.keys()];
|
||||||
|
if (DepVars.length) {
|
||||||
|
const Re = new RegExp('([\\w$]+)\\s*=\\s*(' + DepVars.map((V) => V.replace(/\$/g, '\\$')).join('|') + ')\\.\\$_\\$\\.([\\w$]+)', 'g');
|
||||||
|
let A;
|
||||||
|
while ((A = Re.exec(S))) {
|
||||||
|
Imports.push({ Alias: A[1], Dep: DepVar.get(A[2]), Export: A[3] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去重
|
||||||
|
const Seen = new Set();
|
||||||
|
const Unique = Imports.filter((I) => {
|
||||||
|
const K = I.Alias + '|' + I.Dep + '|' + I.Export;
|
||||||
|
if (Seen.has(K)) return false;
|
||||||
|
Seen.add(K);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Head) Stats.StyleIIFE++;
|
||||||
|
else Stats.StylePlain++;
|
||||||
|
if (!Unique.length) Stats.NoImports++;
|
||||||
|
Stats.Aliases += Unique.length;
|
||||||
|
|
||||||
|
Modules[Id] = { File: F, Style: Head ? 'iife' : 'plain', Imports: Unique };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反向索引:"<dep>::<export>" → ["<module>::<alias>"]
|
||||||
|
const ByExport = {};
|
||||||
|
for (const [Id, Info] of Object.entries(Modules)) {
|
||||||
|
for (const I of Info.Imports) {
|
||||||
|
const K = I.Dep + '::' + I.Export;
|
||||||
|
(ByExport[K] = ByExport[K] || []).push(Id + '::' + I.Alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
OutPath,
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
Note:
|
||||||
|
'跨模块别名表:每个模块的 `alias = <depVar>.$_$.<exportName>`。' +
|
||||||
|
'依赖变量来自内层 IIFE 参数(对应尾部 i(<id>) 列表)或体内 `var x = i(<id>)`。' +
|
||||||
|
'ByExport 为反向索引,供「某个导出被谁消费」查询。',
|
||||||
|
Stats,
|
||||||
|
Modules,
|
||||||
|
ByExport,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`模块 ${Stats.Files}(iife ${Stats.StyleIIFE} / plain ${Stats.StylePlain}),` +
|
||||||
|
`别名 ${Stats.Aliases} 条,无导入模块 ${Stats.NoImports} 个。`,
|
||||||
|
);
|
||||||
Reference in new issue
Block a user