Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 298 lines (238 sloc) 8.437 kB
#!/usr/bin/env node
var fs = require('fs'),
http = require('http'),
https = require('https'),
path = require('path'),
url = require('url'),
nopt = require('nopt'),
options = { manifest: path,
root: path,
external: Boolean,
stylesheets: Boolean,
packages: Boolean,
bundles: path,
output: String,
directory: path
},
shorts = { 'm': '--manifest',
'r': '--root',
'e': '--external',
's': '--stylesheets',
'p': '--packages',
'P': '--no-packages',
'b': '--bundles',
'o': '--output',
'd': '--directory'
},
outputs = ['code', 'paths'],
params = nopt(options, shorts),
include = params.argv.remain.slice(),
dir = new RegExp('^' + path.resolve(params.directory || process.cwd()) + '/')
var C = require('../src/console').Console,
P = require('../index')
P.WEB_ROOT = path.resolve(params.root || process.cwd())
P.ENV.JS = P
var die = function(message) {
C.consoleFormat('white', 'bgred')
C.print('ERROR')
C.reset()
C.puts(' ' + message)
process.exit(1)
}
if (params.output && outputs.indexOf(params.output) < 0)
die('--output must be one of: ' + outputs.join(', '))
if (include.length === 0) {
C.bold()
C.puts('\nUsage: jsbuild --manifest MANIFEST --root ROOT [OPTIONS] module1 [module2 ...]')
C.reset()
C.puts('\nOptions: --manifest, -m : path to JS.Packages manifest script')
C.puts(' --root, -r : directory containing your JavaScripts')
C.puts(' --external, -e : include external scripts from the web')
C.puts(' --stylesheets, -s : embed required CSS stylesheets in JS')
C.puts(' --no-packages, -P : don\'t include JS.Package system in build')
C.puts(' --bundles, -b : path to bundle definitions file')
C.puts(' --output, -o : select output type: \'code\' or \'paths\'')
C.puts(' --directory, -d : directory for paths to print relative to')
C.puts('\ne.g. jsbuild -m path/to/js/manifest.js -r path/to/js/ -Pe jQuery JS.Set\n')
process.exit()
}
var HTTP_REGEX = /^https?:/i,
CSS_REGEX = /\.css$/i
var resolve = function(file, ref) {
var root = P.WEB_ROOT
if (HTTP_REGEX.test(ref)) {
return ref
} else if (HTTP_REGEX.test(file)) {
var uri = url.parse(file)
return uri.protocol + '//' + uri.host + path.resolve(path.dirname(uri.pathname), ref)
} else {
if (root && /^\//.test(ref)) ref = (root + '/' + ref).replace(/\/+/g, '/')
return path.resolve(path.dirname(file), ref)
}
}
if (params.manifest) require(params.manifest)
var packages = [],
excluded = [],
bundles = params.bundles ? JSON.parse(fs.readFileSync(params.bundles)) : {},
source = [],
output = function() { console.log(source.join('\n\n')) }
var expand = function(name, list) {
var pkg = P.Package._getByName(name),
deps = list || []
pkg._deps.list.forEach(function(p) { expand(p, deps) })
if (deps.indexOf(pkg) === -1) deps.push(pkg)
pkg._uses.list.forEach(function(p) { expand(p, deps) })
return deps
}
var array = function(object) {
if (object === undefined) return []
return [].concat(object)
}
var expandBundle = function(name) {
var bundle = bundles[name]
array(bundle.include).forEach(function(p) { expand(p, packages) })
var expandExcluded = function(excl) {
var exclBundle = bundles[excl]
if (!exclBundle) return expand(excl, excluded)
array(exclBundle.include).forEach(function(p) { expand(p, excluded) })
array(exclBundle.exclude).forEach(expandExcluded)
}
array(bundle.exclude).forEach(expandExcluded)
}
var packageFiles = function(packages) {
var files = []
packages.forEach(function(pkg) {
var paths = pkg._loader
if (!(paths instanceof Array))
die('Cannot bundle ' + pkg + ': no path specified in your manifest')
if (params.stylesheets)
paths = paths.concat(pkg._styles.list)
if (!params.external)
paths = paths.filter(function(p) { return !HTTP_REGEX.test(p) })
paths.forEach(function(loader) { files.push(loader) })
})
return files
}
include.forEach(function(inc) {
if (bundles[inc]) expandBundle(inc)
else expand(inc, packages)
})
var includedFiles = packageFiles(packages),
excludedFiles = packageFiles(excluded),
files = includedFiles.slice()
excludedFiles.forEach(function(exclude) {
var index = files.indexOf(exclude)
if (index >= 0) files.splice(index, 1)
})
if (params.output === 'paths') {
var formatPath = function(s) { return s.replace(dir, '') }
console.log(files.map(formatPath).join('\n'))
process.exit()
}
if (params.packages !== false)
source.push(fs.readFileSync(__dirname + '/../src/package-browser.js'))
var readLocal = function(file, callback) {
fs.readFile(file, function(error, content) {
if (error)
die('Cannot find file ' + file + ', please check your --root setting')
callback(content)
})
}
var readRemote = function(file, callback) {
var uri = url.parse(file),
client = (uri.protocol === 'https:') ? https : http,
port = uri.port || (client === https ? '443' : '80'),
options = {host: uri.hostname, port: port, path: uri.pathname},
chunks = [],
length = 0
client.get(options, function(response) {
if (response.statusCode < 200 || response.statusCode >= 300)
die('Request unsuccessful: ' + file)
response.addListener('data', function(chunk) {
chunks.push(chunk)
length += chunk.length
})
response.addListener('end', function() {
var offset = 0,
body = new Buffer(length)
chunks.forEach(function(chunk) {
chunk.copy(body, offset)
offset += chunk.length
})
callback(body)
})
})
.on('error', function() {
die('Request unsuccessful: ' + file)
})
}
var read = function(file, callback) {
console.error('[READ] ' + file)
var readFile = HTTP_REGEX.test(file) ? readRemote : readLocal
readFile(file, callback)
}
var expandCSS = function(file, css, callback) {
var assets = css.match(/\burl\s*\(("(\\.|[^"])*"|'(\\.|[^'])*'|[^\)]*)\)/g) || [],
length = assets.length,
inline = [],
complete = 0
if (length === 0) return callback(css)
var rewrite = function() {
inline.forEach(function(pair) {
css = css.replace(pair[0], pair[1])
})
callback(css)
}
assets.forEach(function(snippet, i) {
var url = snippet.replace(/^url\s*\(('|"|)/, '').replace(/('|"|)\)$/, ''),
pth = resolve(file, url),
type = url.split('.').pop()
var fill = function(buffer) {
var mime = (type === 'css') ? 'text/css' : 'image/' + type,
base64 = (typeof buffer === 'string')
? buffer
: 'url(data:' + mime + ';base64,' + buffer.toString('base64') + ')'
inline[i] = [snippet, base64]
complete += 1
if (complete === length) rewrite()
}
if (/^data:/.test(url)) return fill(snippet)
read(pth, function(buffer) {
if (type === 'css')
expandCSS(pth, buffer.toString('utf8'), function(css) {
fill(new Buffer(css, 'utf8'))
})
else
fill(buffer)
})
})
}
var embedCSS = function(file, css, callback) {
expandCSS(file, css, function(css) {
var base64 = new Buffer(css, 'utf8').toString('base64')
css = "(function() {\n" +
" if (typeof document === 'undefined') return;\n" +
" var head = document.getElementsByTagName('head')[0],\n" +
" style = document.createElement('style');\n" +
" style.type = 'text/css';\n" +
" style.innerHTML = '@import url(data:text/css;base64," + base64 + ")';\n" +
" head.appendChild(style);\n" +
"})();"
callback(css)
})
}
var bundle = function(i) {
if (i >= files.length) return output()
var file = files[i]
var next = function(code) {
source.push(code.toString('utf8'))
bundle(i + 1)
}
read(file, function(code) {
if (CSS_REGEX.test(file))
embedCSS(file, code.toString('utf8'), next)
else
next(code)
})
}
bundle(0)
Jump to Line
Something went wrong with that request. Please try again.