amd-optimize
Version:
An AMD (i.e. RequireJS) optimizer that's stream-friendly. Made for gulp. (WIP)
238 lines (176 loc) • 5.63 kB
text/coffeescript
_ = require("lodash")
fs = require("fs")
path = require("path")
vinylFs = require("vinyl-fs")
async = require("async")
through = require("through2")
Readable = require("stream").Readable
trace = require("./trace")
exportModule = require("./export")
util = require("./util")
firstChunk = (stream, callback) ->
settled = false
stream
.on("data", (data) ->
if not settled
settled = true
callback(null, data)
return
).on("end", ->
if not settled
callback()
return
).on("error", (err) ->
if not settled
settled = true
callback(err)
return
)
return
collectModules = (module, omitInline = true) ->
outputBuffer = []
collector = (currentModule) ->
currentModule.deps.forEach( (depModule) ->
collector(depModule)
)
if not (omitInline and currentModule.isInline) and not _.any(outputBuffer, name : currentModule.name)
outputBuffer.push(currentModule)
collector(module)
return outputBuffer
mergeOptionsFile = (file, options = {}) ->
return _.merge(
{}
Function("""
var output,
requirejs = require = {
config : function (options) { output = options; }
},
define = function () {};
#{file.contents.toString("utf8")};
return output;
""")()
options
)
defaultLoader = (fileBuffer, options) ->
return (name, callback, asPlainFile) ->
addJs = (!asPlainFile) and '.js' or ''
if options.baseUrl and file = _.detect(fileBuffer, path : path.resolve(options.baseUrl, name + addJs))
callback(null, file)
else if file = _.detect(fileBuffer, relative : path.join(options.baseUrl, name + addJs))
callback(null, file)
else if options.loader
options.loader(name, callback)
else
module.exports.loader()(path.join(options.baseUrl, name + addJs), callback)
module.exports = rjs = (entryModuleName, options = {}) ->
options = _.defaults(
options, {
baseUrl : ""
configFile : null
exclude : []
excludeShallow : []
findNestedDependencies : false
loader : null
preserveComments : false
}
)
if _.isString(options.exclude)
options.exclude = [options.exclude]
if _.isString(options.excludeShallow)
options.excludeShallow = [options.excludeShallow]
if _.isString(options.configFile) or _.isArray(options.configFile)
configFileStream = vinylFs.src(options.configFile)
else if _.isObject(options.configFile)
configFileStream = options.configFile
fileBuffer = []
mainStream = through.obj(
(file, enc, done) ->
fileBuffer.push(file)
done()
(done) ->
async.waterfall([
(callback) ->
if configFileStream
configFileStream.pipe(
through.obj(
(file, enc, done) ->
options = mergeOptionsFile(file, options)
done()
-> callback()
)
)
else
callback()
(callback) ->
trace(entryModuleName, options, null, defaultLoader(fileBuffer, options), callback)
(module, callback) ->
callback(null, collectModules(module))
(modules, callback) ->
if _.isArray(options.exclude)
async.map(
options.exclude
(moduleName, callback) ->
trace(moduleName, options, null, defaultLoader(fileBuffer, options), callback)
(err, excludedModules) ->
if err
callback(err)
else
callback(null, modules, _(excludedModules)
.map((module) -> collectModules(module))
.flatten()
.pluck("name")
.unique()
.value())
)
else
callback(null, modules, [])
(modules, excludedModuleNames, callback) ->
# printTree(module)
# Remove excluded modules
modules = _.reject(modules, (module) ->
return _.contains(excludedModuleNames, module.name) or
_.contains(options.excludeShallow, module.name)
)
exportStream = exportModule(options)
exportStream
.on("data", (file) ->
mainStream.push(file)
)
.on("end", -> callback())
.on("error", callback)
modules.forEach(exportStream.write.bind(exportStream))
exportStream.end()
], done)
)
return mainStream
module.exports.src = (moduleName, options) ->
source = rjs(moduleName, options)
process.nextTick -> source.end()
return source
module.exports.loader = (filenameResolver, pipe) ->
(moduleName, callback) ->
if filenameResolver
filename = filenameResolver(moduleName)
else
filename = moduleName
source = vinylFs.src(filename).pipe(through.obj())
if pipe
source = source.pipe(pipe())
firstChunk(source, callback)
return