Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80499 views
1
var mdeps = require('module-deps');
2
var depsSort = require('deps-sort');
3
var bpack = require('browser-pack');
4
var insertGlobals = require('insert-module-globals');
5
var syntaxError = require('syntax-error');
6
7
var builtins = require('./lib/builtins.js');
8
9
var splicer = require('labeled-stream-splicer');
10
var through = require('through2');
11
var concat = require('concat-stream');
12
13
var inherits = require('inherits');
14
var EventEmitter = require('events').EventEmitter;
15
var xtend = require('xtend');
16
var isarray = require('isarray');
17
var defined = require('defined');
18
var has = require('has');
19
var sanitize = require('htmlescape').sanitize;
20
var shasum = require('shasum');
21
22
var bresolve = require('browser-resolve');
23
var resolve = require('resolve');
24
25
var readonly = require('read-only-stream');
26
27
module.exports = Browserify;
28
inherits(Browserify, EventEmitter);
29
30
var path = require('path');
31
var paths = {
32
empty: path.join(__dirname, 'lib/_empty.js')
33
};
34
35
function Browserify (files, opts) {
36
var self = this;
37
if (!(this instanceof Browserify)) return new Browserify(files, opts);
38
if (!opts) opts = {};
39
40
if (typeof files === 'string' || isarray(files) || isStream(files)) {
41
opts = xtend(opts, { entries: [].concat(opts.entries || [], files) });
42
}
43
else opts = xtend(files, opts);
44
45
self._options = opts;
46
if (opts.noparse) opts.noParse = opts.noparse;
47
48
if (opts.basedir !== undefined && typeof opts.basedir !== 'string') {
49
throw new Error('opts.basedir must be either undefined or a string.');
50
}
51
52
self._external = [];
53
self._exclude = [];
54
self._ignore = [];
55
self._expose = {};
56
self._hashes = {};
57
self._pending = 0;
58
self._transformOrder = 0;
59
self._transformPending = 0;
60
self._transforms = [];
61
self._entryOrder = 0;
62
self._ticked = false;
63
self._bresolve = opts.browserField === false
64
? function (id, opts, cb) {
65
if (!opts.basedir) opts.basedir = path.dirname(opts.filename)
66
resolve(id, opts, cb)
67
}
68
: bresolve
69
;
70
self._syntaxCache = {};
71
72
var ignoreTransform = [].concat(opts.ignoreTransform).filter(Boolean);
73
self._filterTransform = function (tr) {
74
if (Array.isArray(tr)) {
75
return ignoreTransform.indexOf(tr[0]) === -1;
76
}
77
return ignoreTransform.indexOf(tr) === -1;
78
};
79
80
self.pipeline = self._createPipeline(opts);
81
82
[].concat(opts.transform).filter(Boolean).filter(self._filterTransform)
83
.forEach(function (tr) {
84
self.transform(tr);
85
});
86
87
[].concat(opts.entries).filter(Boolean).forEach(function (file) {
88
self.add(file, { basedir: opts.basedir });
89
});
90
91
[].concat(opts.require).filter(Boolean).forEach(function (file) {
92
self.require(file, { basedir: opts.basedir });
93
});
94
95
[].concat(opts.plugin).filter(Boolean).forEach(function (p) {
96
self.plugin(p, { basedir: opts.basedir });
97
});
98
}
99
100
Browserify.prototype.require = function (file, opts) {
101
var self = this;
102
if (isarray(file)) {
103
file.forEach(function (x) {
104
if (typeof x === 'object') {
105
self.require(x.file, xtend(opts, x));
106
}
107
else self.require(x, opts);
108
});
109
return this;
110
}
111
112
if (!opts) opts = {};
113
var basedir = defined(opts.basedir, self._options.basedir, process.cwd());
114
var expose = opts.expose;
115
if (file === expose && /^[\.]/.test(expose)) {
116
expose = '/' + path.relative(basedir, expose);
117
}
118
if (expose === undefined && this._options.exposeAll) {
119
expose = true;
120
}
121
if (expose === true) {
122
expose = '/' + path.relative(basedir, file);
123
}
124
125
if (isStream(file)) {
126
self._pending ++;
127
var order = self._entryOrder ++;
128
file.pipe(concat(function (buf) {
129
var filename = opts.file || file.file || path.join(
130
basedir,
131
'_stream_' + order + '.js'
132
);
133
var id = file.id || expose || filename;
134
if (expose || opts.entry === false) {
135
self._expose[id] = filename;
136
}
137
if (!opts.entry && self._options.exports === undefined) {
138
self._bpack.hasExports = true;
139
}
140
var rec = {
141
source: buf.toString('utf8'),
142
entry: defined(opts.entry, false),
143
file: filename,
144
id: id
145
};
146
if (rec.entry) rec.order = order;
147
if (rec.transform === false) rec.transform = false;
148
self.pipeline.write(rec);
149
150
if (-- self._pending === 0) self.emit('_ready');
151
}));
152
return this;
153
}
154
155
var row;
156
if (typeof file === 'object') {
157
row = xtend(file, opts);
158
}
159
else if (isExternalModule(file)) {
160
// external module or builtin
161
row = xtend(opts, { id: expose || file, file: file });
162
}
163
else {
164
row = xtend(opts, { file: file });
165
}
166
167
if (!row.id) {
168
row.id = expose || file;
169
}
170
if (expose || !row.entry) {
171
// Make this available to mdeps so that it can assign the value when it
172
// resolves the pathname.
173
row.expose = row.id;
174
}
175
176
if (opts.external) return self.external(file, opts);
177
if (row.entry === undefined) row.entry = false;
178
179
if (!row.entry && self._options.exports === undefined) {
180
self._bpack.hasExports = true;
181
}
182
183
if (row.entry) {
184
row.file = path.resolve(basedir, row.file);
185
row.order = self._entryOrder ++;
186
}
187
188
if (opts.transform === false) row.transform = false;
189
self.pipeline.write(row);
190
return self;
191
};
192
193
Browserify.prototype.add = function (file, opts) {
194
var self = this;
195
if (!opts) opts = {};
196
if (isarray(file)) {
197
file.forEach(function (x) { self.add(x, opts) });
198
return this;
199
}
200
return this.require(file, xtend({ entry: true, expose: false }, opts));
201
};
202
203
Browserify.prototype.external = function (file, opts) {
204
var self = this;
205
if (isarray(file)) {
206
file.forEach(function (f) {
207
if (typeof f === 'object') {
208
self.external(f, xtend(opts, f));
209
}
210
else self.external(f, opts)
211
});
212
return this;
213
}
214
if (file && typeof file === 'object' && typeof file.bundle === 'function') {
215
var b = file;
216
self._pending ++;
217
218
var bdeps = {};
219
var blabels = {};
220
221
b.on('label', function (prev, id) {
222
self._external.push(id);
223
224
if (prev !== id) {
225
blabels[prev] = id;
226
self._external.push(prev);
227
}
228
});
229
230
b.pipeline.get('deps').push(through.obj(function (row, enc, next) {
231
bdeps = xtend(bdeps, row.deps);
232
this.push(row);
233
next();
234
}));
235
236
self.on('dep', function (row) {
237
Object.keys(row.deps).forEach(function (key) {
238
var prev = bdeps[key];
239
if (prev) {
240
var id = blabels[prev];
241
if (id) {
242
row.indexDeps[key] = id;
243
}
244
}
245
});
246
});
247
248
b.pipeline.get('label').once('end', function () {
249
if (-- self._pending === 0) self.emit('_ready');
250
});
251
return this;
252
}
253
254
if (!opts) opts = {};
255
var basedir = defined(opts.basedir, process.cwd());
256
this._external.push(file);
257
this._external.push('/' + path.relative(basedir, file));
258
return this;
259
};
260
261
Browserify.prototype.exclude = function (file, opts) {
262
if (!opts) opts = {};
263
var basedir = defined(opts.basedir, process.cwd());
264
this._exclude.push(file);
265
this._exclude.push('/' + path.relative(basedir, file));
266
return this;
267
};
268
269
Browserify.prototype.ignore = function (file, opts) {
270
if (!opts) opts = {};
271
var basedir = defined(opts.basedir, process.cwd());
272
273
// Handle relative paths
274
if (file[0] === '.') {
275
this._ignore.push(path.resolve(basedir, file));
276
}
277
else {
278
this._ignore.push(file);
279
}
280
return this;
281
};
282
283
Browserify.prototype.transform = function (tr, opts) {
284
var self = this;
285
if (typeof opts === 'function' || typeof opts === 'string') {
286
tr = [ opts, tr ];
287
}
288
if (isarray(tr)) {
289
opts = tr[1];
290
tr = tr[0];
291
}
292
293
//if the bundler is ignoring this transform
294
if (typeof tr === 'string' && !self._filterTransform(tr)) {
295
return this;
296
}
297
298
function resolved () {
299
self._transforms[order] = rec;
300
-- self._pending;
301
if (-- self._transformPending === 0) {
302
self._transforms.forEach(function (transform) {
303
self.pipeline.write(transform);
304
});
305
306
if (self._pending === 0) {
307
self.emit('_ready');
308
}
309
}
310
}
311
312
if (!opts) opts = {};
313
opts._flags = '_flags' in opts ? opts._flags : self._options;
314
315
var basedir = defined(opts.basedir, this._options.basedir, process.cwd());
316
var order = self._transformOrder ++;
317
self._pending ++;
318
self._transformPending ++;
319
320
var rec = {
321
transform: tr,
322
options: opts,
323
global: opts.global
324
};
325
326
if (typeof tr === 'string') {
327
var topts = {
328
basedir: basedir,
329
paths: (self._options.paths || []).map(function (p) {
330
return path.resolve(basedir, p);
331
})
332
};
333
resolve(tr, topts, function (err, res) {
334
if (err) return self.emit('error', err);
335
rec.transform = res;
336
resolved();
337
});
338
}
339
else process.nextTick(resolved);
340
return this;
341
};
342
343
Browserify.prototype.plugin = function (p, opts) {
344
if (isarray(p)) {
345
opts = p[1];
346
p = p[0];
347
}
348
if (!opts) opts = {};
349
var basedir = defined(opts.basedir, this._options.basedir, process.cwd());
350
if (typeof p === 'function') {
351
p(this, opts);
352
}
353
else {
354
var pfile = resolve.sync(String(p), { basedir: basedir })
355
var f = require(pfile);
356
if (typeof f !== 'function') {
357
throw new Error('plugin ' + p + ' should export a function');
358
}
359
f(this, opts);
360
}
361
return this;
362
};
363
364
Browserify.prototype._createPipeline = function (opts) {
365
var self = this;
366
if (!opts) opts = {};
367
this._mdeps = this._createDeps(opts);
368
this._mdeps.on('file', function (file, id) {
369
pipeline.emit('file', file, id);
370
self.emit('file', file, id);
371
});
372
this._mdeps.on('package', function (pkg) {
373
pipeline.emit('package', pkg);
374
self.emit('package', pkg);
375
});
376
this._mdeps.on('transform', function (tr, file) {
377
pipeline.emit('transform', tr, file);
378
self.emit('transform', tr, file);
379
});
380
381
var dopts = {
382
index: !opts.fullPaths && !opts.exposeAll,
383
dedupe: true,
384
expose: this._expose
385
};
386
this._bpack = bpack(xtend(opts, { raw: true }));
387
388
var pipeline = splicer.obj([
389
'record', [ this._recorder() ],
390
'deps', [ this._mdeps ],
391
'json', [ this._json() ],
392
'unbom', [ this._unbom() ],
393
'unshebang', [ this._unshebang() ],
394
'syntax', [ this._syntax() ],
395
'sort', [ depsSort(dopts) ],
396
'dedupe', [ this._dedupe() ],
397
'label', [ this._label(opts) ],
398
'emit-deps', [ this._emitDeps() ],
399
'debug', [ this._debug(opts) ],
400
'pack', [ this._bpack ],
401
'wrap', []
402
]);
403
if (opts.exposeAll) {
404
var basedir = defined(opts.basedir, process.cwd());
405
pipeline.get('deps').push(through.obj(function (row, enc, next) {
406
if (self._external.indexOf(row.id) >= 0) return next();
407
if (self._external.indexOf(row.file) >= 0) return next();
408
409
if (isAbsolutePath(row.id)) {
410
row.id = '/' + path.relative(basedir, row.file);
411
}
412
Object.keys(row.deps || {}).forEach(function (key) {
413
row.deps[key] = '/' + path.relative(basedir, row.deps[key]);
414
});
415
this.push(row);
416
next();
417
}));
418
}
419
return pipeline;
420
};
421
422
Browserify.prototype._createDeps = function (opts) {
423
var self = this;
424
var mopts = xtend(opts);
425
var basedir = defined(opts.basedir, process.cwd());
426
427
// Let mdeps populate these values since it will be resolving file paths
428
// anyway.
429
mopts.expose = this._expose;
430
mopts.extensions = [ '.js', '.json' ].concat(mopts.extensions || []);
431
self._extensions = mopts.extensions;
432
433
mopts.transform = [];
434
mopts.transformKey = [ 'browserify', 'transform' ];
435
mopts.postFilter = function (id, file, pkg) {
436
if (opts.postFilter && !opts.postFilter(id, file, pkg)) return false;
437
if (self._external.indexOf(file) >= 0) return false;
438
if (self._exclude.indexOf(file) >= 0) return false;
439
440
//filter transforms on module dependencies
441
if (pkg && pkg.browserify && pkg.browserify.transform) {
442
//In edge cases it may be a string
443
pkg.browserify.transform = [].concat(pkg.browserify.transform)
444
.filter(Boolean)
445
.filter(self._filterTransform);
446
}
447
return true;
448
};
449
mopts.filter = function (id) {
450
if (opts.filter && !opts.filter(id)) return false;
451
if (self._external.indexOf(id) >= 0) return false;
452
if (self._exclude.indexOf(id) >= 0) return false;
453
if (opts.bundleExternal === false && isExternalModule(id)) {
454
return false;
455
}
456
return true;
457
};
458
mopts.resolve = function (id, parent, cb) {
459
if (self._ignore.indexOf(id) >= 0) return cb(null, paths.empty, {});
460
461
self._bresolve(id, parent, function (err, file, pkg) {
462
if (file && self._ignore.indexOf(file) >= 0) {
463
return cb(null, paths.empty, {});
464
}
465
if (file && self._ignore.length) {
466
var nm = file.split('/node_modules/')[1];
467
if (nm) {
468
nm = nm.split('/')[0];
469
if (self._ignore.indexOf(nm) >= 0) {
470
return cb(null, paths.empty, {});
471
}
472
}
473
}
474
475
if (file) {
476
var ex = '/' + path.relative(basedir, file);
477
if (self._external.indexOf(ex) >= 0) {
478
return cb(null, ex);
479
}
480
if (self._exclude.indexOf(ex) >= 0) {
481
return cb(null, ex);
482
}
483
if (self._ignore.indexOf(ex) >= 0) {
484
return cb(null, paths.empty, {});
485
}
486
}
487
cb(err, file, pkg);
488
});
489
};
490
491
if (opts.builtins === false) {
492
mopts.modules = {};
493
self._exclude.push.apply(self._exclude, Object.keys(builtins));
494
}
495
else if (opts.builtins && isarray(opts.builtins)) {
496
mopts.modules = {};
497
opts.builtins.forEach(function (key) {
498
mopts.modules[key] = builtins[key];
499
});
500
}
501
else if (opts.builtins && typeof opts.builtins === 'object') {
502
mopts.modules = opts.builtins;
503
}
504
else mopts.modules = xtend(builtins);
505
506
Object.keys(builtins).forEach(function (key) {
507
if (!has(mopts.modules, key)) self._exclude.push(key);
508
});
509
510
mopts.globalTransform = [];
511
if (!this._bundled) {
512
this.once('bundle', function () {
513
self.pipeline.write({
514
transform: globalTr,
515
global: true,
516
options: {}
517
});
518
});
519
}
520
521
var no = [].concat(opts.noParse).filter(Boolean);
522
var absno = no.filter(function(x) {
523
return typeof x === 'string';
524
}).map(function (x) {
525
return path.resolve(basedir, x);
526
});
527
528
function globalTr (file) {
529
if (opts.detectGlobals === false) return through();
530
531
if (opts.noParse === true) return through();
532
if (no.indexOf(file) >= 0) return through();
533
if (absno.indexOf(file) >= 0) return through();
534
535
var parts = file.split('/node_modules/');
536
for (var i = 0; i < no.length; i++) {
537
if (typeof no[i] === 'function' && no[i](file)) {
538
return through();
539
}
540
else if (no[i] === parts[parts.length-1].split('/')[0]) {
541
return through();
542
}
543
else if (no[i] === parts[parts.length-1]) {
544
return through();
545
}
546
}
547
548
var vars = xtend({
549
process: function () { return "require('_process')" },
550
}, opts.insertGlobalVars);
551
552
if (opts.bundleExternal === false) {
553
delete vars.process;
554
delete vars.buffer;
555
}
556
557
return insertGlobals(file, xtend(opts, {
558
debug: opts.debug,
559
always: opts.insertGlobals,
560
basedir: opts.commondir === false
561
? '/'
562
: opts.basedir || process.cwd()
563
,
564
vars: vars
565
}));
566
}
567
return mdeps(mopts);
568
};
569
570
Browserify.prototype._recorder = function (opts) {
571
var self = this;
572
var ended = false;
573
this._recorded = [];
574
575
if (!this._ticked) {
576
process.nextTick(function () {
577
self._ticked = true;
578
self._recorded.forEach(function (row) {
579
stream.push(row);
580
});
581
if (ended) stream.push(null);
582
});
583
}
584
585
var stream = through.obj(write, end);
586
return stream;
587
588
function write (row, enc, next) {
589
self._recorded.push(row);
590
if (self._ticked) this.push(row);
591
next();
592
}
593
function end () {
594
ended = true;
595
if (self._ticked) this.push(null);
596
}
597
};
598
599
Browserify.prototype._json = function () {
600
return through.obj(function (row, enc, next) {
601
if (/\.json$/.test(row.file)) {
602
row.source = 'module.exports=' + sanitize(row.source);
603
}
604
this.push(row);
605
next();
606
});
607
};
608
609
Browserify.prototype._unbom = function () {
610
return through.obj(function (row, enc, next) {
611
if (/^\ufeff/.test(row.source)) {
612
row.source = row.source.replace(/^\ufeff/, '');
613
}
614
this.push(row);
615
next();
616
});
617
};
618
619
Browserify.prototype._unshebang = function () {
620
return through.obj(function (row, enc, next) {
621
if (/^#!/.test(row.source)) {
622
row.source = row.source.replace(/^#![^\n]*\n/, '');
623
}
624
this.push(row);
625
next();
626
});
627
};
628
629
Browserify.prototype._syntax = function () {
630
var self = this;
631
return through.obj(function (row, enc, next) {
632
var h = shasum(row.source);
633
if (typeof self._syntaxCache[h] === 'undefined') {
634
var err = syntaxError(row.source, row.file || row.id);
635
if (err) return this.emit('error', err);
636
self._syntaxCache[h] = true;
637
}
638
this.push(row);
639
next();
640
});
641
};
642
643
Browserify.prototype._dedupe = function () {
644
return through.obj(function (row, enc, next) {
645
if (!row.dedupeIndex && row.dedupe) {
646
row.source = 'arguments[4]['
647
+ JSON.stringify(row.dedupe)
648
+ '][0].apply(exports,arguments)'
649
;
650
row.nomap = true;
651
}
652
else if (row.dedupeIndex) {
653
row.source = 'arguments[4]['
654
+ JSON.stringify(row.dedupeIndex)
655
+ '][0].apply(exports,arguments)'
656
;
657
row.nomap = true;
658
}
659
if (row.dedupeIndex && row.indexDeps) {
660
row.indexDeps.dup = row.dedupeIndex;
661
}
662
this.push(row);
663
next();
664
});
665
};
666
667
Browserify.prototype._label = function (opts) {
668
var self = this;
669
var basedir = defined(opts.basedir, process.cwd());
670
671
return through.obj(function (row, enc, next) {
672
var prev = row.id;
673
674
if (self._external.indexOf(row.id) >= 0) return next();
675
if (self._external.indexOf('/' + path.relative(basedir, row.id)) >= 0) {
676
return next();
677
}
678
if (self._external.indexOf(row.file) >= 0) return next();
679
680
if (row.index) row.id = row.index;
681
682
self.emit('label', prev, row.id);
683
if (row.indexDeps) row.deps = row.indexDeps || {};
684
685
Object.keys(row.deps).forEach(function (key) {
686
if (self._expose[key]) {
687
row.deps[key] = key;
688
return;
689
}
690
691
var afile = path.resolve(path.dirname(row.file), key);
692
var rfile = '/' + path.relative(basedir, afile);
693
if (self._external.indexOf(rfile) >= 0) {
694
row.deps[key] = rfile;
695
}
696
if (self._external.indexOf(afile) >= 0) {
697
row.deps[key] = rfile;
698
}
699
if (self._external.indexOf(key) >= 0) {
700
row.deps[key] = key;
701
return;
702
}
703
704
for (var i = 0; i < self._extensions.length; i++) {
705
var ex = self._extensions[i];
706
if (self._external.indexOf(rfile + ex) >= 0) {
707
row.deps[key] = rfile + ex;
708
break;
709
}
710
}
711
});
712
713
if (row.entry || row.expose) {
714
self._bpack.standaloneModule = row.id;
715
}
716
this.push(row);
717
next();
718
});
719
};
720
721
Browserify.prototype._emitDeps = function () {
722
var self = this;
723
return through.obj(function (row, enc, next) {
724
self.emit('dep', row);
725
this.push(row);
726
next();
727
})
728
};
729
730
Browserify.prototype._debug = function (opts) {
731
var basedir = defined(opts.basedir, process.cwd());
732
return through.obj(function (row, enc, next) {
733
if (opts.debug) {
734
row.sourceRoot = 'file://localhost';
735
row.sourceFile = path.relative(basedir, row.file)
736
.replace(/\\/g, '/');
737
}
738
this.push(row);
739
next();
740
});
741
};
742
743
Browserify.prototype.reset = function (opts) {
744
if (!opts) opts = {};
745
var hadExports = this._bpack.hasExports;
746
this.pipeline = this._createPipeline(xtend(opts, this._options));
747
this._bpack.hasExports = hadExports;
748
this._entryOrder = 0;
749
this._bundled = false;
750
this.emit('reset');
751
};
752
753
Browserify.prototype.bundle = function (cb) {
754
var self = this;
755
if (cb && typeof cb === 'object') {
756
throw new Error(
757
'bundle() no longer accepts option arguments.\n'
758
+ 'Move all option arguments to the browserify() constructor.'
759
);
760
}
761
if (this._bundled) {
762
var recorded = this._recorded;
763
this.reset();
764
recorded.forEach(function (x) {
765
self.pipeline.write(x);
766
});
767
}
768
var output = readonly(this.pipeline);
769
if (cb) {
770
output.on('error', cb);
771
this.pipeline.pipe(concat(function (body) {
772
cb(null, body);
773
}));
774
}
775
776
function ready () {
777
self.emit('bundle', output);
778
self.pipeline.end();
779
}
780
781
if (this._pending === 0) ready();
782
else this.once('_ready', ready);
783
784
this._bundled = true;
785
return output;
786
};
787
788
function isStream (s) { return s && typeof s.pipe === 'function' }
789
function isAbsolutePath (file) {
790
var regexp = process.platform === 'win32' ?
791
/^\w:/ :
792
/^\//;
793
return regexp.test(file);
794
}
795
function isExternalModule (file) {
796
var regexp = process.platform === 'win32' ?
797
/^(\.|\w:)/ :
798
/^[\/.]/;
799
return !regexp.test(file);
800
}
801
802