diff --git a/bin/closure-util.js b/bin/closure-util.js index 29f8282..159e925 100755 --- a/bin/closure-util.js +++ b/bin/closure-util.js @@ -4,6 +4,7 @@ var parser = require('nomnom'); var deps = require('../lib/deps'); var build = require('../lib/build'); +var serve = require('../lib/serve'); parser.options({ loglevel: { @@ -82,6 +83,22 @@ parser.command('build') }); }).help('Build with Closure Compiler'); +parser.command('serve') + .option('config', { + position: 1, + required: true, + help: 'Path to JSON config file' + }) + .callback(function(opts) { + var configFile = opts.config; + serve(configFile, function(err) { + if (err) { + log.error('closure-util', err.message); + process.exit(1); + } + }); + }).help('Start the development server'); + var options = parser.parse(); /** diff --git a/lib/build.js b/lib/build.js index 0855168..15adf48 100644 --- a/lib/build.js +++ b/lib/build.js @@ -2,7 +2,7 @@ var async = require('async'); var fse = require('fs-extra'); var log = require('npmlog'); -var buildconfig = require('./buildconfig'); +var configfile = require('./configfile'); var closure = require('./index'); var config = require('./config'); @@ -20,9 +20,37 @@ log.level = config.get('log_level'); * @param {function(Error, Object)} callback Called with the build * configuration object or any error. */ -function _readConfig(configFile, callback) { +function readConfig(configFile, callback) { log.info('closure-util', 'Reading build config'); - buildconfig.readConfig(configFile, callback); + configfile.readConfig(configFile, callback); +} + + +/** + * Assert that a provided config object is valid. + * @param {Object} config Build configuration object. + * @param {function(Error, Object)} callback Called with the configuration + * object or any error. + */ +function assertValidConfig(config, callback) { + process.nextTick(function() { + if (!config.lib) { + config.lib = config.src; + } + if (!Array.isArray(config.lib)) { + callback(new Error('Config "lib" must be an array')); + return; + } + if (typeof config.compile !== 'object') { + callback(new Error('Config "compile" must be an object')); + return; + } + if (config.jvm && !Array.isArray(config.jvm)) { + callback(new Error('Config "jvm" must be an array')); + return; + } + callback(null, config); + }); } @@ -32,7 +60,7 @@ function _readConfig(configFile, callback) { * @param {function(Error, Array.)} callback Called with a * list of paths or any error. */ -function _getDependencies(config, callback) { +function getDependencies(config, callback) { log.info('closure-util', 'Getting Closure dependencies'); var options = { lib: config.lib || config.src, @@ -55,7 +83,7 @@ function _getDependencies(config, callback) { * @param {function(Error, string)} callback Called with the compiled output * or any error. */ -function _compile(config, paths, callback) { +function compile(config, paths, callback) { log.info('closure-util', 'Compiling ' + paths.length + ' sources'); var options = { compile: config.compile, @@ -73,7 +101,7 @@ function _compile(config, paths, callback) { * @param {string} code The code to write to the output file. * @param {function(Error)} callback Called with the error if any. */ -function _writeOutput(outputFile, code, callback) { +function writeOutput(outputFile, code, callback) { log.info('closure-util', 'Writing compiled code to ' + outputFile); fse.outputFile(outputFile, code, callback); } @@ -90,10 +118,11 @@ function _writeOutput(outputFile, code, callback) { */ module.exports = function(configFile, outputFile, callback) { async.waterfall([ - _readConfig.bind(null, configFile), - _getDependencies, - _compile, - _writeOutput.bind(null, outputFile) + readConfig.bind(null, configFile), + assertValidConfig, + getDependencies, + compile, + writeOutput.bind(null, outputFile) ], function(err) { if (err) { callback(err); diff --git a/lib/buildconfig.js b/lib/configfile.js similarity index 57% rename from lib/buildconfig.js rename to lib/configfile.js index 5cc1579..384d4d5 100644 --- a/lib/buildconfig.js +++ b/lib/configfile.js @@ -10,26 +10,6 @@ var config = require('./config'); log.level = config.get('log_level'); -/** - * Assert that a provided config object is valid. - * @param {Object} config Build configuration object. - */ -function assertValidConfig(config) { - if (!config.lib) { - config.lib = config.src; - } - if (!Array.isArray(config.lib)) { - throw new Error('Config "lib" must be an array'); - } - if (typeof config.compile !== 'object') { - throw new Error('Config "compile" must be an object'); - } - if (config.jvm && !Array.isArray(config.jvm)) { - throw new Error('Config "jvm" must be an array'); - } -} - - /** * Read the build configuration file. * @param {string} configPath Path to config file. @@ -52,12 +32,6 @@ exports.readConfig = function(configPath, callback) { err2.message)); return; } - try { - assertValidConfig(config); - } catch (err3) { - callback(err3); - return; - } callback(null, config); }); }; diff --git a/lib/serve.js b/lib/serve.js new file mode 100644 index 0000000..02a7db6 --- /dev/null +++ b/lib/serve.js @@ -0,0 +1,113 @@ +var async = require('async'); +var log = require('npmlog'); + +var config = require('./config'); +var configfile = require('./configfile'); +var manager = require('./manager'); +var server = require('./server'); + + +/** + * Configurable log level. + * @type {string} + */ +log.level = config.get('log_level'); + + +/** + * Read the build configuration file. + * @param {string} configFile Config file. + * @param {function(Error, Object)} callback Called with the build + * configuration object or any error. + */ +function readConfig(configFile, callback) { + log.info('closure-util', 'Reading build config'); + configfile.readConfig(configFile, callback); +} + + +/** + * Assert that a provided config object is valid. + * @param {Object} config Build configuration object. + * @param {function(Error, Object)} callback Called with the configuration + * object or any error. + */ +function assertValidConfig(config, callback) { + process.nextTick(function() { + if (!config.lib) { + config.lib = config.src; + } + if (!Array.isArray(config.lib)) { + callback(new Error('Config "lib" must be an array')); + return; + } + callback(null, config); + }); +} + + +/** + * Create a development server. + * @param {Object} config The configuration object. + * @param {function(Error, closure.Server)} callback Callback. + */ +function createServer(config, callback) { + var srv; + var mgr = new manager.Manager({ + lib: config.lib + }); + mgr.on('error', function(err) { + if (server) { + log.error('serve', err.message); + } else { + callback(err); + } + }); + mgr.on('ready', function() { + srv = new server.Server({ + manager: mgr + }); + callback(null, config, srv); + }); +} + + +/** + * @param {Object} config The configuration object. + * @param {closure.Server} server The server. + * @param {function(Error)} callback Callback with an error, if any. + */ +function listen(config, server, callback) { + var port = config.serve && config.serve.port ? + config.serve.port : 3000; + server.listen(port, function() { + log.info('closure-util', 'Listening on http://localhost:' + + port + '/ (Ctrl+C to stop)'); + }); + server.on('error', function(err) { + log.error('serve', 'Server failed to start: ' + err.message); + callback(err); + }); + callback(null); +} + + +/** + * @param {string} configFile Config file path. + * @param {function(Error)} callback Called when the compilation is + * finished or when any error occured. + */ +module.exports = function(configFile, callback) { + async.waterfall([ + readConfig.bind(null, configFile), + assertValidConfig, + createServer, + listen + ], function(err) { + if (err) { + callback(err); + } else { + callback(null); + } + }); +}; diff --git a/readme.md b/readme.md index b0181bb..f9530fa 100644 --- a/readme.md +++ b/readme.md @@ -81,21 +81,28 @@ Available configuration options (see `default-config.json` for default values): ## CLI -The `closure-util` command line utility provides commands for updating (or installing) specific versions of the Closure Compiler and Closure Library for use with your project, and a command for building your project using the Closure Compiler. +The `closure-util` command line utility provides `update` commands for updating (or installing) specific versions of the Closure Compiler and Closure Library for use with your project, a `build` command for building your project using the Closure Compiler, and a `serve` command for starting a development server for your project. * `closure-util update` - Update both the Compiler and Library. * `closure-util update-compiler` - Update the Compiler. * `closure-util update-library` - Update the Library. * `closure-util build` - Build a JavaScript application. + * `closure-util serve` - Start a development server. * `closure-util --help` - Display command usage and options. See the [configuration](#configuration) section above for information on how to configure URLs for specific versions of the Compiler or Library. The `closure-util` utility will look for this configuration when executing one of the `update`, `update-compiler` or `update-library` commands. This is how the `build` command is used: - closure-util build build.json app.min.js + closure-util build config.json app.min.js -where `build.json` is a build config file and `app.min.js` in the output file including the compiled code. As an example for a build config file see the [`build-config.json`](test/fixtures/build-config.json) file used in the `closure-util` tests. +where `config.json` is a build config file and `app.min.js` in the output file including the compiled code. As an example for a build config file see the [`config.json`](test/fixtures/config.json) file used in the `closure-util` tests. The config file should include a `"lib"` and a `"compile"` sections. + +This is how the `serve` command is used: + + closure-util serve config.json + +where `config.json` is a config file. You can look at the [`config.json`](test/fixtures/config.json) again. For the `serve` command the config file should include a `"lib"` and a `"serve"` sections. ## Development diff --git a/test/fixtures/build-config.json b/test/fixtures/config.json similarity index 97% rename from test/fixtures/build-config.json rename to test/fixtures/config.json index 8c99478..dde9b18 100644 --- a/test/fixtures/build-config.json +++ b/test/fixtures/config.json @@ -59,5 +59,8 @@ "warning_level": "VERBOSE", "output_wrapper": "(function(){%output%})();", "use_types_for_optimization": true + }, + "serve": { + "port": 3001 } } diff --git a/test/spec/build.spec.js b/test/spec/build.spec.js index 0f73f5e..c79db1d 100644 --- a/test/spec/build.spec.js +++ b/test/spec/build.spec.js @@ -15,7 +15,7 @@ describe('build', function() { // this test runs the compiler, increase the timeout value this.timeout(30000); var outputFile = temp.path({suffix: '.js'}); - var configFile = path.join(fixtures, 'build-config.json'); + var configFile = path.join(fixtures, 'config.json'); build(configFile, outputFile, function(err) { assert.isNull(err); fs.exists(outputFile, function(exists) {