├── test.sh ├── index.html ├── README.md ├── .eslintrc ├── lib ├── check.js ├── index.js ├── read_module.js └── read_module_names.js ├── .gitignore ├── package.json ├── index.js └── modules └── sm.js /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -rf ./tests/a && ./index.js unpack ./tests/s.js ./tests/a sm && webpack ./tests/a/index ./tests/bundle.js 4 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unwebpack 2 | unwebpack webpacked bundle 3 | 4 | ### install 5 | 6 | `npm i -g unwebpack` 7 | 8 | 9 | ### usage 10 | 11 | `unwebpack unpack xxx.js path/to/folder [modules]` 12 | 13 | e.g. `unwebpack unpack s.js /tom/targetFolder module1 module2` 14 | 15 | modules is your own code handler in folder modules, see 'modules/sm.js' for example. -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | "_": true, 8 | "Promise": true 9 | }, 10 | "parserOptions": { 11 | "sourceType": "module" 12 | }, 13 | "extends": "airbnb/legacy", 14 | "rules": { 15 | "no-new": 0, 16 | "no-param-reassign": 0, 17 | "func-names": 0, 18 | "no-underscore-dangle": 0, 19 | "max-len": 0 20 | } 21 | } -------------------------------------------------------------------------------- /lib/check.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // Require modules check it works well or not 6 | let errorCount = 0; 7 | 8 | function check(maps, targetDir) { 9 | maps.forEach(item => { 10 | try { 11 | require(path.join('..', targetDir, item.name)); 12 | } catch (e) { 13 | errorCount += 1; 14 | if (errorCount < 5) { 15 | console.log(item.name, 'module load error', e.message, e.stack); 16 | } 17 | } 18 | }); 19 | 20 | console.log(errorCount, 'modules test error,', maps.length, 'total'); 21 | } 22 | 23 | module.exports = check; 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | tests/* -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint global-require:0 */ 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const readModuleNames = require('./read_module_names'); 8 | const readModule = require('./read_module'); 9 | const check = require('./check'); 10 | 11 | // Main function 12 | function unpack(content, targetDir, modules) { 13 | const ms = (modules || []).map(m => require('../modules/' + m)); 14 | 15 | const names = readModuleNames(content); 16 | let codes = names.map(item => { 17 | return { 18 | number: item.number, 19 | name: item.name, 20 | code: readModule(content, item.number, names) }; 21 | }); 22 | 23 | ms.forEach(m => { 24 | codes = m(codes); 25 | }); 26 | 27 | codes.forEach(code => { 28 | fs.writeFileSync(path.join(targetDir, code.name + '.js'), code.code); 29 | }); 30 | 31 | check(names, targetDir); 32 | 33 | console.log('done'); 34 | } 35 | 36 | module.exports = unpack; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unwebpack", 3 | "version": "1.0.3", 4 | "description": "unwebpack webpacked bundle", 5 | "main": "index.js", 6 | "bin": { 7 | "unwebpack": "./index.js" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/wanming/unwebpack.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/wanming/unwebpack/issues" 20 | }, 21 | "homepage": "https://github.com/wanming/unwebpack#readme", 22 | "devDependencies": { 23 | "eslint": "^3.12.2", 24 | "eslint-config-airbnb": "^13.0.0", 25 | "eslint-plugin-import": "^2.2.0", 26 | "eslint-plugin-jsx-a11y": "^2.2.3", 27 | "eslint-plugin-react": "^6.8.0" 28 | }, 29 | "dependencies": { 30 | "commander": "^2.9.0", 31 | "js-beautify": "^1.6.8", 32 | "lodash": "^4.17.4" 33 | } 34 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* eslint no-console: 0 */ 4 | 5 | 'use strict'; 6 | 7 | const program = require('commander'); 8 | const pkg = require('./package'); 9 | const fs = require('fs'); 10 | const unpack = require('./lib'); 11 | 12 | program 13 | .version(pkg.version) 14 | .command('unpack [modules...]') 15 | .action(function (source, _targetDir, modules) { 16 | if (!fs.existsSync(source)) { 17 | throw new Error('source file not exists'); 18 | } 19 | 20 | const targetDir = _targetDir || './'; 21 | 22 | if (!fs.existsSync(targetDir)) { 23 | fs.mkdirSync(targetDir); 24 | } 25 | 26 | if (fs.readdirSync(targetDir).length > 2) { 27 | throw new Error('targetDir must be empty'); 28 | } 29 | 30 | console.log('Target folder is', targetDir); 31 | 32 | const content = fs.readFileSync(source).toString(); 33 | unpack(content, targetDir, modules); 34 | }); 35 | 36 | program.parse(process.argv); 37 | -------------------------------------------------------------------------------- /lib/read_module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const formatter = require('js-beautify').js_beautify; 4 | 5 | function readModule(content, moduleNumber, maps) { 6 | const lines = content.split('\n'); 7 | 8 | let startLine = lines.findIndex(line => line.indexOf(`/* ${moduleNumber} */`) > -1); 9 | let endLine = lines.findIndex(line => line.indexOf(`/* ${Number(moduleNumber) + 1} */`) > -1); 10 | 11 | if (endLine === -1) { 12 | endLine = lines.length - 3; 13 | } 14 | 15 | // Remove wrapped function 16 | startLine += 2; 17 | endLine -= 1; 18 | let moduleContent = [`// unwebpack module number: ${moduleNumber}\n`] 19 | .concat(lines.slice(startLine, endLine)) 20 | .concat(['']) 21 | .join('\n'); 22 | 23 | moduleContent = moduleContent.replace(/__webpack_require__\((\d+)\)/g, function replace(match, p1) { 24 | const mo = maps.find(i => i.number === Number(p1)); 25 | return `require('./${mo.name}')`; 26 | }); 27 | return formatter(moduleContent, { indent_size: 2 }); 28 | } 29 | 30 | module.exports = readModule; 31 | -------------------------------------------------------------------------------- /modules/sm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function custormize(codes) { 4 | const index = codes.find(i => i.number === 0); 5 | const replaceString = `require('./${index.name}').`; 6 | 7 | return codes.map(item => { 8 | if (item.number !== 0) { 9 | item.code = item.code.replace(/GC\.Spread\./g, replaceString); 10 | } 11 | 12 | if (item.number === 0) { 13 | item.code = item.code.replace('GC = GC || {};', 'var GC = GC || {};'); 14 | } 15 | 16 | // Replace "require('./index').Sheets.Bindings;" to "require('./Bindings');" 17 | item.code = item.code.split('\n').map(line => { 18 | return line.replace( 19 | /require\('\.\/index'\)\.([A-Za-z0-9_]+)(\.([A-Za-z0-9_]+))?;/, 20 | (str, a1, dotA2, a2) => { 21 | if (a2) { 22 | return `require('./${a2}');`; 23 | } 24 | if (a1) { 25 | return `require('./${a1}');`; 26 | } 27 | 28 | return null; 29 | } 30 | ); 31 | }).join('\n'); 32 | 33 | return item; 34 | }); 35 | } 36 | 37 | module.exports = custormize; 38 | -------------------------------------------------------------------------------- /lib/read_module_names.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint no-cond-assign: 0 */ 4 | 5 | const _ = require('lodash'); 6 | 7 | // GC.Spread = __webpack_require__(1); 8 | const regexp = /\b(([a-zA-Z_0-9.]+)\s+=\s+)?__webpack_require__\((\d+)\)[^.]/g; 9 | 10 | // return map of mudule name and num like: 11 | // { 'fileHelper': 1, 'testHelper': 2 } 12 | function readModules(content) { 13 | // { 1: ['name1', 'nema2', ...] } 14 | const dirtyResult = {}; 15 | let match; 16 | while (match = regexp.exec(content)) { 17 | const arr = match.slice(1); 18 | const name = arr[1]; 19 | const number = arr[2]; 20 | 21 | if (!dirtyResult[number]) { 22 | dirtyResult[number] = []; 23 | } 24 | 25 | if (dirtyResult[number].indexOf(name) === -1) { 26 | dirtyResult[number].push(name); 27 | } 28 | } 29 | 30 | // Remove useless named undefined or 'module.exports' 31 | Object.keys(dirtyResult).forEach(key => { 32 | dirtyResult[key] = dirtyResult[key].filter(i => { 33 | return i && 34 | i !== 'module.exports' && 35 | i !== 'module' && 36 | i !== 'exports'; 37 | }); 38 | if (dirtyResult[key].length === 0) { 39 | dirtyResult[key] = ['Unnamed']; 40 | } 41 | }); 42 | 43 | 44 | // Check no matched module numbers 45 | const keys = Object.keys(dirtyResult).map(Number); 46 | const missed = []; 47 | for (let i = 0; i < Math.max(keys); i += 1) { 48 | if (!dirtyResult[i]) { 49 | missed.push(i); 50 | } 51 | } 52 | if (missed.length > 1) { 53 | console.warn('missed modules:', missed); 54 | } 55 | 56 | 57 | // Pick a best name for each module, 58 | // name undefined to name 'unnamedXX' 59 | 60 | // Avoid duplicated name (lower case) 61 | // format: { moduleName: 1(named time) } 62 | const namedModules = {}; 63 | const pushNameStack = name => { 64 | if (typeof namedModules[name] === 'undefined') { 65 | namedModules[name] = 0; 66 | } 67 | namedModules[name] += 1; 68 | }; 69 | 70 | // Return name like tom1, tom2, and so on 71 | const makeName = name => { 72 | pushNameStack(name); 73 | if (namedModules[name] === 1) { 74 | return name; 75 | } 76 | return name + namedModules[name]; 77 | }; 78 | 79 | const pickName = arr => { 80 | // Look for word without dot 81 | let ret = arr.find(i => i.indexOf('.') === -1); 82 | if (!ret) { 83 | ret = _.last(arr[0].split('.')); 84 | } 85 | return ret === 'index' ? 'index' : _.capitalize(_.camelCase(makeName(ret.toLowerCase()))); 86 | }; 87 | 88 | // Add index 89 | if (dirtyResult[0]) { 90 | dirtyResult[0].unshift('index'); 91 | } 92 | 93 | const result = Object.keys(dirtyResult).map(key => { 94 | return { 95 | // name: pickName(dirtyResult[key]) + '_' + key, 96 | name: pickName(dirtyResult[key]), 97 | number: Number(key), 98 | // Used for replacing namespaced vars like a.b.c 99 | aliases: dirtyResult[key].filter(i => i.indexOf('.') > 0) 100 | }; 101 | }); 102 | 103 | return result; 104 | } 105 | 106 | module.exports = readModules; 107 | --------------------------------------------------------------------------------