├── .gitignore ├── .travis.yml ├── test └── index.js ├── package.json ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .idea 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //TODO add tests 4 | describe('gulp-express', function() { 5 | 6 | it('works', function(done) { 7 | done(); 8 | }); 9 | 10 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-express", 3 | "version": "0.3.6", 4 | "description": "gulp livereload plugin", 5 | "homepage": "https://github.com/gimm/gulp-express", 6 | "main": "./index.js", 7 | "scripts": { 8 | "test": "mocha", 9 | "install": "echo \"*** Please use [gulp-live-server] instead! *** \"" 10 | }, 11 | "keywords": [ 12 | "gulpplugin", 13 | "livereload", 14 | "server", 15 | "express" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/gimm/gulp-express" 20 | }, 21 | "author": { 22 | "name": "yucc2008@gmail.com" 23 | }, 24 | "license": "WTFPL", 25 | "dependencies": { 26 | "chalk": "^1.0.0", 27 | "debug": "^2.1.1", 28 | "deepmerge": "~0.2.7", 29 | "event-stream": "~3.2.1", 30 | "tiny-lr": "0.0.9" 31 | }, 32 | "readmeFilename": "README.md", 33 | "bugs": { 34 | "url": "https://github.com/gimm/gulp-express/issues" 35 | }, 36 | "devDependencies": { 37 | "mocha": "^2.0.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | please use [gulp-live-server](https://github.com/gimm/gulp-live-server) instead, it's a new version of `gulp-express` with a better name and new features. 2 | === 3 | 4 | [![Build Status][1]][2] [![Livereload downloads][3]][4] [![Tag][9]][8] [![MIT Licensed][5]](http://www.wtfpl.net/) 5 | 6 | [1]: http://img.shields.io/travis/gimm/gulp-express/master.svg 7 | [2]: https://travis-ci.org/gimm/gulp-express 8 | 9 | [3]: http://img.shields.io/npm/dm/gulp-express.svg 10 | [4]: https://www.npmjs.com/package/gulp-express 11 | 12 | [5]: http://img.shields.io/badge/license-WTFPL-blue.svg 13 | 14 | [8]: https://github.com/gimm/gulp-express/releases 15 | [9]: https://img.shields.io/github/tag/gimm/gulp-express.svg 16 | 17 | A gulp plugin which serve the app with livereload, internally, it does the following: 18 | * use [`ChildProcess.spawn`](http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) to start a node process; 19 | * use [`tiny-lr`](https://github.com/mklabs/tiny-lr) provide livereload ability; 20 | 21 | ## Install 22 | [![NPM](https://nodei.co/npm/gulp-express.png?compact=true)](https://nodei.co/npm/gulp-express/) 23 | 24 | ## Update notice 25 | * v0.3.0 26 | 27 | > change signature of `server.run`. the third param `livereload` is used to config tiny-lr server. 28 | 29 | * v0.2.0 30 | 31 | > get `console.log` back. 32 | 33 | * v0.1.12 34 | 35 | > `options.lr` is used for creating tiny-lr server. `options` here is the second parameter for [server.run](#serverrunargsoptions). 36 | 37 | * v0.1.7 38 | > change signature for [server.run](#serverrunargsoptions), split `options` into `args` and `options`. 39 | 40 | * v0.1.5 41 | > pipe support added for [server.notify](#servernotifyevent) 42 | 43 | 44 | ## API 45 | 46 | ### server.run([args][,options][,livereload]) 47 | Run/re-run the script file, which will create a http(s) server. 48 | 49 | Start a livereload(tiny-lr) server if it's not started yet. 50 | 51 | Use the same arguments with [ChildProcess.spawn](http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) with 'node' as command. 52 | 53 | * `args` - `Array` - Array List of string arguments. The default value is `['app.js']`. 54 | * `options` - `Object` - The third parameter for [ChildProcess.spawn](http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options), the default value is: 55 | ```js 56 | options = { 57 | cwd: undefined 58 | } 59 | options.env = process.env; 60 | options.env.NODE_ENV = 'development'; 61 | ``` 62 | * `livereload` - `Boolean|Number|Object` - The option for tiny-lr server. The default value is `35729`. 63 | * `false` - will disable tiny-lr livereload server. 64 | * `number` - treated as port number of livereload server. 65 | * `object` - used to create tiny-lr server `new tinylr.Server(livereload);`. 66 | * Returns a [ChildProcess](http://nodejs.org/api/child_process.html#child_process_class_childprocess) instance of spawned server. 67 | 68 | ### server.stop() 69 | Stop the instantiated spawned server programmatically, and the tiny-lr server. 70 | 71 | ### server.notify([event]) 72 | Send a notification to the tiny-lr server in order to trigger a reload on page. 73 | pipe support is added after v0.1.5, so you can also do this: 74 | ```js 75 | gulp.src('css/*.css') 76 | // … 77 | .pipe(gulp.dest('public/css/')) 78 | .pipe(server.notify()) 79 | ``` 80 | * `event` (required when server.notify is invoked without pipe) - `Object` - Event object that is normally passed to [gulp.watch](https://github.com/gulpjs/gulp/blob/master/docs/API.md#cbevent) callback. 81 | Should contain `path` property with changed file path. 82 | 83 | ## Usage 84 | 85 | ```js 86 | // gulpfile.js 87 | var gulp = require('gulp'); 88 | var server = require('gulp-express'); 89 | 90 | gulp.task('server', function () { 91 | // Start the server at the beginning of the task 92 | server.run(['app.js']); 93 | 94 | // Restart the server when file changes 95 | gulp.watch(['app/**/*.html'], server.notify); 96 | gulp.watch(['app/styles/**/*.scss'], ['styles:scss']); 97 | //gulp.watch(['{.tmp,app}/styles/**/*.css'], ['styles:css', server.notify]); 98 | //Event object won't pass down to gulp.watch's callback if there's more than one of them. 99 | //So the correct way to use server.notify is as following: 100 | gulp.watch(['{.tmp,app}/styles/**/*.css'], function(event){ 101 | gulp.run('styles:css'); 102 | server.notify(event); 103 | //pipe support is added for server.notify since v0.1.5, 104 | //see https://github.com/gimm/gulp-express#servernotifyevent 105 | }); 106 | 107 | gulp.watch(['app/scripts/**/*.js'], ['jshint']); 108 | gulp.watch(['app/images/**/*'], server.notify); 109 | gulp.watch(['app.js', 'routes/**/*.js'], [server.run]); 110 | }); 111 | ``` 112 | ```js 113 | // app.js 114 | var express = require('express'); 115 | var app = module.exports.app = exports.app = express(); 116 | 117 | //you won't need 'connect-livereload' if you have livereload plugin for your browser 118 | app.use(require('connect-livereload')()); 119 | ``` 120 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Gimm on 7/17/14. 3 | */ 4 | 5 | var util = require('util'), 6 | path = require('path'), 7 | spawn = require('child_process').spawn, 8 | merge = require('deepmerge'), 9 | tinylr = require('tiny-lr'), 10 | es = require('event-stream'), 11 | chalk = require('chalk'), 12 | debug = require('debug')('gulp-express'); 13 | 14 | module.exports = (function () { 15 | var server = undefined, // the server child process 16 | lr = undefined, // tiny-lr server 17 | config = { 18 | args: ['app.js'], // args for child_process.spawn 19 | options: { // options for child_process.options 20 | cwd: undefined 21 | }, 22 | livereload: { //option for tiny-lr server 23 | port: 35729 24 | } 25 | }, 26 | info = chalk.gray, 27 | error = chalk.bold.red; 28 | config.options.env = process.env; 29 | config.options.env.NODE_ENV = 'development'; 30 | 31 | var callback = { 32 | processExit: function (code, sig) { 33 | debug(info('Main process exited with [code => %s | sig => %s]'), code, sig); 34 | server && server.kill(); 35 | }, 36 | 37 | serverExit: function (code, sig) { 38 | debug(info('server process exited with [code => %s | sig => %s]'), code, sig); 39 | if(sig !== 'SIGKILL'){ 40 | //server stopped unexpectedly 41 | if (lr) { 42 | lr.close(); 43 | } 44 | } 45 | }, 46 | 47 | lrServerReady: function () { 48 | console.log(info('livereload[tiny-lr] listening on %s ...'), config.livereload.port); 49 | }, 50 | 51 | serverLog: function (data) { 52 | console.log(info(data.trim())); 53 | }, 54 | 55 | serverError: function (data) { 56 | console.log(error(data.trim())); 57 | } 58 | }; 59 | 60 | return { 61 | /** 62 | * Start/restart a child process by running the script file, 63 | * this process work as the http(s) server; 64 | * And start a livereload(tiny-lr) server if it's not started yet. 65 | * 66 | * @param {array} [args] 67 | * @param {object} [options] 68 | * @param {boolean|number|object} [livereload] 69 | */ 70 | run: function (args, options, livereload) { 71 | //deal with args 72 | if(util.isArray(args) && args.length){ 73 | config.args = args; 74 | } 75 | 76 | //deal with options 77 | config.options = merge(config.options, options || {}); 78 | 79 | //deal with livereload 80 | if(livereload === false){ //livereload disabled 81 | config.livereload = false; 82 | }else if(livereload){ 83 | if(typeof livereload === 'object'){ 84 | config.livereload = livereload; 85 | }else{ 86 | config.livereload.port = livereload; 87 | } 88 | } 89 | 90 | if (server) { // server already running 91 | debug(info('kill server')); 92 | server.kill('SIGKILL'); 93 | //server.removeListener('exit', callback.serverExit); 94 | server = undefined; 95 | } else { 96 | if(config.livereload){ 97 | lr = tinylr(config.livereload); 98 | lr.listen(config.livereload.port, callback.lrServerReady); 99 | } 100 | } 101 | 102 | server = spawn('node', config.args, config.options); 103 | server.stdout.setEncoding('utf8'); 104 | server.stderr.setEncoding('utf8'); 105 | 106 | server.stdout.on('data', callback.serverLog); 107 | server.stderr.on('data', callback.serverError); 108 | server.once('exit', callback.serverExit); 109 | 110 | process.listeners('exit') || process.once('exit', callback.processExit); 111 | }, 112 | 113 | /** 114 | * Stop the server child process and the livereload server 115 | */ 116 | stop: function () { 117 | if (server) { 118 | debug(info('kill server')); 119 | //use SIGHUP instead of SIGKILL, see issue #34 120 | server.kill('SIGKILL'); 121 | //server.removeListener('exit', callback.serverExit); 122 | server = undefined; 123 | } 124 | if(lr){ 125 | debug(info('close livereload server')); 126 | lr.close(); 127 | //TODO how to stop tiny-lr from hanging the terminal 128 | lr = undefined; 129 | } 130 | }, 131 | 132 | /** 133 | * Tell the livereload.js to reload the resources that been changed 134 | * @param event 135 | * @returns {*} 136 | */ 137 | notify: function (event) { 138 | if(event && event.path){ 139 | var filepath = path.relative(__dirname, event.path); 140 | debug(info('file(s) changed: %s'), event.path); 141 | lr.changed({body: {files: [filepath]}}); 142 | } 143 | 144 | return es.map(function(file, done) { 145 | var filepath = path.relative(__dirname, file.path); 146 | debug(info('file(s) changed: %s'), filepath); 147 | lr.changed({body: {files: [filepath]}}); 148 | done(null, file); 149 | }); 150 | } 151 | }; 152 | })(); 153 | --------------------------------------------------------------------------------