├── 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 |
--------------------------------------------------------------------------------