feat(3dtank): 本地镜像与 hornet 工具链迁到新版 bundle main.6469b966.js
旧 main.b7114b43.js 已被新构建替换(10,011,039 字节 / 1335 模块),本地镜像整体重切到新版并清掉旧产物;hornet 工具链按 tools/map_symbols.js 生成的新旧符号映射改名后重新打包(3dtank_bundle.js 与 unpacked/hornet_bundle.js 同步)。tools/symbol_map.json(8.9MB 映射产物)与切割解包目录不进库,可用 map_symbols.js / split.js 重新生成。
This commit is contained in:
1 parent
fd32881c3d
commit
90c377ad79
103 files changed
+400753
-399528
No files matched your search
+120
-25
@@ -1,24 +1,49 @@
|
||||
// split.js - 将 main.b7114b43.js 切割为多文件加载结构
|
||||
// split.js - 将 main.<hash>.js 切割为多文件加载结构
|
||||
// 优化: 资源模块(标准 t.exports=i.p+"..." 格式)用 JSON + 模板替代,不生成独立文件
|
||||
// 用法: node split.js [bundle路径] [输出目录] [--clean] [--no-hornet]
|
||||
// 未指定 bundle 时自动取项目根目录下最新的 main.<hash>.js
|
||||
// 未指定输出目录时默认 3dtank.com/play/static/js
|
||||
// --clean 先删除旧产物(unpacked/module_*.js、resources.json、旧 main.*.js;保留 hornet_*.js)
|
||||
// --no-hornet 不在主文件末尾注入 hornet_bundle 自动加载
|
||||
// 输出:
|
||||
// ./3dtank.com/play/static/js/main.b7114b43.js (主文件: 加载器 + runtime)
|
||||
// ./3dtank.com/play/static/js/unpacked/resources.json (资源模块 ID→路径映射)
|
||||
// ./3dtank.com/play/static/js/unpacked/module_<id>.js (仅代码模块,约 126 个)
|
||||
// <输出目录>/main.<hash>.js (主文件: 加载器 + runtime)
|
||||
// <输出目录>/unpacked/resources.json (资源模块 ID→路径映射)
|
||||
// <输出目录>/unpacked/module_<id>.js (仅代码模块)
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const acorn = require('acorn');
|
||||
const walk = require('acorn-walk');
|
||||
const prettier = require('prettier');
|
||||
|
||||
const BUNDLE_PATH = path.resolve(__dirname, '..', 'main.b7114b43.js');
|
||||
const OUT_DIR = path.resolve(__dirname, '..', '3dtank.com', 'play', 'static', 'js');
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
|
||||
// 自动取最新的 main.<hash>.js(排除输出目录里的加载器)
|
||||
function AutoDetectBundle() {
|
||||
const cands = fs.readdirSync(ROOT)
|
||||
.filter((f) => /^main\.[0-9a-f]+\.js$/.test(f))
|
||||
.map((f) => path.join(ROOT, f))
|
||||
.filter((f) => fs.statSync(f).isFile())
|
||||
.sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs);
|
||||
return cands[0] || null;
|
||||
}
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const flags = argv.filter((a) => a.startsWith('--'));
|
||||
const positional = argv.filter((a) => !a.startsWith('--'));
|
||||
const CLEAN = flags.indexOf('--clean') >= 0;
|
||||
const WITH_HORNET = flags.indexOf('--no-hornet') < 0;
|
||||
const LOADER_ONLY = flags.indexOf('--loader-only') >= 0; // 只重生成主文件,复用已有模块文件
|
||||
|
||||
const autoBundle = positional[0] ? null : AutoDetectBundle();
|
||||
const BUNDLE_PATH = path.resolve(positional[0] || autoBundle || '');
|
||||
const OUT_DIR = path.resolve(positional[1] || path.join(ROOT, '3dtank.com', 'play', 'static', 'js'));
|
||||
const UNPACKED_DIR = path.join(OUT_DIR, 'unpacked');
|
||||
const MAIN_PATH = path.join(OUT_DIR, 'main.b7114b43.js');
|
||||
const MAIN_PATH = BUNDLE_PATH ? path.join(OUT_DIR, path.basename(BUNDLE_PATH)) : '';
|
||||
const RESOURCES_JSON_PATH = path.join(UNPACKED_DIR, 'resources.json');
|
||||
|
||||
// UMD 包装模板
|
||||
const UMD_PREFIX = `var __script_base_url__=(typeof document!=="undefined"&&document.currentScript&&document.currentScript.src)?document.currentScript.src.replace(/[^\\/]+$/,""):"./";!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.Launcher=n():t.Launcher=n()}(globalThis,async()=>{`;
|
||||
const UMD_SUFFIX = `})//# sourceMappingURL=main.b7114b43.js.map\n`;
|
||||
const UMD_SUFFIX = (bundlePath) => `})//# sourceMappingURL=${path.basename(bundlePath)}.map\n`;
|
||||
|
||||
// 资源模块识别:标准格式 (t,n,i)=>{"use strict";t.exports=i.p+"path"}
|
||||
// 提取 path
|
||||
@@ -39,7 +64,32 @@ function tryExtractResource(valueSrc) {
|
||||
}
|
||||
|
||||
(async () => {
|
||||
if (!BUNDLE_PATH || !fs.existsSync(BUNDLE_PATH)) {
|
||||
console.error('找不到待切割的 bundle:' + (BUNDLE_PATH || '(未自动探测到 main.<hash>.js)'));
|
||||
process.exit(1);
|
||||
}
|
||||
if (!fs.existsSync(UNPACKED_DIR)) fs.mkdirSync(UNPACKED_DIR, { recursive: true });
|
||||
|
||||
if (CLEAN) {
|
||||
console.log('[0/7] 清理旧产物...');
|
||||
let removed = 0;
|
||||
for (const dir of [UNPACKED_DIR, OUT_DIR]) {
|
||||
if (!fs.existsSync(dir)) continue;
|
||||
for (const f of fs.readdirSync(dir)) {
|
||||
const isModule = /^module_\d+\.js$/.test(f);
|
||||
const isResources = f === 'resources.json';
|
||||
const isOldMain = /^main\.[0-9a-f]+\.js$/.test(f) && path.join(dir, f) !== MAIN_PATH;
|
||||
if (isModule || isResources || isOldMain) {
|
||||
fs.unlinkSync(path.join(dir, f));
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(` 已删除 ${removed} 个旧文件(保留 hornet_*.js)`);
|
||||
}
|
||||
|
||||
console.log('[1/7] 读取原 bundle...');
|
||||
console.log(` 文件: ${BUNDLE_PATH}`);
|
||||
const source = fs.readFileSync(BUNDLE_PATH, 'utf8');
|
||||
console.log(` 大小: ${(source.length / 1024 / 1024).toFixed(2)} MB`);
|
||||
|
||||
@@ -107,6 +157,12 @@ function tryExtractResource(valueSrc) {
|
||||
const paddedId = typeof keyNum === 'number' ? String(keyNum).padStart(5, '0') : keyRaw;
|
||||
const idLiteral = typeof keyNum === 'number' ? keyNum : JSON.stringify(keyRaw);
|
||||
|
||||
if (LOADER_ONLY && fs.existsSync(path.join(UNPACKED_DIR, `module_${paddedId}.js`))) {
|
||||
codeModuleIds.push(keyNum);
|
||||
codeCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
let beautified = valueSrc;
|
||||
try {
|
||||
beautified = await prettier.format('(' + valueSrc + ')', {
|
||||
@@ -143,20 +199,24 @@ function tryExtractResource(valueSrc) {
|
||||
console.log(` 完成。资源模块: ${resourceCount}, 代码模块: ${codeCount}, 美化失败: ${failCount}`);
|
||||
|
||||
console.log('[4/7] 写入 resources.json...');
|
||||
fs.writeFileSync(
|
||||
RESOURCES_JSON_PATH,
|
||||
JSON.stringify(
|
||||
{
|
||||
type: 'webpack-resource-map',
|
||||
generatedAt: new Date().toISOString(),
|
||||
totalResources: resourceCount,
|
||||
resourceMap,
|
||||
},
|
||||
null,
|
||||
0
|
||||
),
|
||||
'utf8'
|
||||
);
|
||||
if (LOADER_ONLY && fs.existsSync(RESOURCES_JSON_PATH)) {
|
||||
console.log(' --loader-only:复用已有 resources.json');
|
||||
} else {
|
||||
fs.writeFileSync(
|
||||
RESOURCES_JSON_PATH,
|
||||
JSON.stringify(
|
||||
{
|
||||
type: 'webpack-resource-map',
|
||||
generatedAt: new Date().toISOString(),
|
||||
totalResources: resourceCount,
|
||||
resourceMap,
|
||||
},
|
||||
null,
|
||||
0
|
||||
),
|
||||
'utf8'
|
||||
);
|
||||
}
|
||||
console.log(` resources.json 大小: ${(fs.statSync(RESOURCES_JSON_PATH).size / 1024).toFixed(1)} KB`);
|
||||
|
||||
console.log('[5/7] 提取原 webpack runtime...');
|
||||
@@ -170,7 +230,7 @@ function tryExtractResource(valueSrc) {
|
||||
}
|
||||
console.log(` runtime 代码长度: ${runtimeCode.length} 字符`);
|
||||
|
||||
console.log('[6/7] 生成主文件 main.b7114b43.js...');
|
||||
console.log(`[6/7] 生成主文件 ${path.basename(BUNDLE_PATH)}...`);
|
||||
const codeIdsArrayLiteral = codeModuleIds
|
||||
.map((id) => (typeof id === 'number' ? id : JSON.stringify(id)))
|
||||
.join(',');
|
||||
@@ -211,14 +271,49 @@ await Promise.all(__code_ids__.map(async (__id__) => {
|
||||
var __webpack_modules__ = window.__modules__;
|
||||
`;
|
||||
|
||||
// 6.3 hornet 工具链自动加载(注入在 runtime 入口模块加载之后、return 之前)
|
||||
const hornetCode = WITH_HORNET
|
||||
? `
|
||||
// ===== 暴露 webpack require(hornet 工具链 hook_gamemode/hornet_lib 依赖)=====
|
||||
window.__wp_require__ = __webpack_require__;
|
||||
|
||||
// ===== 加载 hornet_bundle(默认关闭:URL 加 ?hornet=1 才加载,避免日志遮挡)=====
|
||||
if (/[?&]hornet=1(&|$)/.test(location.search)) {
|
||||
try {
|
||||
const __hornet_url__ = __base_url__ + 'unpacked/hornet_bundle.js';
|
||||
const __hornet_resp__ = await fetch(__hornet_url__);
|
||||
if (__hornet_resp__.ok) {
|
||||
const __hornet_code__ = await __hornet_resp__.text();
|
||||
(0, eval)(__hornet_code__);
|
||||
console.log('[Hornet] bundle 已加载');
|
||||
} else {
|
||||
console.warn('[Hornet] bundle 加载失败: HTTP ' + __hornet_resp__.status);
|
||||
}
|
||||
} catch (__hornet_err__) {
|
||||
console.error('[Hornet] bundle 加载异常:', __hornet_err__);
|
||||
}
|
||||
}
|
||||
|
||||
`
|
||||
: '';
|
||||
let runtimeWithHornet = runtimeCode;
|
||||
if (WITH_HORNET) {
|
||||
const retIdx = runtimeWithHornet.lastIndexOf('return __webpack_exports__');
|
||||
if (retIdx >= 0) {
|
||||
runtimeWithHornet = runtimeWithHornet.slice(0, retIdx) + hornetCode + runtimeWithHornet.slice(retIdx);
|
||||
} else {
|
||||
console.warn(' [WARN] 未找到 return __webpack_exports__,跳过 hornet 注入');
|
||||
}
|
||||
}
|
||||
|
||||
const newBundle =
|
||||
UMD_PREFIX +
|
||||
'\n' +
|
||||
loaderCode +
|
||||
'\n// ===== 原 webpack runtime (一字未改) =====\n' +
|
||||
runtimeCode +
|
||||
runtimeWithHornet +
|
||||
';\n' +
|
||||
UMD_SUFFIX;
|
||||
UMD_SUFFIX(BUNDLE_PATH);
|
||||
|
||||
fs.writeFileSync(MAIN_PATH, newBundle, 'utf8');
|
||||
console.log(` 主文件大小: ${(newBundle.length / 1024).toFixed(1)} KB`);
|
||||
|
||||
Reference in new issue
Block a user