graphquire
Version:
module graph builder and installer.
160 lines (142 loc) • 5.02 kB
JavaScript
/* vim:set ts=2 sw=2 sts=2 expandtab */
/*jshint asi: true undef: true es5: true node: true devel: true
forin: true latedef: false globalstrict: true */
/*global define: true */
;
var fs = require('fs')
var url = require('url')
var http = require('http')
var https = require('https')
var path = require('path')
var streamer = require('./streamer')
function getGraph(options) {
var descriptor = getDescriptorStream(options)
var modules = getModules(descriptor)
}
function fileStream(path) {
return function stream(next, stop) {
fs.readFile(path, function onRead(error, content) {
if (error) return stop(error)
next(content)
stop()
})
}
}
function uriStream(uri) {
var options = url.parse(uri)
options.path = options.pathname
options.followRedirect = true
options.maxRedirects = 2
var get = options.protocol === 'http:' ? http.get : https.get
return function stream(next, stop) {
get(options, function onResponse(response) {
response.on('error', stop)
response.on('end', stop)
response.on('data', function onData(content) {
next(content)
})
}).on('error', stop)
}
}
function fallback(primary, secondary) {
return function stream(next, stop) {
primary(next, function onStop(error) {
if (error) secondary(next, stop)
else stop()
})
}
}
function resourceStream(resource) {
var uri = resource.uri
var path = resource.path
var isLocal = resource.isLocal
return isLocal ? fileStream(path) : fallback(fileStream(path), uriStream(uri))
}
function descriptor(resource) {
return resourceStream(resource)
}
function requirements(content) {
return function stream(next, stop) {
content(function onContent(source) {
var dependency, dependencies = []
// Removing comments to avoid capturing commented require statements.
source = String(source).replace(COMMENT_PATTERN, '')
// Push each found dependency into array.
while ((dependency = REQUIRE_PATTERN.exec(source))) next(dependency[1])
stop()
}, stop)
}
}
function resolve(base) {
return function descriptor(id) {
return {
id: url.resolve(base.id, id),
uri: url.resolve(base.uri, id),
path: url.resolve(base.path, id)
}
}
}
function module(descriptor) {
return function stream(next, stop) {
var content = resourceStream(descriptor)
content(function onContent(source) {
if (!descriptor.isLocal) descriptor.source = source
var dependency, dependencies = requirements(source)
if (dependencies.length) descriptor.requirements = {}
while ((dependency = dependencies.shift()))
descriptor.requirements[dependency] = resolve(dependency, descriptor.id)
}, stop)
}
}
function depends(module) {
return function stream(next, stop) {
module(function onModule(descriptor) {
streamer.values(descriptor.dependencies)(next)
}, stop)
}
}
function isPluginID(id) { return id && ~id.indexOf('!') }
function isRelativeID(id) { return id && id.charAt(0) === '.' }
function isNativeID(id) { return !isRelativeID(id) && isPluginID(id) }
function isInManifest(manifest, id) { return id in manifest }
function nativeModule(id) {
return { id: id }
}
function modules(manifest, descriptor) {
var dependencies = depends(module(descriptor))
var unique = streamer.filter(dependencies, isInManifest.bind(null, manifest))
var absolute = streamer.filter(unique, isPluginID)
var relative = streamer.filter(unique, isRelativeID)
var native = streamer.filter(unique, isNativeID)
var absolute = streamer.merge(absolute, streamer.map(relative, resolve.bind(null, descriptor)))
var native = streamer.map(native, nativeModule.bind(manifest))
}), function map(id) { })
module(descriptor)(function onModule(descriptor) {
var id, requirements = descriptor.requirements
streamer.filter(streamer.values(requirements), function onRequirement() {
})
}, function onStop(error) { if (error) stop(error) })
return streamer.join(streamer.merge(dependencies), streamer.list([descriptors]))
var requirements = dependencies(base)
var dependencies = requirements(base)
var descriptors = streamer.map(dependencies, resolve(descriptor))
var moduleStreams = streamer.map(descriptors, modules)
var module = streamer.map(streamer.zip(streamer(descriptor), content), function)
return streamer.join(streamer.stream(descriptor), streamer.merge(moduleStreams))
re
dependencies(function onDependency(id) {
descriptor[id] = url.resolve(descriptor.id, id)
}, next.bind(null, descriptor))
join(stream(descriptor), streamer.merge(modules))
zip(requirements, content)(next.bind(null, descriptor), end)
var requirements = map(dependencies, requirement)
next(module)
map(requirements, modules)(next, end)
}
}
function getModules(descriptor) {
return function stream(next, done) {
descriptor(function onDescriptor(next, metadata) {
})
}
}