UNPKG

5.83 kBJavaScriptView Raw
1'use strict';
2/**
3 * Created by gimm on 3/13/2015.
4 */
5
6var util = require('util'),
7 path = require('path'),
8 assert = require('assert'),
9 spawn = require('child_process').spawn,
10 merge = require('deepmerge'),
11 tinylr = require('tiny-lr'),
12 es = require('event-stream'),
13 Q = require('q'),
14 chalk = require('chalk'),
15 debug = require('debug')('gulp-live-server');
16
17var info = chalk.gray,
18 error = chalk.bold.red;
19
20var callback = {
21 processExit: function (code, sig) {
22 debug(info('Main process exited with [code => %s | sig => %s]'), code, sig);
23 server && server.kill();
24 },
25
26 serverExit: function (code, sig) {
27 debug(info('server process exited with [code => %s | sig => %s]'), code, sig);
28 if(sig !== 'SIGKILL'){
29 //server stopped unexpectedly
30 process.exit(0);
31 }
32 },
33
34 lrServerReady: function () {
35 console.log(info('livereload[tiny-lr] listening on %s ...'), this.config.livereload.port);
36 },
37
38 serverLog: function (data) {
39 console.log(info(data.trim()));
40 },
41
42 serverError: function (data) {
43 console.log(error(data.trim()));
44 }
45};
46
47/**
48 * set config data for the new server child process
49 * @type {Function}
50 */
51module.exports = exports = (function() {
52 var defaults = {
53 options: {
54 cwd: undefined
55 },
56 livereload: {
57 port: 35729
58 }
59 };
60 defaults.options.env = Object.create(process.env);
61 defaults.options.env.NODE_ENV = 'development';
62
63 return function(args, options, livereload){
64 var config = {}
65 config.args = util.isArray(args) ? args : [args];
66 //deal with options
67 config.options = merge(defaults.options, options || {});
68 //deal with livereload
69 if (livereload) {
70 config.livereload = (typeof livereload === 'object' ? livereload : {port: livereload});
71 }else{
72 config.livereload = (livereload === false ? false : defaults.livereload);
73 }
74 // return exports with its state, the server and livereload instance
75 // this allows multiple servers at once
76 return merge({
77 config: config,
78 server: undefined, // the server child process
79 lr: undefined, // tiny-lr serverexports;
80 }, exports);
81 };
82})();
83
84/**
85* default server script, the static server
86*/
87exports.script = path.join(__dirname, 'scripts/static.js');
88
89/**
90* create a server child process with the script file
91*/
92exports.new = function (script) {
93 if(!script){
94 return console.log(error('script file not specified.'));
95 }
96 var args = util.isArray(script) ? script : [script];
97 return this(args);
98};
99
100/**
101* create a server child process with the static server script
102*/
103exports.static = function (folder, port) {
104 var script = this.script;
105 folder = folder || process.cwd();
106 util.isArray(folder) && (folder = folder.join(','));
107 port = port || 3000;
108 return this([script, folder, port]);
109};
110
111/**
112* start/restart the server
113*/
114exports.start = function () {
115 if (this.server) { // server already running
116 debug(info('kill server'));
117 this.server.kill('SIGKILL');
118 //server.removeListener('exit', callback.serverExit);
119 this.server = undefined;
120 } else {
121 if(this.config.livereload){
122 this.lr = tinylr(this.config.livereload);
123 this.lr.listen(this.config.livereload.port, callback.lrServerReady.bind(this));
124 }
125 }
126
127 var deferred = Q.defer();
128 this.server = spawn(process.execPath, this.config.args, this.config.options);
129 this.server.stdout.setEncoding('utf8');
130 this.server.stderr.setEncoding('utf8');
131
132 this.server.stdout.on('data', function (data) {
133 deferred.notify(data);
134 callback.serverLog(data);
135 });
136 this.server.stderr.on('data', function (data) {
137 deferred.notify(data);
138 callback.serverError(data);
139 });
140 this.server.once('exit', function (code, sig) {
141 setTimeout(function() { // yield event loop for stdout/stderr
142 deferred.resolve({
143 code: code,
144 signal: sig
145 });
146 callback.serverExit(code, sig);
147 }, 0)
148 });
149
150 process.listeners('exit') || process.once('exit', callback.processExit);
151
152 return deferred.promise;
153};
154
155/**
156* stop the server
157*/
158exports.stop = function () {
159 var deferred = Q.defer();
160 if (this.server) {
161 this.server.once('exit', function (code) {
162 deferred.resolve(code);
163 });
164
165 debug(info('kill server'));
166 //use SIGHUP instead of SIGKILL, see issue #34
167 this.server.kill('SIGKILL');
168 //server.removeListener('exit', callback.serverExit);
169 this.server = undefined;
170 }else{
171 deferred.resolve(0);
172 }
173 if(this.lr){
174 debug(info('close livereload server'));
175 this.lr.close();
176 //TODO how to stop tiny-lr from hanging the terminal
177 this.lr = undefined;
178 }
179
180 return deferred.promise;
181};
182
183/**
184* tell livereload.js to reload the changed resource(s)
185*/
186exports.notify = function (event) {
187 if(event && event.path){
188 var filepath = path.relative(__dirname, event.path);
189 debug(info('file(s) changed: %s'), event.path);
190 this.lr.changed({body: {files: [filepath]}});
191 }
192
193 return es.map(function(file, done) {
194 var filepath = path.relative(__dirname, file.path);
195 debug(info('file(s) changed: %s'), filepath);
196 this.lr.changed({body: {files: [filepath]}});
197 done(null, file);
198 });
199};