UNPKG

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