@kezh/my-custom-js-converter 1.6.4 → 1.9.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.
@@ -96,8 +96,24 @@ function decoding$0(inPath, outPath, config, settingDir, libDirs, conflicts) {
96
96
  // 有补充则先写回 mod.map,确保后续 importMods/deleteModMethods 能用到
97
97
  mapContext.writeToLocalDir(settingDir);
98
98
  }
99
- (0, ast_1.importMods)(path_1.default.dirname(outPath), srcAst, mapContext, config.encode.useAlias);
100
- (0, ast_1.deleteModMethods)(srcAst, mapContext);
99
+ // 稽核内联工具方法体是否与源文件一致(忽略注释)
100
+ // 必须在 importMods 之前:有不一致方法的依赖不生成 require(方案B:自包含)
101
+ const mismatches = (0, ast_1.auditModMethods)(srcAst, mapContext);
102
+ const skipDepNames = new Set();
103
+ if (mismatches.length) {
104
+ // 推导出有不一致方法的依赖名集合:该依赖所有方法都保留内联,不 require
105
+ mismatches.forEach((m) => skipDepNames.add(m.modName));
106
+ logger.warn(`检测到 ${mismatches.length} 个内联工具方法与源文件不一致,涉及依赖: ${[
107
+ ...skipDepNames
108
+ ].join(", ")}`);
109
+ logger.warn(`以下依赖的所有方法将保留内联形态不还原(不生成 require,decoded.js 自包含):`);
110
+ mismatches.forEach((m) => {
111
+ logger.warn(` - ${m.methodKey}(源文件: ${m.srcPath})`);
112
+ });
113
+ logger.warn(`请检查上述方法,确认 coded.js 与源文件差异后手动处理或重新 encode。`);
114
+ }
115
+ (0, ast_1.importMods)(path_1.default.dirname(outPath), srcAst, mapContext, config.encode.useAlias, skipDepNames);
116
+ (0, ast_1.deleteModMethods)(srcAst, mapContext, skipDepNames);
101
117
  // 从onFormReady方法中提取版本日志并移除
102
118
  extractAndRemoveVersionLog(srcAst, settingDir);
103
119
  let { code: dist } = (0, generator_1.default)(srcAst, { compact: true });
@@ -11,6 +11,7 @@ const map_context_1 = require("../context/map_context");
11
11
  const ast_1 = require("../util/ast");
12
12
  const version_1 = require("../util/version");
13
13
  const core_1 = require("@babel/core");
14
+ const parent_path_1 = require("../util/parent_path");
14
15
  const logger_1 = require("../util/logger");
15
16
  const logger = (0, logger_1.createLogger)("encode");
16
17
  (function encoding(config) {
@@ -26,6 +27,13 @@ const logger = (0, logger_1.createLogger)("encode");
26
27
  const settingDir = path_1.default.join(workDir, config.settingDir);
27
28
  logger.step(`编码处理 [${index + 1}/${codeFiles.length}]`);
28
29
  logger.info(`源文件: ${inPath}`);
30
+ // 防护检测:源文件 mount 对象内若残留 __mod 后缀方法,说明用户复用了 decode 产物
31
+ // 作为二开源文件。此时禁止通过 require 引入工具方法,只做无依赖编码。
32
+ const guardResult = guardInlinedModMethods(inPath, encodeConfig.entry, config);
33
+ if (guardResult) {
34
+ logger.error(guardResult);
35
+ return;
36
+ }
29
37
  const mapContext = new map_context_1.MapContext(inPath, encodeConfig.entry, settingDir, true);
30
38
  const methods = encoding$0(mapContext, encodeConfig.entry);
31
39
  totalMethods += methods.length;
@@ -109,3 +117,43 @@ function reorderMethods(methods) {
109
117
  methods.length = 0;
110
118
  methods.push(...priorityMethods, ...restMethods);
111
119
  }
120
+ /**
121
+ * 防护检测:源文件 mount 对象内若残留 `__mod` 后缀方法,且同时通过 require 引入了
122
+ * 工具方法依赖,则禁止编码。
123
+ *
124
+ * 场景:用户把 decode 产物(含未还原的 `validate__util` 等内联方法)复制为 index.js
125
+ * 进行二开,同时又保留了 `const util = require(...)`。此时若继续 encode,会导致
126
+ * 方法重复定义、mod.map 错乱。应提示用户移除 require 引入,只做无依赖编码。
127
+ *
128
+ * @param inPath - 源文件路径
129
+ * @param entry - 入口对象名
130
+ * @param config - 主配置
131
+ * @returns 错误提示字符串(检测到连规时返回,未连规返回 null)
132
+ */
133
+ function guardInlinedModMethods(inPath, entry, config) {
134
+ const srcAst = (0, ast_1.parseSrcAst)(inPath);
135
+ if (!srcAst) {
136
+ return null;
137
+ }
138
+ const inlinedNames = (0, ast_1.getInlinedModMethodNames)(srcAst, entry);
139
+ if (!inlinedNames.length) {
140
+ return null;
141
+ }
142
+ // 检测是否存在 require 引入的工具方法依赖
143
+ const rootDir = config.encode.useAlias
144
+ ? (0, parent_path_1.getParentRootDir)() || config.baseDir
145
+ : undefined;
146
+ const requireModPaths = (0, ast_1.getRequireModPaths)(path_1.default.dirname(inPath), srcAst, undefined, rootDir);
147
+ if (!requireModPaths.size) {
148
+ // 有 __mod 方法但无 require 依赖:允许无依赖编码
149
+ return null;
150
+ }
151
+ const depNames = [...requireModPaths.keys()];
152
+ return (`检测到源文件 mount 对象内残留工具方法后缀(${inlinedNames.join(", ")}),` +
153
+ `同时存在 require 依赖引入(${depNames.join(", ")})。\n` +
154
+ `此场景不允许编码:请移除源文件中的 require 工具方法引入(${depNames
155
+ .map((n) => `const ${n} = require(...)`)
156
+ .join("; ")}),` +
157
+ `仅保留 mount 对象内的 __mod 后缀方法做无依赖编码;` +
158
+ `或将 __mod 后缀方法抽为独立工具文件后再 encode。`);
159
+ }
package/core/util/ast.js CHANGED
@@ -16,6 +16,8 @@ exports.importMods = importMods;
16
16
  exports.deleteModMethods = deleteModMethods;
17
17
  exports.getInlineMethodsByMethodName = getInlineMethodsByMethodName;
18
18
  exports.getUnknownDepNames = getUnknownDepNames;
19
+ exports.auditModMethods = auditModMethods;
20
+ exports.getInlinedModMethodNames = getInlinedModMethodNames;
19
21
  const fs_1 = __importDefault(require("fs"));
20
22
  const parser_1 = require("@babel/parser");
21
23
  const traverse_1 = __importDefault(require("@babel/traverse"));
@@ -360,8 +362,8 @@ function getDependentMethodNames(srcAst, methodNames, depNames) {
360
362
  * @param mapContext
361
363
  * @param useAlias - 是否使用 @ 别名路径
362
364
  */
363
- function importMods(outDir, srcAst, mapContext, useAlias = false) {
364
- const depNames = mapContext.getDependencyNameByMod("self");
365
+ function importMods(outDir, srcAst, mapContext, useAlias = false, skipDepNames = new Set()) {
366
+ const depNames = mapContext.getDependencyNameByMod("self").filter((name) => !skipDepNames.has(name));
365
367
  const rootDir = (0, parent_path_1.getParentRootDir)() || config_1.config.baseDir;
366
368
  const importMods = depNames.map((name) => {
367
369
  const srcPath = mapContext.getAbsoluteSrcPathByMod(name);
@@ -399,8 +401,12 @@ function importMods(outDir, srcAst, mapContext, useAlias = false) {
399
401
  /**
400
402
  * 去除第三方模块方法
401
403
  * @param srcAst - 源码AST
404
+ * @param mapContext
405
+ * @param skipDepNames - 需要跳过(不删除定义、不还原调用、不生成 require)的依赖名集合。
406
+ * 用于稽核出方法体不一致的依赖:该依赖任一方法与源文件不一致时,共所有方法
407
+ * 都保留内联形态(包括一致方法),实现 decoded.js 自包含,不 require 源文件。
402
408
  */
403
- function deleteModMethods(srcAst, mapContext) {
409
+ function deleteModMethods(srcAst, mapContext, skipDepNames = new Set()) {
404
410
  const deps = new Set();
405
411
  mapContext
406
412
  .getModNames()
@@ -417,6 +423,10 @@ function deleteModMethods(srcAst, mapContext) {
417
423
  return;
418
424
  }
419
425
  const depName = split.length ? split[split.length - 1] : "";
426
+ // 稽核不一致的依赖:保留其所有内联定义,不删除
427
+ if (skipDepNames.has(depName)) {
428
+ return;
429
+ }
420
430
  deps.has(depName) && path.remove();
421
431
  }
422
432
  });
@@ -432,6 +442,10 @@ function deleteModMethods(srcAst, mapContext) {
432
442
  }
433
443
  const depName = split[split.length - 1];
434
444
  if (deps.has(depName)) {
445
+ // 稽核不一致的依赖:保留 this.method__dep 调用形态,不还原
446
+ if (skipDepNames.has(depName)) {
447
+ return;
448
+ }
435
449
  path.node.property.name = methodName.replace(`__${depName}`, "");
436
450
  if (core_1.types.isThisExpression(path.node.object)) {
437
451
  path.node.object = core_1.types.identifier(depName);
@@ -518,3 +532,217 @@ function getUnknownDepNames(srcAst, knownDeps) {
518
532
  });
519
533
  return [...depNames];
520
534
  }
535
+ /**
536
+ * 递归清除节点及其子树上所有 comments 字段(leading/inner/trailing),
537
+ * 用于“忽略注释”的代码比对。
538
+ */
539
+ function stripCommentsDeep(node) {
540
+ if (!node || typeof node !== "object") {
541
+ return;
542
+ }
543
+ // 处理数组节点
544
+ if (Array.isArray(node)) {
545
+ node.forEach((child) => stripCommentsDeep(child));
546
+ return;
547
+ }
548
+ const n = node;
549
+ n.leadingComments = null;
550
+ n.innerComments = null;
551
+ n.trailingComments = null;
552
+ for (const key of Object.keys(n)) {
553
+ const val = n[key];
554
+ if (val && typeof val === "object") {
555
+ stripCommentsDeep(val);
556
+ }
557
+ }
558
+ }
559
+ /**
560
+ * 生成节点的归一化代码:去注释 + generate + 压缩空白/换行,
561
+ * 使仅注释或排版差异的代码视为相同。
562
+ */
563
+ function normalizeNodeCode(node) {
564
+ // 克隆避免污染原 AST
565
+ const cloned = JSON.parse(JSON.stringify(node));
566
+ stripCommentsDeep(cloned);
567
+ const { code } = (0, generator_1.default)(cloned, { compact: true });
568
+ // 压缩连续空白与换行,统一比较基准
569
+ return code.replace(/\s+/g, " ").trim();
570
+ }
571
+ /**
572
+ * 对单个克隆的方法节点,在其方法体内应用与 encode `modifyObjectMethods`
573
+ * (非 root 分支)一致的调用重命名,使其与 coded.js 内联方法体可比对:
574
+ * - `this.method`(method 属于该 mod 自身方法集)→ `this.method__entry`
575
+ * - `dep.method`(dep 属于 mod.dependencies 且 method 在 dep.methods)→ `this.method__dep`
576
+ * 同时把方法 key 由 `method` 重命名为 `method__entry`。
577
+ *
578
+ * 注意:仅作用于传入的方法节点子树,不污染原 AST(调用方需传入克隆节点)。
579
+ */
580
+ function renameCallsInsideMethod(methodNode, entry, selfMethods, deps) {
581
+ // 重命名方法 key:method → method__entry
582
+ if (core_1.types.isIdentifier(methodNode.key)) {
583
+ methodNode.key.name = `${methodNode.key.name}__${entry}`;
584
+ }
585
+ // 在方法体内遍历 MemberExpression 做调用重命名
586
+ // 使用 path 作用域限定在该方法节点内
587
+ const selfSet = new Set(selfMethods);
588
+ const depMap = new Map();
589
+ deps.forEach((d) => depMap.set(d.name, new Set(d.methods)));
590
+ // 手动遍历方法体(避免引入完整 traverse 的作用域问题)
591
+ const visit = (node) => {
592
+ if (!node || typeof node !== "object") {
593
+ return;
594
+ }
595
+ if (Array.isArray(node)) {
596
+ node.forEach((child) => visit(child));
597
+ return;
598
+ }
599
+ if (core_1.types.isMemberExpression(node)) {
600
+ // this.method → this.method__entry(method 属于自身方法集)
601
+ if (core_1.types.isThisExpression(node.object) &&
602
+ core_1.types.isIdentifier(node.property) &&
603
+ selfSet.has(node.property.name)) {
604
+ node.property.name = `${node.property.name}__${entry}`;
605
+ }
606
+ // dep.method → this.method__dep
607
+ if (core_1.types.isIdentifier(node.object) &&
608
+ core_1.types.isIdentifier(node.property) &&
609
+ depMap.has(node.object.name) &&
610
+ depMap.get(node.object.name).has(node.property.name)) {
611
+ const depName = node.object.name;
612
+ node.object = core_1.types.thisExpression();
613
+ node.property.name = `${node.property.name}__${depName}`;
614
+ }
615
+ }
616
+ for (const key of Object.keys(node)) {
617
+ if (key === "loc" || key === "start" || key === "end" || key === "range") {
618
+ continue;
619
+ }
620
+ const val = node[key];
621
+ if (val && typeof val === "object") {
622
+ visit(val);
623
+ }
624
+ }
625
+ };
626
+ visit(methodNode);
627
+ }
628
+ /**
629
+ * 稽核 coded.js 中内联工具方法的方法体是否与源文件一致(忽略注释)。
630
+ *
631
+ * 比对基准:对源文件中对应方法应用与 encode 一致的重命名后,生成归一化代码,
632
+ * 与 coded.js 内联方法体的归一化代码比对。若不一致,说明 coded.js 被手动修改
633
+ * 或源文件已漂移,应跳过该方法的 decode(保留内联定义与 this.method__mod 调用)。
634
+ *
635
+ * @param srcAst - coded.js 的 AST(已包裹为 mount 对象)
636
+ * @param mapContext - mod.map 上下文
637
+ * @returns 不一致的方法列表
638
+ */
639
+ function auditModMethods(srcAst, mapContext) {
640
+ const mismatches = [];
641
+ // 所有已知依赖名并集(与 deleteModMethods 一致)
642
+ const deps = new Set();
643
+ mapContext
644
+ .getModNames()
645
+ .flatMap((name) => { var _a; return Object.keys((_a = mapContext.getMod(name)) === null || _a === void 0 ? void 0 : _a.dependencies); })
646
+ .forEach((dep) => deps.add(dep));
647
+ // 缓存源文件 AST,避免重复解析
648
+ const srcAstCache = new Map();
649
+ const getSrcAst = (modName) => {
650
+ if (srcAstCache.has(modName)) {
651
+ return srcAstCache.get(modName);
652
+ }
653
+ const srcPath = mapContext.getAbsoluteSrcPathByMod(modName);
654
+ const ast = srcPath ? parseSrcAst(srcPath) : undefined;
655
+ srcAstCache.set(modName, ast);
656
+ return ast;
657
+ };
658
+ (0, traverse_1.default)(srcAst, {
659
+ ObjectMethod(path) {
660
+ if (!core_1.types.isIdentifier(path.node.key)) {
661
+ return;
662
+ }
663
+ const fullMethodName = path.node.key.name;
664
+ const split = fullMethodName.split("__");
665
+ if (split.length < 2) {
666
+ return;
667
+ }
668
+ const depName = split[split.length - 1];
669
+ if (!deps.has(depName)) {
670
+ return;
671
+ }
672
+ // 原始方法名:去掉 __depName 后缀
673
+ const originalName = fullMethodName.slice(0, fullMethodName.length - depName.length - 2);
674
+ const srcPath = mapContext.getAbsoluteSrcPathByMod(depName);
675
+ const depSrcAst = getSrcAst(depName);
676
+ if (!depSrcAst || !srcPath) {
677
+ // 源文件不可读,无法稽核,跳过(不视为不一致)
678
+ return;
679
+ }
680
+ // 从源文件取出对应方法节点
681
+ const srcMethods = getObjectMethodsByEntryAndMethodNames(depSrcAst, depName, [originalName]);
682
+ if (!srcMethods.length) {
683
+ // 源文件中找不到该方法,视为不一致(源文件已变更)
684
+ mismatches.push({
685
+ methodKey: fullMethodName,
686
+ methodName: originalName,
687
+ modName: depName,
688
+ srcPath
689
+ });
690
+ return;
691
+ }
692
+ // 克隆源方法节点,应用 encode 重命名,归一化
693
+ // 自身方法集取源文件中该 entry 对象的所有方法名(与 encode modifyObjectMethods 一致),
694
+ // 而非 mod.map 的引用集(补充依赖场景下引用集可能为空)
695
+ const allSrcMethods = getObjectMethodsByEntryAndMethodNames(depSrcAst, depName);
696
+ const selfMethodNames = getObjectMethodNames(allSrcMethods);
697
+ const srcCloned = JSON.parse(JSON.stringify(srcMethods[0]));
698
+ const mod = mapContext.getMod(depName);
699
+ const depEntries = mod
700
+ ? Object.keys(mod.dependencies).map((name) => ({
701
+ name,
702
+ methods: mod.dependencies[name].methods
703
+ }))
704
+ : [];
705
+ renameCallsInsideMethod(srcCloned, depName, selfMethodNames, depEntries);
706
+ const srcNormalized = normalizeNodeCode(srcCloned);
707
+ // coded.js 内联方法节点归一化(直接比对,已是重命名后形态)
708
+ const codedCloned = JSON.parse(JSON.stringify(path.node));
709
+ const codedNormalized = normalizeNodeCode(codedCloned);
710
+ if (srcNormalized !== codedNormalized) {
711
+ mismatches.push({
712
+ methodKey: fullMethodName,
713
+ methodName: originalName,
714
+ modName: depName,
715
+ srcPath
716
+ });
717
+ }
718
+ }
719
+ });
720
+ return mismatches;
721
+ }
722
+ /**
723
+ * 检测入口对象内是否含有 `__mod` 后缀的方法定义。
724
+ *
725
+ * 用于 encode 防护:当源文件(index.js)的 mount 对象内残留带 `__mod` 后缀的方法时,
726
+ * 说明用户直接复用了 decode 产物(含未还原的内联工具方法)作为二开源文件。
727
+ * 此类源文件不应再通过 require 引入工具方法做依赖编码,否则会导致方法重复定义、
728
+ * mod.map 错乱。encode 检测到此场景应禁止 require 引入,只做无依赖编码。
729
+ *
730
+ * @param srcAst - 源码AST
731
+ * @param entry - 入口对象名
732
+ * @returns 含 `__mod` 后缀的方法名列表(如 `["validate__util", "fetchData__util"]`)
733
+ */
734
+ function getInlinedModMethodNames(srcAst, entry) {
735
+ const inlinedNames = [];
736
+ // 取入口对象的所有方法,筛选出 key 含 __ 后缀的
737
+ const methods = getObjectMethodsByEntryAndMethodNames(srcAst, entry);
738
+ methods.forEach((method) => {
739
+ if (!core_1.types.isIdentifier(method.key)) {
740
+ return;
741
+ }
742
+ const name = method.key.name;
743
+ if (name.split("__").length >= 2) {
744
+ inlinedNames.push(name);
745
+ }
746
+ });
747
+ return inlinedNames;
748
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kezh/my-custom-js-converter",
3
- "version": "1.6.4",
3
+ "version": "1.9.0",
4
4
  "description": "自定义代码转换器",
5
5
  "main": "index.js",
6
6
  "files": [