From 438a3230386646d850b8b57d88f210a155c65dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=8D=E7=BC=96=E8=AF=91=E5=B7=A5=E4=BD=9C=E5=8C=BA?= Date: Wed, 29 Apr 2026 11:49:31 +0800 Subject: [PATCH] feat(unpack-webpack): add webpack bundle unpacker script to frontend-source/scripts Former-commit-id: 9b8bf61302736531deffa87de927f7d77a70ae0c --- frontend-source/scripts/unpack-webpack.js | 92 +++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100755 frontend-source/scripts/unpack-webpack.js diff --git a/frontend-source/scripts/unpack-webpack.js b/frontend-source/scripts/unpack-webpack.js new file mode 100755 index 00000000..3f30fef7 --- /dev/null +++ b/frontend-source/scripts/unpack-webpack.js @@ -0,0 +1,92 @@ +#!/usr/bin/env node +/** + * webpack bundle 解包器 + * 识别 __webpack_modules__ 结构,按模块 ID 拆分为独立文件 + * + * 用法: node unpack-webpack.js + */ + +const fs = require('fs'); +const path = require('path'); + +function unpackWebpackBundle(bundlePath, outputDir) { + const code = fs.readFileSync(bundlePath, 'utf-8'); + + let modules = null; + + // 模式1: __webpack_modules__ = { 123: function(module, exports, __webpack_require__) {...}, ... } + const modulesAssignMatch = code.match(/__webpack_modules__\s*=\s*(\{[\s\S]*?\});/); + // 模式2: (function(modules) { ... })({ 123: function(...) {...}, ... }) + const iifeMatch = code.match(/\(function\(\s*\w+\s*\)\s*\{[\s\S]*?\}\s*\)\s*\(\s*(\{[\s\S]*?\})\s*\)\s*;/); + + if (modulesAssignMatch) { + modules = eval('(' + modulesAssignMatch[1] + ')'); + } else if (iifeMatch) { + modules = eval('(' + iifeMatch[1] + ')'); + } else { + console.error('未识别到 webpack 模块结构,尝试按 function(module, exports) 正则提取'); + modules = extractByRegex(code); + } + + if (!modules || Object.keys(modules).length === 0) { + console.error('未能提取任何模块'); + process.exit(1); + } + + fs.mkdirSync(outputDir, { recursive: true }); + + let count = 0; + for (const [id, factory] of Object.entries(modules)) { + const moduleCode = typeof factory === 'function' + ? factory.toString() + : JSON.stringify(factory, null, 2); + + const safeId = String(id).replace(/[^a-zA-Z0-9_-]/g, '_'); + fs.writeFileSync(path.join(outputDir, `module_${safeId}.js`), moduleCode); + count++; + } + + console.log(`拆解完成: ${count} 个模块 → ${outputDir}`); + return count; +} + +function extractByRegex(code) { + const modules = {}; + const re = /(\d+):\s*function\s*\(\s*\w+\s*,\s*\w+\s*,\s*\w+\s*\)\s*\{/g; + + let match; + const matches = []; + + while ((match = re.exec(code)) !== null) { + matches.push({ id: match[1], start: match.index }); + } + + for (let i = 0; i < matches.length; i++) { + const current = matches[i]; + const next = matches[i + 1]; + const startIdx = current.start; + const endIdx = next ? next.start : code.length; + + let depth = 1; + let pos = startIdx + current[0].length; + while (depth > 0 && pos < endIdx) { + if (code[pos] === '{') depth++; + if (code[pos] === '}') depth--; + pos++; + } + + const funcBody = code.substring(startIdx, pos); + modules[current.id] = funcBody; + } + + return modules; +} + +// CLI +const args = process.argv.slice(2); +if (args.length < 2) { + console.log('用法: node unpack-webpack.js '); + process.exit(1); +} + +unpackWebpackBundle(args[0], args[1]);