1 | "use strict";
|
2 |
|
3 | var through = require('through2'),
|
4 | gutil = require('gulp-util'),
|
5 | http = require('http'),
|
6 | https = require('https'),
|
7 | inject = require('connect-inject'),
|
8 | connect = require('connect'),
|
9 | proxy = require('proxy-middleware'),
|
10 | watch = require('node-watch'),
|
11 | fs = require('fs'),
|
12 | serveIndex = require('serve-index'),
|
13 | serveStatic = require('serve-static'),
|
14 | path = require('path'),
|
15 | open = require('open'),
|
16 | enableMiddlewareShorthand = require('./enableMiddlewareShorthand'),
|
17 | socket = require('socket.io'),
|
18 | url = require('url'),
|
19 | extend = require('node.extend');
|
20 |
|
21 |
|
22 | var BROWSER_SCIPTS_DIR = path.join(__dirname, 'browser-scripts');
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | module.exports = function(options) {
|
28 | var defaults = {
|
29 | |
30 |
|
31 |
|
32 |
|
33 |
|
34 | host: 'localhost',
|
35 | port: 8000,
|
36 | defaultFile: 'index.html',
|
37 | fallback: null,
|
38 | https: false,
|
39 | open: false,
|
40 | log: 'info',
|
41 |
|
42 | |
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | livereload: {
|
58 | enable: false,
|
59 | port: 35729,
|
60 | filter: function(filename, cb) {
|
61 | cb( !(/node_modules/.test(filename)) );
|
62 | },
|
63 | clientConsole: false,
|
64 | },
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | directoryListing: {
|
70 | enable: false,
|
71 | path: './',
|
72 | options: undefined
|
73 | },
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | proxies: []
|
79 |
|
80 | };
|
81 |
|
82 |
|
83 |
|
84 | var config = enableMiddlewareShorthand(defaults, options, ['directoryListing', 'livereload']);
|
85 |
|
86 | var openInBrowser = function () {
|
87 | if (config.open === false) return;
|
88 | open('http' + (config.https ? 's' : '') + '://' + config.host + ':' + config.port);
|
89 | openInBrowser = undefined;
|
90 | };
|
91 |
|
92 |
|
93 | var app = connect();
|
94 |
|
95 | for (var i = 0, len = config.proxies.length; i < len; i++) {
|
96 | var proxyoptions = url.parse(config.proxies[i].target);
|
97 | if (config.proxies[i].hasOwnProperty('options')) {
|
98 | extend(proxyoptions, config.proxies[i].options);
|
99 | }
|
100 |
|
101 | proxyoptions.route = config.proxies[i].source;
|
102 | app.use(proxy(proxyoptions));
|
103 |
|
104 | gutil.log(config.proxies[i].source + ' is proxied.');
|
105 | }
|
106 |
|
107 | if (config.directoryListing.enable) {
|
108 | app.use(serveIndex(path.resolve(config.directoryListing.path), config.directoryListing.options));
|
109 | }
|
110 |
|
111 |
|
112 | if (config.livereload.enable) {
|
113 | var ioServerOrigin = 'http://' + config.host + ':' + config.livereload.port;
|
114 |
|
115 | var snippetParams = [];
|
116 |
|
117 | if (config.livereload.clientConsole) {
|
118 | snippetParams.push("extra=capture-console");
|
119 | }
|
120 |
|
121 | var snippet =
|
122 | "<script type='text/javascript' async defer src='"
|
123 | + ioServerOrigin
|
124 | + "/livereload.js?"
|
125 | + snippetParams.join('&')
|
126 | + "'></script>";
|
127 |
|
128 | app.use(inject({
|
129 | snippet: snippet,
|
130 | rules: [{
|
131 | match: /<\/body>/,
|
132 | fn: function(w, s) {
|
133 | return s + w;
|
134 | }
|
135 | }]
|
136 | }));
|
137 |
|
138 | var io = config.livereload.io = socket();
|
139 | io.serveClient(true);
|
140 | io.path("");
|
141 | io.on('connection', function(socket){
|
142 | gutil.log('Livereload client connected');
|
143 |
|
144 | socket.on('console_log', function(data){
|
145 | var args = [
|
146 | gutil.colors.green('log')
|
147 | ];
|
148 | for (var i in data) {
|
149 | args.push(data[i]);
|
150 | }
|
151 | gutil.log.apply(null, args);
|
152 | });
|
153 | socket.on('console_warn', function(data){
|
154 | var args = [
|
155 | gutil.colors.yellow('warn')
|
156 | ];
|
157 | for (var i in data) {
|
158 | args.push(data[i]);
|
159 | }
|
160 | gutil.log.apply(null, args);
|
161 | });
|
162 | socket.on('console_info', function(data){
|
163 | var args = [
|
164 | gutil.colors.cyan('info')
|
165 | ];
|
166 | for (var i in data) {
|
167 | args.push(data[i]);
|
168 | }
|
169 | gutil.log.apply(null, args);
|
170 | });
|
171 | socket.on('console_error', function(data){
|
172 | var args = [
|
173 | gutil.colors.red('err')
|
174 | ];
|
175 | for (var i in data) {
|
176 | args.push(data[i]);
|
177 | }
|
178 | gutil.log.apply(null, args);
|
179 | });
|
180 | });
|
181 |
|
182 | var ioApp = connect();
|
183 |
|
184 | ioApp.use(serveStatic(BROWSER_SCIPTS_DIR, { index: false }));
|
185 |
|
186 | var ioServer = config.livereload.ioServer =
|
187 | http.createServer(ioApp).listen(config.livereload.port, config.host);
|
188 |
|
189 | io.attach(ioServer, {
|
190 | path: '/socket.io'
|
191 | });
|
192 | }
|
193 |
|
194 |
|
195 | var webserver = null;
|
196 | if (config.https) {
|
197 | var options = {
|
198 | key: fs.readFileSync(config.https.key || __dirname + '/../ssl/dev-key.pem'),
|
199 | cert: fs.readFileSync(config.https.cert || __dirname + '/../ssl/dev-cert.pem')
|
200 | };
|
201 |
|
202 | webserver = https.createServer(options, app);
|
203 | }
|
204 | else {
|
205 | webserver = http.createServer(app);
|
206 | }
|
207 |
|
208 | var files = [];
|
209 |
|
210 |
|
211 | var stream = through.obj(function(file, enc, callback) {
|
212 | if ('debug' === config.log) {
|
213 | app.use(function(req, res, next) {
|
214 | gutil.log(req.method + ' ' + req.url);
|
215 |
|
216 | next();
|
217 | });
|
218 | }
|
219 |
|
220 |
|
221 | app.use(serveStatic(file.path, {
|
222 | index: (config.directoryListing.enable ? false : config.defaultFile)
|
223 | }));
|
224 |
|
225 | if (config.livereload.enable) {
|
226 | watch(file.path, function(filename) {
|
227 | config.livereload.filter(filename, function(shouldReload) {
|
228 | if (shouldReload) {
|
229 | gutil.log('Livereload: file changed: ' + filename);
|
230 |
|
231 | config.livereload.io.sockets.emit('reload');
|
232 |
|
233 | filename = filename.replace(/\.map$/, '');
|
234 |
|
235 | config.livereload.io.sockets.emit('file_changed', {
|
236 | path: filename,
|
237 | name: path.basename(filename),
|
238 | ext: path.extname(filename),
|
239 | });
|
240 | }
|
241 | });
|
242 | });
|
243 | }
|
244 |
|
245 | this.push(file);
|
246 |
|
247 | callback();
|
248 | })
|
249 | .on('data', function(f) {
|
250 | files.push(f);
|
251 |
|
252 |
|
253 | webserver.listen(config.port, config.host, openInBrowser);
|
254 |
|
255 | gutil.log('Webserver started at', gutil.colors.cyan('http' + (config.https ? 's' : '') + '://' + config.host + ':' + config.port));
|
256 | })
|
257 | .on('end', function(){
|
258 | if (config.fallback) {
|
259 | files.forEach(function(file){
|
260 | var fallbackFile = file.path + '/' + config.fallback;
|
261 | if (fs.existsSync(fallbackFile)) {
|
262 | app.use(function(req, res) {
|
263 | res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
264 | fs.createReadStream(fallbackFile).pipe(res);
|
265 | });
|
266 | }
|
267 | });
|
268 | }
|
269 | });
|
270 |
|
271 |
|
272 |
|
273 | stream.on('kill', function() {
|
274 | webserver.close();
|
275 |
|
276 | if (config.livereload.enable) {
|
277 | config.livereload.ioServer.close();
|
278 | }
|
279 | });
|
280 |
|
281 | return stream;
|
282 | };
|