UNPKG

hihat

Version:

local Node/Browser development with Chrome DevTools

238 lines (206 loc) 6.33 kB
var assign = require('object-assign') var path = require('path') var serializerr = require('serializerr') var getPort = require('getport') var parseArgs = require('./lib/parse-args') var defaults = require('lodash.defaults') var globby = require('globby') var createHihat = require('./lib/hihat') module.exports = hihat module.exports.fromArgs = fromArgs function fromArgs (args, opt) { var cliOpts = parseArgs(args) return hihat(assign(cliOpts, opt)) } function hihat (opts) { // require these at runtime var app = require('app') app.commandLine.appendSwitch('disable-http-cache') app.commandLine.appendSwitch('v', 0) app.commandLine.appendSwitch('vmodule', 'console=0') var BrowserWindow = require('browser-window') var globalShortcut = require('global-shortcut') opts = assign({}, opts) // ensure defaults like devtool / electron-builtins are set defaults(opts, parseArgs.defaults) var entries = opts.entries || [] if (typeof entries === 'string') { entries = [ entries ] } if (opts.exec) { opts.devtool = false opts.quit = true opts.print = true } var basedir = opts.basedir || process.cwd() Error.stackTraceLimit = Infinity // this will allow the Chrome Console to also // find modules within the current working directory if (opts.node) { var findNodeModules = require('find-node-modules') process.env.NODE_PATH = findNodeModules({ cwd: basedir, relative: false }).join(path.delimiter) } var hihat var mainWindow = null var lastError = null app.on('window-all-closed', close) process.on('uncaughtException', function (err) { process.stderr.write((err.stack ? err.stack : err) + '\n') if (opts.quit) { close() } else { lastError = err printLastError() } }) function close () { app.quit() if (hihat) { hihat.close() } } app.on('ready', function () { var basePort = opts.port || 9541 getPort(basePort, function (err, port) { if (err) { console.error('Could not get available port') process.exit(1) } var unparsedArgs = opts.browserifyArgs globby(entries).then(function (entryFiles) { start(assign({}, opts, { entries: entryFiles, browserifyArgs: entryFiles.concat(unparsedArgs), port: port, host: opts.host || 'localhost', dir: opts.dir || process.cwd() })) }, function (err) { console.error('Error with glob patterns: ' + err.message) process.exit(1) }).catch(function (err) { console.error('Error running hihat: ' + err.message) process.exit(1) }) }) }) function start (opt) { hihat = createHihat(opt) .on('connect', function (ev) { var bounds = parseBounds(opts.frame) // a hidden browser window mainWindow = new BrowserWindow(assign({ 'node-integration': opts.node, 'use-content-size': true }, bounds, { preload: getPrelude(), icon: path.join(__dirname, 'img', 'logo-thumb.png') })) // reload page shortcuts setupShortcuts() var webContents = mainWindow.webContents webContents.once('did-start-loading', function () { if (opts.devtool !== false) { mainWindow.openDevTools({ detach: true }) } }) webContents.once('did-frame-finish-load', function () { mainWindow.loadURL(ev.uri) mainWindow.once('dom-ready', function () { printLastError() }) if (typeof opts.timeout === 'number') { setTimeout(function () { close() }, opts.timeout) } }) mainWindow.show() // REPL with no browserify entries if (entries.length === 0) { mainWindow.reload() } mainWindow.once('closed', function () { mainWindow = null hihat.close() }) }) .on('update', function () { if (mainWindow) { mainWindow.reload() } }) } function setupShortcuts () { app.on('browser-window-focus', function () { globalShortcut.register('CmdOrCtrl+R', refresh) globalShortcut.register('F5', refresh) }) app.on('browser-window-blur', function () { globalShortcut.unregister('CmdOrCtrl+R') globalShortcut.unregister('F5') }) } function refresh () { if (mainWindow) mainWindow.reload() } function parseBounds (frame) { var bounds = { width: 0, height: 0, x: 0, y: 0 } if (typeof frame === 'string') { var parts = frame.split(',').map(function (x) { return parseInt(x, 10) }) if (parts.length === 2) { bounds = { width: parts[0], height: parts[1]} } else if (parts.length === 4) { bounds.x = parts[0] bounds.y = parts[1] bounds.width = parts[2] bounds.height = parts[3] } else { throw new Error('must specify 2 or 4 values for --frame') } } else if (frame) { bounds = {} // allow programmatic frame object if (typeof frame.x === 'number') bounds.x = frame.x if (typeof frame.y === 'number') bounds.y = frame.y if (typeof frame.width === 'number') bounds.width = frame.width if (typeof frame.height === 'number') bounds.height = frame.height } return bounds } function printLastError () { if (!mainWindow || !lastError) return var err = serializerr(lastError) mainWindow.webContents.executeJavaScript([ '(function() {', // simulate server-side Error object 'var errObj = ' + JSON.stringify(err), 'var err = new Error()', 'mixin(err, errObj)', 'try {throw err} catch(e) {console.error(e)}', 'function mixin(a, b) { for (var key in b) a[key] = b[key] }', '})()' ].join('\n')) lastError = null } function getPrelude () { var name if (opts.node && opts.print) { name = 'node-console.js' } else if (opts.node) { name = 'node.js' } else if (opts.print) { name = 'console.js' } if (name) { return path.resolve(__dirname, 'lib', 'prelude', name) } else { return undefined } } }