├── .gitignore ├── LICENSE ├── README.md ├── examples ├── short │ ├── README.md │ ├── google.js │ └── package.json └── static │ ├── Makefile │ ├── README.md │ └── src │ ├── dist │ └── index.html │ ├── main.js │ ├── package.json │ └── server.js ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .cache/ 2 | node_modules/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Max Franks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # electronify-server 2 | 3 | `electronify-server` is a lightweight tool which starts a local web server and opens a url in an Electron shell. 4 | 5 | #### Installation 6 | 7 | ``` 8 | npm install --save electronify-server 9 | ``` 10 | 11 | ## How does it work? 12 | 13 | When the Electron app loads, `electronify-server` will start a child process with the command you gave it in the configuration. This command is assumed to be a web server (but it doesn't have to be). If the child process was started successfully, the window will open and load the url to your server. 14 | 15 | ## Examples 16 | 17 | There are a couple examples included in the repo. In order to run the examples, you need to have electron installed. If you do not have it installed, perhaps the simplest way is to use `electron-prebuilt` like so: 18 | 19 | ``` 20 | npm install -g electron-prebuilt 21 | ``` 22 | 23 | To run the examples, simply go into each example folder and run: 24 | 25 | ``` 26 | electron . 27 | ``` 28 | 29 | The `static` example has a dependency that will need to be installed first via: 30 | 31 | ``` 32 | npm install 33 | ``` 34 | 35 | ### Short Example 36 | 37 | ```js 38 | var electronify = require('electronify-server'); 39 | 40 | electronify({ 41 | url: 'https://google.com', 42 | noServer: true 43 | }); 44 | ``` 45 | 46 | ### Long Example 47 | 48 | ```js 49 | var electronify = require('electronify-server'); 50 | 51 | electronify({ 52 | command: 'python -m SimpleHTTPServer', 53 | url: 'http://127.0.0.1:8000', 54 | debug: true, 55 | window: {height: 768, width: 1024}, 56 | ready: function(app){ 57 | // application event listeners could be added here 58 | }, 59 | preLoad: function(app, window){ 60 | // window event listeners could be added here 61 | }, 62 | postLoad: function(app, window){ 63 | // url finished loading 64 | }, 65 | showDevTools: false 66 | }).on('child-started', function(child) { 67 | // child process has started 68 | console.log('PID: ' + child.pid); 69 | 70 | // setup logging on child process 71 | child.stdout.on('data', console.log); 72 | child.stderr.on('data', console.log); 73 | 74 | }).on('child-closed', function(app, stderr, stdout) { 75 | // the child process has finished 76 | 77 | }).on('child-error', function(err, app) { 78 | // close electron if the child crashes 79 | app.quit(); 80 | }); 81 | ``` 82 | 83 | ## Configuration Options 84 | 85 | * `url [String]`: URL to load after child process starts 86 | * `command [String]`: command to start child process 87 | * `options [Object]`: options for [exec][2] 88 | * `debug [Boolean]`: enables debug output 89 | * `noServer [Boolean]`: allows urls to load without starting a child process 90 | * `showDevTools [Boolean]`: shows dev tools on startup 91 | * `window [Object]`: BrowserWindow configuration 92 | * `ready [Function]`: called when the application is ready 93 | * `app [Object]`: Electron application 94 | * `preLoad [Function]`: called after the window is created but before the url is loaded 95 | * `app [Object]`: Electron application 96 | * `window [Object]`: Electron BrowserWindow 97 | * `postLoad [Function]`: called after the url has finished loading 98 | * `app [Object]`: Electron application 99 | * `window [Object]`: Electron BrowserWindow 100 | 101 | #### Relevant documentation 102 | 103 | * [Node Child Process](https://nodejs.org/api/child_process.html#child_process_class_childprocess) 104 | * [Electron Application](https://github.com/atom/electron/blob/master/docs/api/app.md) 105 | * [Electron BrowserWindow](https://github.com/atom/electron/blob/master/docs/api/browser-window.md#new-browserwindowoptions) 106 | 107 | ## Events 108 | 109 | `electronify` also returns an EventEmitter which emits several events for the child process. This allows you to do additional work when the child process starts, exits or fails unexpectantly. 110 | 111 | * `child-started`: Emitted immediately on successful start of child process 112 | * `process`: The [child process][1] that was started 113 | * `child-closed`: Emitted when the child process exits gracefully. 114 | * `app`: Electron application 115 | * `stderr`: Standard error of child process 116 | * `stdout`: Standard output of child process 117 | * `child-error`: Emitted when the child process fails. 118 | * `err`: Error returned from executing child process. 119 | * `app`: Electron application 120 | * `error`: Configuration error 121 | * `err`: Configuration error 122 | * `app`: Electron application 123 | 124 | [1]: https://nodejs.org/api/child_process.html#child_process_class_childprocess "child process" 125 | [2]: https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback "child_process.exec" 126 | -------------------------------------------------------------------------------- /examples/short/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Installation 3 | 4 | ``` 5 | cd src && npm install 6 | ``` 7 | 8 | # Packaging 9 | 10 | Packaging required the `electron-packager` package. You can install it like so: 11 | 12 | ``` 13 | npm install --save-dev electron-packager 14 | ``` 15 | 16 | To build the package, from within `src` run: 17 | 18 | ``` 19 | ./node_modules/electron-packager/cli.js . Google --electron-version=1.4.14 --overwrite 20 | ``` 21 | -------------------------------------------------------------------------------- /examples/short/google.js: -------------------------------------------------------------------------------- 1 | var electronify = require('electronify-server'); 2 | 3 | electronify({ 4 | url: 'https://google.com', 5 | noServer: true, 6 | debug: true 7 | }); 8 | -------------------------------------------------------------------------------- /examples/short/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "main": "google.js", 5 | "devDependencies": { 6 | "electron-builder": "^7.9.0", 7 | "electron-packager": "^8.5.0" 8 | }, 9 | "dependencies": { 10 | "electronify-server": "^0.1.4" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/static/Makefile: -------------------------------------------------------------------------------- 1 | 2 | package: 3 | electron-packager src/ Matrix --platform=darwin --arch=x64 --version=1.4.14 --overwrite 4 | -------------------------------------------------------------------------------- /examples/static/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Installation 3 | 4 | ``` 5 | cd src && npm install 6 | ``` 7 | 8 | # Packaging 9 | 10 | Packaging required the `electron-packager` package. You can install it like so: 11 | 12 | ``` 13 | npm install --save-dev electron-packager 14 | ``` 15 | 16 | To build the package, from within `src` run: 17 | 18 | ``` 19 | ./node_modules/electron-packager/cli.js . Matrix --electron-version=1.4.14 --overwrite 20 | ``` 21 | -------------------------------------------------------------------------------- /examples/static/src/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Neo 6 | 19 | 20 | 21 | electronify 22 | 23 | 24 | -------------------------------------------------------------------------------- /examples/static/src/main.js: -------------------------------------------------------------------------------- 1 | var electronify = require('electronify-server'); 2 | const {dialog} = require('electron') 3 | 4 | electronify({ 5 | command: 'node', 6 | args: ['server.js'], 7 | options: { 8 | cwd: './Resources/app' 9 | }, 10 | url: 'http://127.0.0.1:8080', 11 | debug: true, 12 | window: {height: 768, width: 1024, 'title-bar-style': 'default', frame: true}, 13 | ready: function(app){ 14 | // application event listeners could be added here 15 | console.log(process.cwd()); 16 | }, 17 | preLoad: function(app, window){ 18 | // window event listeners could be added here 19 | }, 20 | postLoad: function(app, window, error){ 21 | // Error only exists if there was an error while loading 22 | // error == { 23 | // event: event, 24 | // errorCode: errorCode, 25 | // errorDescription: errorDescription, 26 | // validatedURL: validatedURL, 27 | // isMainFrame: isMainFrame 28 | // } 29 | if (error) { 30 | console.log(error.errorCode, error.errorDescription, error.validatedURL); 31 | } 32 | 33 | // url finished loading 34 | }, 35 | showDevTools: false 36 | }).on('child-started', function(child) { 37 | // child process has started 38 | console.log('PID: ' + child.pid); 39 | 40 | // setup logging on child process 41 | child.stdout.on('data', function(buffer) { 42 | console.log(buffer.toString()) 43 | }); 44 | child.stderr.on('data', function(buffer) { 45 | console.log(buffer.toString()) 46 | }); 47 | 48 | }).on('child-closed', function(app, stderr, stdout) { 49 | // the child process has finished 50 | 51 | }).on('child-error', function(err, app) { 52 | // close electron if the child crashes 53 | console.log(err); 54 | 55 | // app.quit(); 56 | // throw err; 57 | }); 58 | -------------------------------------------------------------------------------- /examples/static/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "main": "main.js", 5 | "dependencies": { 6 | "node-static": "^0.7.7", 7 | "electronify-server": "^0.5.1" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/static/src/server.js: -------------------------------------------------------------------------------- 1 | var static = require('node-static'); 2 | var file = new static.Server('./dist'); 3 | 4 | require('http').createServer(function (request, response) { 5 | request.addListener('end', function () { 6 | file.serve(request, response); 7 | }).resume(); 8 | }).listen(8080); 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* eslint no-path-concat: 0, func-names:0 */ 4 | 5 | // Imports 6 | const electron = require('electron'); 7 | const {app} = electron; 8 | const {BrowserWindow} = electron; 9 | 10 | var util = require('util'); 11 | var colors = require('colors'); 12 | var EventEmitter = require('events'); 13 | var spawn = require('child-process-promise').spawn; 14 | 15 | var DEFAULT_WINDOW = { width: 1024, height: 728, show: false }; 16 | var NOOP = function(){}; 17 | var DEBUG = function(text) { 18 | var time = new Date().toLocaleTimeString(); 19 | console.log('['.grey + time.green + ']'.grey + colors.reset(' ' + text)); 20 | }; 21 | 22 | function electronify(cfg) { 23 | var self = this; 24 | 25 | // Initialize necessary properties from `EventEmitter` in this instance 26 | EventEmitter.call(self); 27 | 28 | // setup debug logger 29 | var debug = NOOP; 30 | if (cfg.debug) { 31 | debug = DEBUG; 32 | } 33 | 34 | app.on('window-all-closed', function() { 35 | debug('All windows closed.'); 36 | 37 | // quit after last window is closed 38 | app.quit(); 39 | }); 40 | 41 | app.on('ready', function() { 42 | debug('App ready.'); 43 | if (cfg.ready) cfg.ready(app); 44 | 45 | // The app will start with no command if the noServer option is true. 46 | if (cfg.noServer) { 47 | start(cfg, app, null, debug); 48 | return 49 | 50 | // The app will quit if there is no command to run. 51 | } else if (!cfg.noServer && !cfg.command) { 52 | debug('No command configured!'); 53 | self.emit('error', new Error('Empty command!'), app); 54 | return 55 | } 56 | 57 | // start child process 58 | var args = cfg.args? cfg.args : []; 59 | var opts = cfg.options? cfg.options: {}; 60 | spawn(cfg.command, args, opts) 61 | .then(function (result) { 62 | debug('Command completed.'); 63 | 64 | // Send output 65 | var stdout = result.stdout; 66 | var stderr = result.stderr; 67 | self.emit('child-closed', app, stderr, stdout); 68 | }) 69 | .fail(function (err) { 70 | debug(err); 71 | self.emit('child-error', err, app); 72 | // app.quit(); 73 | }) 74 | .progress(function (childProcess) { 75 | debug('Command started. [PID: ' + childProcess.pid + ']'); 76 | self.emit('child-started', childProcess); 77 | 78 | start(cfg, app, childProcess, debug); 79 | }); 80 | }); 81 | } 82 | 83 | // Inherit functions from `EventEmitter`'s prototype 84 | util.inherits(electronify, EventEmitter); 85 | 86 | module.exports = function(cfg) { 87 | return new electronify(cfg); 88 | }; 89 | 90 | function start(cfg, app, childProcess, debug) { 91 | app.on('quit', function() { 92 | if(childProcess && !childProcess.killed) childProcess.kill(); 93 | }); 94 | 95 | // setup window config 96 | var browserConfig = cfg.window || DEFAULT_WINDOW ; 97 | browserConfig.show = false; 98 | 99 | // create window 100 | var window = new BrowserWindow(browserConfig); 101 | 102 | // call pre load handler 103 | // menus could be created in this function 104 | // window and app event handlers could also be added here 105 | if (cfg.preLoad) cfg.preLoad(app, window); 106 | 107 | // health check to verify server started 108 | if (cfg.healthCheck) { 109 | cfg.healthCheck(app, window, function() { 110 | window.loadURL(cfg.url); 111 | }) 112 | } else { 113 | // load url 114 | window.loadURL(cfg.url); 115 | } 116 | 117 | window.webContents.on('did-finish-load', function() { 118 | debug('Finished loading.'); 119 | 120 | // call post load handler 121 | if (cfg.postLoad) cfg.postLoad(app, window); 122 | 123 | window.show(); 124 | }); 125 | 126 | window.webContents.on('did-fail-load', function(event, errorCode, errorDescription, validatedURL, isMainFrame) { 127 | debug('Loading failed.'); 128 | 129 | var error = { 130 | event: event, 131 | errorCode: errorCode, 132 | errorDescription: errorDescription, 133 | validatedURL: validatedURL, 134 | isMainFrame: isMainFrame 135 | } 136 | 137 | if (cfg.postLoad) cfg.postLoad(app, window, error); 138 | }); 139 | 140 | // Clean resource 141 | window.on('closed', function() { 142 | debug('Window closed.'); 143 | window = null; 144 | }); 145 | 146 | // Enable dev tools 147 | if (cfg.showDevTools) window.openDevTools(); 148 | } 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electronify-server", 3 | "version": "0.6.2", 4 | "description": "electronify-server is a tool which presents local web servers in an Electron shell.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/eliquious/electronify-server" 12 | }, 13 | "keywords": [ 14 | "electron", 15 | "desktop", 16 | "atom" 17 | ], 18 | "author": "Max Franks", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/eliquious/electronify-server/issues" 22 | }, 23 | "homepage": "https://github.com/eliquious/electronify-server", 24 | "dependencies": { 25 | "child-process-promise": "^2.2.0", 26 | "colors": "^1.1.2" 27 | } 28 | } 29 | --------------------------------------------------------------------------------