#!/usr/bin/env node
var program = require('commander');
// options
program
.usage('component build [scripts] [styles] [files]')
.option('-w, --watch', 'watch for file changes and rebuild automatically')
.option('-r, --reload', 'refresh livereload server on file changes (works only with --watch)')
.option('-o, --out
', 'output directory defaulting to ./build', 'build')
.option('-n, --name ', 'base name for build files defaulting to build', 'build')
.option('-d, --dev', 'build development dependencies, use aliases, and use sourceURLs')
.option('-s, --standalone [name]', 'build a standalone, UMD-wrapped version of the component with the given global name')
.option('-R, --no-require', 'exclude require from build')
.option('-a, --no-auto', 'not require automatically')
.option('-p, --prefix ', 'prefix css asset urls with ', '')
.option('-b, --browsers ', 'browsers to support with autoprefixer')
.option('-c, --copy', 'copy files instead of linking')
.option('--umd [name]', 'alias for --standalone')
.option('--debug', 'turn on debug statements');
// examples
program.on('--help', function(){
console.log(' Examples:');
console.log();
console.log(' # build to ./build');
console.log(' $ component build');
console.log();
console.log(' # build to ./dist as assets.js, assets.css');
console.log(' $ component build -o dist -n assets');
console.log();
console.log(' # build as standalone as window.$');
console.log(' $ component build --standalone $');
console.log();
console.log(' # build only .js');
console.log(' $ component build scripts');
console.log();
process.exit();
});
// parse argv
program.parse(process.argv);
if (program.debug) require('debug').enable('component-build*,component-consoler*,component-resolver*');
var Resolve = require('component-resolver');
var utils = require('component-consoler');
var Build = require('component-build');
var mkdir = require('mkdirp');
var path = require('path');
var fs = require('fs');
var rimraf = require('rimraf');
var exists = fs.existsSync || path.existsSync;
var args = program.args;
var log = utils.log;
var slice = Array.prototype.slice;
// object of which files to build
var builds;
if (!args.length) {
builds = {
scripts: true,
styles: true,
files: true,
};
} else {
builds = {
scripts: !!~args.indexOf('scripts')
|| !!~args.indexOf('js'),
styles: !!~args.indexOf('styles')
|| !!~args.indexOf('css'),
files: !!~args.indexOf('files'),
};
}
// component.json required
if (!exists('component.json')) utils.fatal('missing component.json');
// output paths
var jsPath = path.join(program.out, program.name + '.js');
var cssPath = path.join(program.out, program.name + '.css');
// mkdir -p
mkdir.sync(program.out);
// whitespace
console.log();
process.on('exit', function(){
console.log();
});
// resolve
var options = {
development: program.dev,
install: true,
verbose: true,
require: program.require,
autorequire: program.auto,
umd: program.standalone || program.umd || '',
prefix: program.prefix || '',
browsers: program.browsers,
destination: program.out,
copy: program.copy,
};
var watching = program.watch || program.reload;
var resolving = false;
var build;
if (!watching) return resolve();
var watcher = require('component-watcher')({
root: process.cwd(),
development: program.dev
});
watcher.on('resolve', resolve);
watcher.on('scripts', buildScripts);
watcher.on('styles', buildStyles);
process.stdin.setEncoding('utf8');
process.stdin.on('data', function (data) {
switch (data.trim()) {
case 'r':
case 'resolve':
return resolve();
case 's':
case 'j':
case 'js':
case 'scripts':
return buildScripts();
case 'c':
case 'css':
case 'styles':
return buildStyles();
}
});
if (!program.reload) return;
var server = require('tiny-lr-fork')();
server.listen(35729);
server.on('error', function (err) {
if (err.code === 'EADDRINUSE') {
utils.fatal('livereload port 35729 is already in use by another process');
}
utils.fatal(err);
});
function reload() {
if (program.reload) {
server.changed({
body: {
files: slice.call(arguments)
}
});
}
}
function resolve() {
if (resolving) return;
resolving = true;
var start = Date.now();
Resolve(process.cwd(), options, function (err, tree) {
resolving = false;
if (err) {
if (!watching) utils.fatal(err);
utils.error('build', 'resolve failed: ' + err.message);
return;
}
build = Build(tree, options);
log('build', 'resolved in ' + (Date.now() - start) + 'ms');
buildScripts();
buildStyles();
buildFiles();
})
}
function buildScripts() {
if (resolving) return;
if (!builds.scripts) return;
var start = Date.now();
build.scripts(function (err, js) {
if (err) {
utils.error(err);
if (fs.existsSync(jsPath)) fs.unlinkSync(jsPath);
return;
}
if (!js) return;
fs.writeFile(jsPath, js);
log('build', jsPath + ' in '
+ (Date.now() - start) + 'ms - '
+ (js.length / 1024 | 0) + 'kb');
reload(jsPath);
})
}
function buildStyles() {
if (resolving) return;
if (!builds.styles) return;
var start = Date.now();
build.styles(function (err, css) {
if (err) {
utils.error(err);
if (fs.existsSync(cssPath)) fs.unlinkSync(cssPath);
return;
}
if (!css) return;
fs.writeFile(cssPath, css);
log('build', cssPath + ' in '
+ (Date.now() - start) + 'ms - '
+ (css.length / 1024 | 0) + 'kb');
reload(cssPath);
})
}
function buildFiles() {
if (resolving) return;
if (!builds.files) return;
var start = Date.now();
build.files(function (err) {
if (err) {
utils.error(err);
rimraf(options.destination, function(err) {
if (err) utils.fatal(err);
return
});
} else {
log('build', 'files in ' + (Date.now() - start) + 'ms');
}
})
}