@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.
- package/core/decode/index.js +18 -2
- package/core/encode/index.js +48 -0
- package/core/util/ast.js +231 -3
- package/package.json +1 -1
package/core/decode/index.js
CHANGED
|
@@ -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
|
-
|
|
100
|
-
|
|
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 });
|
package/core/encode/index.js
CHANGED
|
@@ -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
|
+
}
|