UNPKG

boneyard

Version:

Architectural Toolkit for Web Development

284 lines (258 loc) 7.47 kB
/** * Boneyard Composer Tool * @author Patricio Ferreira <3dimentionar@gmail.com> **/ var fs = require('fs'), path = require('path'), resolve = path.resolve, join = path.join, http = require('http'); // Third-party libs var requirejs = require('requirejs'), connect = require('connect'), compression = require('compression'), watch = require('watch'), _ = require('underscore'), _s = require('underscore.string'), io = require('socket.io'); // Project specific libs var Package = require('../utils/package'), Logger = require('../utils/logger'), Utils = require('../utils/util'); var pkg = require('../../package.json'); /** * Composer namespace **/ var Composer = { /** * Composer Tool base path * @public * @property bPath * @type String **/ bPath: resolve(__dirname, '../../'), /** * User Base Path * @public * @property uPath * @type String **/ uPath: null, /** * Composer defaults * @public * @property defaults * @type Object **/ defaults: { configPath: './tools/default.json', config: null, template: './tools/composer/composer.html', requireMain: 'main.js', clear: false, port: 9393 }, /** * Live Reload Server/SocketIO * @public * @property live * @type Object **/ live: { server: null, socket: null }, /** * Source * @public * @property source * @type String **/ source: null, /** * Target Path * @public * @property target * @type String **/ target: null, /** * Config * @public * @property config * @type Object **/ config: null, /** * Require Config Template * @public * @property requireTpl * @type String **/ requireTpl: 'requirejs.config(<%= cfg %>);', /** * Execute Composer * @public * @method execute * @param cfg {Object} Main Config input **/ exec: function(cfg) { cfg || (cfg = {}); Logger.log('Boneyard Composer Tool', { nl: true }); this.loadConfig().loadCustomConfig(cfg); this.setup(cfg).export().output(cfg); this.spinUpServer(); }, /** * Load Default Config * @public * @method loadConfig * @return Composer **/ loadConfig: function() { this.defaults.config = require(resolve(this.bPath, this.defaults.configPath)); return this; }, /** * Load Default Config * @public * @method loadCustomConfig * @param cfg {Object} composer config file * @return Composer **/ loadCustomConfig: function(cfg) { if(!cfg.uPath) { Logger.log('[COMPOSER] Loaded Default Application Config file.', { nl: true }); return this; } try { var configPath = resolve(cfg.uPath, cfg.config); this.uPath = cfg.uPath; this.config = require(configPath); Logger.log('[COMPOSER] Loading Config file [' + configPath + ']', { nl: true }); } catch(ex) { Utils.onError(ex.message); } return this; }, /** * Output options selected in the command line * @public * @method output * @param cfg {Object} composer config file **/ output: function(cfg) { Logger.debug('Configured Settings:', { nl: true }); Logger.debug('\tSource Path: ' + this.source + '\n\tConfig File: ' + ((!cfg.config) ? 'Using Default (Custom not specified)' : cfg.config) + '\n\tClear on Exit: ' + ((this.defaults.clear) ? 'Activated' : 'Deactivated') + '\n\tListening on: http://' + this.host + ':' + this.defaults.port + '/'); }, /** * Load Config files, default one and if provided custom one * @public * @method loadConfig * @param cfg {Object} composer config file * @return Composer **/ setup: function(cfg) { if(this.config && !_.isObject(this.config)) Utils.onError('[COMPOSER] Malformed Custom Config Aapplication File.'); _.extend(this, _.omit(this.defaults.config, 'require'), (this.config) ? _.omit(this.config, 'mainSpec', 'require') : {}); if(!_.isUndefined(cfg.clear)) this.defaults.clear = cfg.clear; if(!_.isUndefined(cfg.port)) this.defaults.port = cfg.port; if(!_.isUndefined(cfg.host)) this.host = cfg.host; this.source = resolve((this.uPath) ? this.uPath : this.bPath, this.source); this.target = resolve((this.uPath) ? this.uPath : this.bPath, this.target); this.defaults.template = resolve(this.bPath, this.defaults.template); return this.createTarget().copyAssets(); }, /** * Create Target Folder * @private * @method createTarget * @return Composer **/ createTarget: function() { Utils.createDir(this.target); return this; }, /** * Copy Assets and Main file used relative to the source path to be deployed in the target * @public * @method copyAssets * @return Composer **/ copyAssets: function() { this.main = (this.config && this.config.requireMain) ? this.config.requireMain : this.defaults.requireMain; Utils.copyFile(resolve(this.source, this.main), resolve(this.target, this.main)); return this; }, /** * Creates the temporal target folder to launch the composer tool * By copying the an index.html, the require main file in use and a initial spec (if specified). * @public * @method createTarget * @return Composer **/ export: function() { var tpl = fs.readFileSync(this.defaults.template, "utf8"), output = _.template(tpl, { name: this.name, version: pkg.version, host: this.host, requireMain: Utils.getFilename(this.main, true), mainSpec: (this.uPath) ? this.config.mainSpec : this.defaults.config.mainSpec, core: this.core }); Utils.createFile(resolve(this.target, './index.html'), output); Logger.warn('[COMPOSER] Composer Environment [Ready]'); return this; }, /** * Spin Up the server with autowatch support to track changes in the project * @public * @method spinUpAutowatch **/ spinUpServer: function() { var server = connect() .use(connect.favicon(resolve(__dirname, './favicon.ico'), { maxAge: 1 })) .use(compression({ threshold: 0 })) .use(connect.static(this.target)) .use(connect.static(this.source)); if(!this.config) server.use(connect.static(resolve(this.bPath, './dist'))); server.listen(this.defaults.port); this.spinUpAutoWatch(); watch.createMonitor(this.source, { ignoreDotFiles: true, ignoreUnreadableDir: true }, _.bind(this.onFileChange, this)); Logger.log('[COMPOSER] Server listening on port ' + this.defaults.port + '...', { nl: true }); }, /** * Spins Up AutoWatch service on source path * @public * @method spinUpAutoWatch **/ spinUpAutoWatch: function() { this.live.server = http.createServer(); this.live.socket = io(this.live.server); this.live.socket.on('connect', _.bind(function(client) { Logger.debug('[COMPOSER] Client Binded [' + client.id + '] - Total Connected [' + _.keys(this.live.socket.sockets.connected).length + ']'); }, this)); this.live.server.listen(9494, function() { Logger.log('[COMPOSER] AutoWatch Socket Service listening on port 9494...'); }); }, /** * File change handler for autowatch * @public * @method onFileChange * @param monitor {Object} monitor reference **/ onFileChange: function(monitor) { monitor.on("created", _.bind(function(f, stat) { Logger.debug('[COMPOSER] File Created [' + f + '] Refreshing browser...'); this.live.socket.emit('reload'); }, this)); monitor.on("changed", _.bind(function (f, curr, prev) { Logger.debug('[COMPOSER] File Modified [' + f + '] Refreshing browser...'); this.live.socket.emit('reload'); }, this)); monitor.on("removed", _.bind(function (f, stat) { Logger.debug('[COMPOSER] File Removed [' + f + '] Refreshing browser...'); this.live.socket.emit('reload'); }, this)); } }; module.exports = Composer;