1 | if (process.env.NODE_ENV === 'development') {
|
2 | require('longjohn');
|
3 | }
|
4 |
|
5 | var fs = require('fs');
|
6 | var path = require('path');
|
7 | var crypto = require('crypto');
|
8 | var esprima = require('esprima');
|
9 | var resolve = require('resolve');
|
10 | var browserResolve = require('browser-resolve');
|
11 | var events = require('events');
|
12 |
|
13 | var core = {
|
14 | "assert": "assert",
|
15 | "buffer_ieee754": "buffer-browserify/buffer_ieee754",
|
16 | "buffer": "buffer-browserify",
|
17 | "child_process": "",
|
18 | "cluster": "",
|
19 | "console": "console-browserify",
|
20 | "constants": "constants-browserify",
|
21 | "crypto": "crypto-browserify",
|
22 | "_debugger": "",
|
23 | "dgram": "",
|
24 | "dns": "",
|
25 | "domain": "",
|
26 | "events": "events",
|
27 | "freelist": "",
|
28 | "fs": "browserify-fs",
|
29 | "http": "http-browserify",
|
30 | "https": "https-browserify",
|
31 | "_linklist": "",
|
32 | "module": "",
|
33 | "net": "",
|
34 | "os": "os-browserify",
|
35 | "path": "path-browserify",
|
36 | "punycode": "punycode",
|
37 | "querystring": "querystring",
|
38 | "readline": "",
|
39 | "repl": "",
|
40 | "stream": "stream-browserify",
|
41 | "string_decoder": "string_decoder",
|
42 | "sys": "",
|
43 | "timers": "timers-browserify",
|
44 | "tls": "",
|
45 | "tty": "tty-browserify",
|
46 | "url": "url",
|
47 | "util": "util",
|
48 | "vm": "vm-browserify",
|
49 | "zlib": "browserify-zlib"
|
50 | };
|
51 |
|
52 | var mainHeader = function (main, globalVal) {
|
53 | globalVal = typeof global !== 'undefined' ? global : globalVal;
|
54 | var cached = {};
|
55 | var loading = {};
|
56 |
|
57 | function bundleRequire(id) {
|
58 | var bundle = main[id];
|
59 | if (!bundle) {
|
60 | throw new Error('Invalid module id ' + id);
|
61 | }
|
62 | if (cached[id]) {
|
63 | return cached[id];
|
64 | }
|
65 | if (loading[id]) {
|
66 | return loading[id];
|
67 | }
|
68 | var module = {exports: loading[id] = {}};
|
69 | bundle[0].call(module.exports, module.exports, function bundlerRequire(moduleName) {
|
70 | if (typeof core != 'undefined' && core.indexOf(moduleName) > -1) {
|
71 | return require(moduleName);
|
72 | }
|
73 | return bundleRequire(bundle[1][moduleName] || moduleName);
|
74 | }, module, globalVal);
|
75 | loading[id] = null;
|
76 | return cached[id] = module.exports;
|
77 | }
|
78 |
|
79 | bundleRequire(0);
|
80 | }.toString();
|
81 |
|
82 | function Bundler(options) {
|
83 | events.EventEmitter.call(this);
|
84 | var defaults = {
|
85 | directory: process.cwd(),
|
86 | strict: false,
|
87 | web: false,
|
88 | extensions: ['.js', '.json'],
|
89 | out: null,
|
90 | wait: 1
|
91 | };
|
92 | if (options) {
|
93 | for (var property in defaults) {
|
94 | if (defaults.hasOwnProperty(property) && options.hasOwnProperty(property)) {
|
95 | defaults[property] = options[property];
|
96 | }
|
97 | }
|
98 | }
|
99 | this.options = defaults;
|
100 | }
|
101 |
|
102 | Bundler.prototype = events.EventEmitter.prototype;
|
103 |
|
104 | Bundler.prototype.files = {};
|
105 |
|
106 | Bundler.prototype.addFile = function (file, directory) {
|
107 | if (!fs.existsSync(file)) {
|
108 | throw new Error('File ' + file + ' not found');
|
109 | }
|
110 | if (!directory) {
|
111 | file = path.join(this.options.directory, file);
|
112 | this.main = file;
|
113 | }
|
114 | var bundled = this.files[file] = new BundledFile(file, directory || '', this);
|
115 | bundled.checkContents();
|
116 | this.needsRebundle();
|
117 | };
|
118 |
|
119 | Bundler.prototype.reloadFile = function (file, directory) {
|
120 | var bundled = this.files[file];
|
121 | if (bundled) {
|
122 | bundled.checkContents();
|
123 | } else {
|
124 | this.addFile(file, directory);
|
125 | }
|
126 | };
|
127 |
|
128 | Bundler.prototype.loadDependencies = function (contents, file, directory) {
|
129 | var tokens;
|
130 | try {
|
131 | tokens = esprima.tokenize(contents);
|
132 | } catch (e) {
|
133 | console.error('Failed to tokenize ' + file, e.stack);
|
134 | return [];
|
135 | }
|
136 | var dependencies = [];
|
137 | for (var i = 0, length = tokens.length; i < length; i++) {
|
138 | var token = tokens[i];
|
139 | if (token.type === 'Identifier' && token.value === 'require' && i < length - 3) {
|
140 |
|
141 | var next = tokens[i + 1];
|
142 | if (next.type !== 'Punctuator' || next.value !== '(') {
|
143 | continue;
|
144 | }
|
145 | next = tokens[i + 3];
|
146 | if (next.type !== 'Punctuator' || next.value !== ')') {
|
147 |
|
148 | while (next.type !== 'String' && next.value !== ')' && i < length - 3) {
|
149 | next = tokens[++i];
|
150 | }
|
151 | i -= 2;
|
152 | }
|
153 | next = tokens[i + 2];
|
154 | if (next.type !== 'String') {
|
155 | continue;
|
156 | }
|
157 | var value = next.value.replace(/['"]+/g, '');
|
158 | if (resolve.isCore(value)) {
|
159 | if (this.options.web) {
|
160 |
|
161 | var specialModule = core[value];
|
162 | if (specialModule.length === 0) {
|
163 | throw new Error('There is no web version of the core module ' + value + '!');
|
164 | }
|
165 | var module = resolve.sync(specialModule, {basedir: __dirname});
|
166 | if (!this.files[module]) {
|
167 | this.reloadFile(module, path.dirname(module));
|
168 | }
|
169 | dependencies.push({module: module, value: value});
|
170 | } else {
|
171 | dependencies.push(value);
|
172 | }
|
173 | } else {
|
174 | try {
|
175 | var module;
|
176 | if (this.options.web) {
|
177 | module = browserResolve.sync(value, {filename: file, extensions: this.options.extensions});
|
178 | } else {
|
179 | module = resolve.sync(value, {basedir: directory || this.options.directory, extensions: this.options.extensions});
|
180 | }
|
181 | if (!this.files[module]) {
|
182 | this.reloadFile(module, path.dirname(module));
|
183 | }
|
184 | dependencies.push({module: module, value: value});
|
185 | } catch (e) {
|
186 | console.error('Failed to load module', value, e.message);
|
187 | }
|
188 | }
|
189 | }
|
190 | }
|
191 | return dependencies;
|
192 | };
|
193 |
|
194 | Bundler.prototype.needsRebundle = function () {
|
195 | if (this._bundling) {
|
196 | return;
|
197 | }
|
198 | this._bundling = true;
|
199 | setTimeout(() => {
|
200 | this._bundling = false;
|
201 | this.bundle();
|
202 | }, this.options.wait);
|
203 | };
|
204 |
|
205 | Bundler.prototype.bundle = function () {
|
206 | var start = Date.now();
|
207 | this.emit('bundleStart', start);
|
208 | var array = [];
|
209 | var i = 0;
|
210 | var reverseIds = {};
|
211 |
|
212 | for (var property in this.files) {
|
213 | if (this.files.hasOwnProperty(property)) {
|
214 | if (reverseIds[property]) {
|
215 | continue;
|
216 | }
|
217 | reverseIds[property] = i++;
|
218 | }
|
219 | }
|
220 |
|
221 | for (var property in this.files) {
|
222 | if (this.files.hasOwnProperty(property)) {
|
223 |
|
224 | var bundled = this.files[property];
|
225 | var dependencies = {};
|
226 |
|
227 | for (var j = 0, length = bundled.dependencies.length; j < length; j++) {
|
228 | var dependency = bundled.dependencies[j];
|
229 | if (typeof dependency !== 'string') {
|
230 | dependencies[dependency.value] = reverseIds[dependency.module];
|
231 | }
|
232 | }
|
233 |
|
234 | var event = {
|
235 | file: property,
|
236 | content: bundled.contents
|
237 | };
|
238 | if (bundled.changed) {
|
239 | this.emit('bundleFile', event);
|
240 | bundled.changed = false;
|
241 | }
|
242 | if (path.extname(event.file) === '.json') {
|
243 | array.push(reverseIds[bundled.file] + `: [function (exports, require, module, global) {module.exports = ${event.content}}, ${JSON.stringify(dependencies)}]`);
|
244 | } else {
|
245 | array.push(reverseIds[bundled.file] + `: [function (exports, require, module, global) {${event.content}}, ${JSON.stringify(dependencies)}]`);
|
246 | }
|
247 | }
|
248 | }
|
249 | var header = mainHeader;
|
250 | header = header.replace('/*main*/', this.main);
|
251 | header = header.replace('/*core*/', this.options.web ? '' : 'var core = ' + JSON.stringify(Object.keys(core)) + ';');
|
252 |
|
253 | var text = '(' + header + ')({' + array.join(',') + '}, this);';
|
254 | if (this.options.strict) {
|
255 | text = '\'use strict\';\n' + text;
|
256 | }
|
257 |
|
258 | var event = {content: text, time: Date.now() - start, files: array.length};
|
259 | this.emit('bundle', event);
|
260 | if (typeof this.options.out === 'string') {
|
261 | fs.writeFileSync(this.options.out, event.content);
|
262 | } else if (this.options.out) {
|
263 | this.options.out(event.content);
|
264 | } else {
|
265 | process.stdout.write(event.content);
|
266 | }
|
267 | };
|
268 |
|
269 | module.exports = Bundler;
|
270 |
|
271 | function BundledFile(file, directory, bundler) {
|
272 | this.file = file;
|
273 | this.directory = directory;
|
274 | this.bundler = bundler;
|
275 | this.changed = false;
|
276 | }
|
277 |
|
278 | BundledFile.prototype.checkContents = function () {
|
279 | try {
|
280 | var contents = fs.readFileSync(this.file);
|
281 | var newHash;
|
282 | if (!this.contents || this.contents.length != contents.length || this.hash != (newHash = calculateHash(contents))) {
|
283 | this.hash = newHash;
|
284 | this.contents = contents;
|
285 | this.changed = true;
|
286 | var event = {contents: this.contents, file: this.file};
|
287 | this.bundler.emit('fileUpdate', event);
|
288 | this.contents = event.contents;
|
289 | this.dependencies = this.bundler.loadDependencies(this.contents, this.file, this.directory);
|
290 | this.bundler.needsRebundle();
|
291 | }
|
292 | } catch (e) {
|
293 | e.message = this.file + '\n' + (e.message || '');
|
294 | throw e;
|
295 | }
|
296 | };
|
297 |
|
298 | function calculateHash(contents) {
|
299 | return crypto.createHash('sha512').update(contents).digest('hex');
|
300 | }
|