node_modules/browserify/index.js in sprockets-browserify-0.1.2 vs node_modules/browserify/index.js in sprockets-browserify-0.2.0

- old
+ new

@@ -1,202 +1,365 @@ +var crypto = require('crypto'); +var through = require('through'); +var duplexer = require('duplexer'); +var concatStream = require('concat-stream'); +var checkSyntax = require('syntax-error'); + +var mdeps = require('module-deps'); +var browserPack = require('browser-pack'); +var browserResolve = require('browser-resolve'); +var insertGlobals = require('insert-module-globals'); +var umd = require('umd'); + var path = require('path'); -var coffee = require('coffee-script'); +var inherits = require('inherits'); var EventEmitter = require('events').EventEmitter; -var wrap = require('./lib/wrap'); -var watch = require('./lib/watch'); +module.exports = function (opts) { + if (opts === undefined) opts = {}; + if (typeof opts === 'string') opts = { entries: [ opts ] }; + if (Array.isArray(opts)) opts = { entries: opts }; + + var b = new Browserify(opts); + [].concat(opts.entries).filter(Boolean).forEach(b.add.bind(b)); + return b; +}; -function idFromPath (path) { - return path.replace(/\\/g, '/'); +function hash(what) { + return crypto.createHash('md5').update(what).digest('base64').slice(0, 6); } -function isAbsolute (pathOrId) { - return path.normalize(pathOrId) === path.normalize(path.resolve(pathOrId)); -} +inherits(Browserify, EventEmitter); -function needsNodeModulesPrepended (id) { - return !/^[.\/]/.test(id) && !isAbsolute(id); +function Browserify (opts) { + var self = this; + + self.files = []; + self.exports = {}; + self._pending = 0; + self._entries = []; + self._ignore = {}; + self._external = {}; + self._expose = {}; + self._mapped = {}; + self._transforms = []; + self._noParse =[]; + + var noParse = [].concat(opts.noParse).filter(Boolean); + var cwd = process.cwd(); + var top = { id: cwd, filename: cwd + '/_fake.js', paths: [] }; + noParse.forEach(function (file) { + self._noParse.push(file, path.resolve(file)); + self._pending ++; + self._resolve(file, top, function (err, r) { + if (r) self._noParse.push(r); + if (--self._pending === 0) self.emit('_ready'); + }); + }); } -var exports = module.exports = function (entryFile, opts) { - if (!opts) opts = {}; +Browserify.prototype.add = function (file) { + this.require(file, { entry: true }); + return this; +}; + +Browserify.prototype.require = function (id, opts) { + var self = this; + if (opts === undefined) opts = { expose: id }; - if (Array.isArray(entryFile)) { - if (Array.isArray(opts.entry)) { - opts.entry.unshift.apply(opts.entry, entryFile); + self._pending ++; + + var basedir = opts.basedir || process.cwd(); + var fromfile = basedir + '/_fake.js'; + + var params = { filename: fromfile, packageFilter: packageFilter }; + browserResolve(id, params, function (err, file) { + if (err) return self.emit('error', err); + if (!file) return self.emit('error', new Error( + 'module ' + JSON.stringify(id) + ' not found in require()' + )); + + if (opts.expose) { + self.exports[file] = hash(file); + + if (typeof opts.expose === 'string') { + self._expose[file] = opts.expose; + self._mapped[opts.expose] = file; + } } - else if (opts.entry) { - opts.entry = entryFile.concat(opts.entry); + + if (opts.external) { + self._external[file] = true; } else { - opts.entry = entryFile; + self.files.push(file); } + + if (opts.entry) self._entries.push(file); + + if (--self._pending === 0) self.emit('_ready'); + }); + + return self; +}; + +// DEPRECATED +Browserify.prototype.expose = function (name, file) { + this.exports[file] = name; + this.files.push(file); +}; + +Browserify.prototype.external = function (id, opts) { + opts = opts || {}; + opts.external = true; + return this.require(id, opts); +}; + +Browserify.prototype.ignore = function (file) { + this._ignore[file] = true; + return this; +}; + +Browserify.prototype.bundle = function (opts, cb) { + var self = this; + if (typeof opts === 'function') { + cb = opts; + opts = {}; } - else if (typeof entryFile === 'object') { - opts = entryFile; - } - else if (typeof entryFile === 'string') { - if (Array.isArray(opts.entry)) { - opts.entry.unshift(entryFile); - } - else if (opts.entry) { - opts.entry = [ opts.entry, entryFile ]; - } - else { - opts.entry = entryFile; - } - } + if (!opts) opts = {}; + if (opts.insertGlobals === undefined) opts.insertGlobals = false; + if (opts.detectGlobals === undefined) opts.detectGlobals = true; + if (opts.ignoreMissing === undefined) opts.ignoreMissing = false; + if (opts.standalone === undefined) opts.standalone = false; - var opts_ = { - cache : opts.cache, - debug : opts.debug, - exports : opts.exports, - }; - var w = wrap(opts_); - w.register('.coffee', function (body, file) { - try { - var res = coffee.compile(body, { filename : file }); - } - catch (err) { - w.emit('syntaxError', err); - } - return res; - }); - w.register('.json', function (body, file) { - return 'module.exports = ' + body + ';\n'; - }); + opts.resolve = self._resolve.bind(self); + opts.transform = self._transforms; + opts.noParse = self._noParse; + opts.transformKey = [ 'browserify', 'transform' ]; - var listening = false; - w._cache = null; - - var self = function (req, res, next) { - if (!listening && req.connection && req.connection.server) { - req.connection.server.on('close', function () { - self.end(); - }); - } - listening = true; - - if (req.url.split('?')[0] === (opts.mount || '/browserify.js')) { - if (!w._cache) self.bundle(); - res.statusCode = 200; - res.setHeader('last-modified', self.modified.toString()); - res.setHeader('content-type', 'text/javascript'); - res.end(w._cache); - } - else next() + var parentFilter = opts.packageFilter; + opts.packageFilter = function (pkg) { + if (parentFilter) pkg = parentFilter(pkg); + return packageFilter(pkg); }; - if (opts.watch) watch(w, opts.watch); + if (self._pending) { + var tr = through(); + self.on('_ready', function () { + var b = self.bundle(opts, cb); + if (!cb) b.on('error', tr.emit.bind(tr, 'error')); + b.pipe(tr); + }); + return tr; + } - if (opts.filter) { - w.register('post', function (body) { - return opts.filter(body); + if (opts.standalone && self._entries.length !== 1) { + process.nextTick(function () { + p.emit('error', 'standalone only works with a single entry point'); }); } - w.ignore(opts.ignore || []); + var d = (opts.deps || self.deps.bind(self))(opts); + var g = opts.detectGlobals || opts.insertGlobals + ? insertGlobals(self.files, { + resolve: self._resolve.bind(self), + always: opts.insertGlobals + }) + : through() + ; + var p = self.pack(opts.debug, opts.standalone); + if (cb) { + p.on('error', cb); + p.pipe(concatStream(cb)); + } - if (opts.require) { - if (Array.isArray(opts.require)) { - opts.require.forEach(function (r) { - r = idFromPath(r); - - var params = {}; - if (needsNodeModulesPrepended(r)) { - params.target = '/node_modules/' + r + '/index.js'; - } - w.require(r, params); - }); + d.on('error', p.emit.bind(p, 'error')); + g.on('error', p.emit.bind(p, 'error')); + d.pipe(through(function (dep) { + if (self._noParse.indexOf(dep.id) >= 0 + || (opts.cache && opts.cache[dep.id])) { + p.write(dep); } - else if (typeof opts.require === 'object') { - Object.keys(opts.require).forEach(function (key) { - opts.require[key] = idFromPath(opts.require[key]); + else this.queue(dep) + })).pipe(g).pipe(p); + + return p; +}; - var params = {}; - if (needsNodeModulesPrepended(opts.require[key])) { - params.target = '/node_modules/' - + opts.require[key] + '/index.js' - ; - } - w.require(opts.require[key], params); - w.alias(key, opts.require[key]); - }); - } - else { - opts.require = idFromPath(opts.require); - - var params = {}; - if (needsNodeModulesPrepended(opts.require)) { - params.target = '/node_modules/' - + opts.require + '/index.js' - ; - } - w.require(opts.require, params); - } +Browserify.prototype.transform = function (t) { + if (typeof t === 'string' && /^\./.test(t)) { + t = path.resolve(t); } + this._transforms.push(t); + return this; +}; - if (opts.entry) { - if (Array.isArray(opts.entry)) { - opts.entry.forEach(function (e) { - w.addEntry(e); +Browserify.prototype.deps = function (opts) { + var self = this; + if (self._pending) { + var tr = through(); + self.on('_ready', function () { + self.deps(opts).pipe(tr); + }); + return tr; + } + + var d = mdeps(self.files, opts); + + var tr = d.pipe(through(write)); + d.on('error', tr.emit.bind(tr, 'error')); + return tr; + + function write (row) { + self.emit('dep', row); + + if (row.id === emptyModulePath) { + row.source = ''; + } + + if (self._expose[row.id]) { + this.queue({ + exposed: self._expose[row.id], + deps: {}, + source: 'module.exports=require(\'' + hash(row.id) + '\');' }); } - else { - w.addEntry(opts.entry); + + if (self.exports[row.id]) row.exposed = self.exports[row.id]; + + // skip adding this file if it is external + if (self._external[row.id]) { + return; } + + if (/\.json$/.test(row.id)) { + row.source = 'module.exports=' + row.source; + } + + var ix = self._entries.indexOf(row.id); + row.entry = ix >= 0; + if (ix >= 0) row.order = ix; + this.queue(row); } +}; + +Browserify.prototype.pack = function (debug, standalone) { + var self = this; + var packer = browserPack({ raw: true }); + var ids = {}; + var idIndex = 1; - Object.keys(w).forEach(function (key) { - Object.defineProperty(self, key, { - set : function (value) { w[key] = value }, - get : function () { return w[key] } - }); - }); + var mainModule; - Object.keys(Object.getPrototypeOf(w)).forEach(function (key) { - self[key] = function () { - var s = w[key].apply(self, arguments) - if (s === self) { w._cache = null } - return s; - }; - }); - - Object.keys(EventEmitter.prototype).forEach(function (key) { - if (typeof w[key] === 'function' && w[key].bind) { - self[key] = w[key].bind(w); + var input = through(function (row_) { + var row = copy(row_); + var ix; + + if (debug) { + row.sourceRoot = 'file://localhost'; + row.sourceFile = row.id; } + + if (row.exposed) { + ix = row.exposed; + } else { - self[key] = w[key]; + ix = ids[row.id] !== undefined ? ids[row.id] : idIndex++; } + if (ids[row.id] === undefined) ids[row.id] = ix; + + if (/^#!/.test(row.source)) row.source = '//' + row.source; + var err = checkSyntax(row.source, row.id); + if (err) return this.emit('error', err); + + row.id = ix; + if (row.entry) mainModule = mainModule || ix; + row.deps = Object.keys(row.deps).reduce(function (acc, key) { + var file = row.deps[key]; + + // reference external and exposed files directly by hash + if (self._external[file] || self.exports[file]) { + acc[key] = hash(file); + return acc; + } + + if (ids[file] === undefined) ids[file] = idIndex++; + acc[key] = ids[file]; + return acc; + }, {}); + + this.queue(row); }); - var firstBundle = true; - self.modified = new Date; + var first = true; + var hasExports = Object.keys(self.exports).length; + var output = through(write, end); - self.bundle = function () { - if (w._cache) return w._cache; - - var src = w.bundle.apply(w, arguments); - self.ok = Object.keys(w.errors).length === 0; - - if (!firstBundle) { - self.modified = new Date; + function writePrelude () { + if (!first) return; + if (standalone) { + return output.queue(umd.prelude(standalone) + 'return '); } - firstBundle = false; - - w._cache = src; - return src; - }; + if (!hasExports) return output.queue(';'); + output.queue('require='); + } - self.end = function () { - Object.keys(w.watches || {}).forEach(function (file) { - w.watches[file].close(); - }); - }; + input.pipe(packer); + packer.pipe(output); + return duplexer(input, output); - return self; + function write (buf) { + if (first) writePrelude(); + first = false; + this.queue(buf); + } + + function end () { + if (first) writePrelude(); + if (standalone) { + output.queue('(' + mainModule + ')' + umd.postlude(standalone)); + } + this.queue('\n;'); + this.queue(null); + } }; -exports.bundle = function (opts) { - return exports(opts).bundle(); +var packageFilter = function (info) { + if (typeof info.browserify === 'string' && !info.browser) { + info.browser = info.browserify; + delete info.browserify; + } + return info; }; + +var emptyModulePath = require.resolve('./_empty'); +Browserify.prototype._resolve = function (id, parent, cb) { + if (this._ignore[id]) return cb(null, emptyModulePath); + var self = this; + var result = function (file, x) { + if (self._pending === 0) { + self.emit('file', file, id, parent); + } + cb(null, file, x); + }; + if (self._mapped[id]) return result(self._mapped[id]); + + return browserResolve(id, parent, function(err, file) { + if (err) return cb(err); + if (!file) return cb(new Error('module ' + + JSON.stringify(id) + ' not found from ' + + JSON.stringify(parent.filename) + )); + + if (self._ignore[file]) return cb(null, emptyModulePath); + if (self._external[file]) return result(file, true); + + result(file); + }); +}; + +function copy (obj) { + return Object.keys(obj).reduce(function (acc, key) { + acc[key] = obj[key]; + return acc; + }, {}); +}