├── .eslintrc ├── .gitignore ├── README.md ├── example ├── app.js ├── index.html ├── index.js └── package.json ├── lib └── index.js └── package.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends" : "eslint:recommended", 3 | "env": { 4 | "node": true, 5 | "browser": true 6 | }, 7 | "rules": { 8 | "quotes": [2, "single"], 9 | "strict": [2, "never"], 10 | "semi": 2 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # node.js 2 | node_modules/ 3 | npm-debug.log 4 | 5 | # osx 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Electron Hot Loader 2 | 3 | Live reload Scripts in Electron Applications. Under the hood it uses Chrome's remote debugger protocol, and reloads scripts after each change. The state of the application is maintained. 4 | 5 | ## INSTALLATION 6 | 7 | ```sh 8 | $ npm install electron-hot 9 | ``` 10 | 11 | ## USAGE 12 | 13 | In index.js: 14 | 15 | ```js 16 | require('electron-hot')(); 17 | ``` 18 | 19 | ### Options 20 | * port: Port for the debugger 21 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | var canvas = document.createElement('canvas'); 2 | canvas.style.position = 'absolute'; 3 | canvas.style.top = '0'; 4 | canvas.style.left = '0'; 5 | canvas.style.background = '#222222'; 6 | 7 | window.addEventListener('resize', function () { 8 | canvas.width = window.innerWidth; 9 | canvas.height = window.innerHeight; 10 | }); 11 | 12 | canvas.width = window.innerWidth; 13 | canvas.height = window.innerHeight; 14 | 15 | document.body.appendChild(canvas); 16 | 17 | var ctx = canvas.getContext('2d'); 18 | 19 | var balls = []; 20 | var start = null; 21 | 22 | function render(now) { 23 | window.requestAnimationFrame(render); 24 | 25 | if (!start) { 26 | start = now; 27 | } 28 | 29 | var seconds = (now - start) / 1000; 30 | 31 | ctx.clearRect(0, 0, canvas.width, canvas.height); 32 | 33 | ctx.fillStyle = '#ffffff'; 34 | ctx.font = '16px Helvetica Neue'; 35 | ctx.textAlign = 'center'; 36 | 37 | ctx.fillText(seconds.toFixed(1) + 's', canvas.width / 2, 32); 38 | 39 | var gravity = [0, 1]; 40 | 41 | balls.forEach(function(ball) { 42 | ball.position[0] += ball.velocity[0]; 43 | ball.position[1] += ball.velocity[1]; 44 | 45 | ball.velocity[0] += gravity[0]; 46 | ball.velocity[1] += gravity[1]; 47 | 48 | if (ball.position[1] + ball.radius > canvas.height) { 49 | ball.position[1] = canvas.height - ball.radius; 50 | ball.velocity[1] *= -ball.elasticity; 51 | if (Math.abs(ball.velocity[1]) < gravity[1]*2) { 52 | ball.velocity[1] = 0; 53 | } 54 | } 55 | ctx.beginPath(); 56 | ctx.arc(ball.position[0], ball.position[1], ball.radius, 0, 2 * Math.PI, false); 57 | ctx.fillStyle = '#ffffff'; 58 | ctx.fill(); 59 | ctx.closePath(); 60 | }); 61 | } 62 | 63 | window.requestAnimationFrame(render); 64 | 65 | window.addEventListener('click', function (evt) { 66 | balls.push({ 67 | position: [evt.x, evt.y], 68 | velocity: [0, 0], 69 | radius: 25, 70 | elasticity: 0.5 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Electron Hot Example 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | var app = require('app'); 2 | var BrowserWindow = require('browser-window'); 3 | var path = require('path'); 4 | 5 | require('crash-reporter').start(); 6 | 7 | require('..')(); 8 | 9 | var mainWindow; 10 | 11 | app.on('window-all-closed', function() { 12 | app.quit(); 13 | }); 14 | 15 | app.on('ready', function() { 16 | mainWindow = new BrowserWindow({ 17 | width: 800, 18 | height: 600 19 | }); 20 | mainWindow.loadUrl('file://' + path.join(__dirname, '/index.html')); 21 | mainWindow.on('close', function() { 22 | mainWindow = null; 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-hot", 3 | "version": "0.1.0", 4 | "description": "Hot loader for Electron", 5 | "main": "index.js", 6 | "author": "Calvin Bulla ", 7 | "license": "MIT" 8 | } 9 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var app = require('app'); 2 | var remote = require('chrome-remote-interface'); 3 | var chokidar = require('chokidar'); 4 | var fs = require('fs'); 5 | 6 | var scriptIds = {}; 7 | var watcher; 8 | 9 | function connectToClient(port) { 10 | return new Promise(function(resolve, reject) { 11 | remote({ 12 | port: port 13 | }, function(client) { 14 | resolve(client); 15 | }).on('error', function() { 16 | reject(Error('Unable to connect to client')); 17 | }); 18 | }); 19 | } 20 | 21 | module.exports = function(options) { 22 | options = options || {}; 23 | options.port = options.port || 9222; 24 | 25 | app.commandLine.appendSwitch('remote-debugging-port', options.port.toString()); 26 | 27 | app.on('quit', function() { 28 | if (watcher) { 29 | watcher.close(); 30 | watcher = null; 31 | } 32 | }); 33 | 34 | app.on('ready', function() { 35 | connectToClient(options.port).then(function(client) { 36 | client.Debugger.scriptParsed(function(script) { 37 | if (script.url.indexOf('file://') === -1) return; 38 | var url = script.url.replace('file://', ''); 39 | if (scriptIds[url]) return; 40 | scriptIds[url] = script.scriptId; 41 | if (!watcher) { 42 | watcher = chokidar.watch(url); 43 | watcher.on('change', function(path) { 44 | var scriptId = scriptIds[path]; 45 | fs.readFile(path, { 46 | encoding: 'utf-8' 47 | }, function(err, data) { 48 | if (err) throw err; 49 | client.Debugger.setScriptSource({ 50 | scriptId: scriptId, 51 | scriptSource: data 52 | }, function(err) { 53 | if (err) throw err; 54 | }); 55 | }); 56 | }); 57 | } else { 58 | watcher.add(url); 59 | } 60 | }); 61 | client.Debugger.enable(); 62 | }); 63 | }); 64 | 65 | }; 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electron-hot", 3 | "version": "0.2.0", 4 | "description": "Electron hot loader", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "example": "electron example" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/cabul/electron-hot.git" 12 | }, 13 | "keywords": [ 14 | "electron", 15 | "hot", 16 | "loader" 17 | ], 18 | "author": "Calvin Bulla ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/cabul/electron-hot/issues" 22 | }, 23 | "homepage": "https://github.com/cabul/electron-hot#readme", 24 | "dependencies": { 25 | "chokidar": "^1.0.5", 26 | "chrome-remote-interface": "^0.9.0" 27 | }, 28 | "devDependencies": { 29 | "electron-prebuilt": "^0.30.2", 30 | "webpack": "^1.11.0" 31 | } 32 | } 33 | --------------------------------------------------------------------------------