fix
This commit is contained in:
94
book/node_modules/tiny-lr/lib/client.js
generated
vendored
Normal file
94
book/node_modules/tiny-lr/lib/client.js
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
import events from 'events';
|
||||
import WebSocket from 'faye-websocket';
|
||||
import objectAssign from 'object-assign';
|
||||
|
||||
const debug = require('debug')('tinylr:client');
|
||||
|
||||
let idCounter = 0;
|
||||
|
||||
export default class Client extends events.EventEmitter {
|
||||
|
||||
constructor (req, socket, head, options = {}) {
|
||||
super();
|
||||
this.options = options;
|
||||
this.ws = new WebSocket(req, socket, head);
|
||||
this.ws.onmessage = this.message.bind(this);
|
||||
this.ws.onclose = this.close.bind(this);
|
||||
this.id = this.uniqueId('ws');
|
||||
}
|
||||
|
||||
message (event) {
|
||||
let data = this.data(event);
|
||||
if (this[data.command]) return this[data.command](data);
|
||||
}
|
||||
|
||||
close (event) {
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
|
||||
this.emit('end', event);
|
||||
}
|
||||
|
||||
// Commands
|
||||
hello () {
|
||||
this.send({
|
||||
command: 'hello',
|
||||
protocols: [
|
||||
'http://livereload.com/protocols/official-7'
|
||||
],
|
||||
serverName: 'tiny-lr'
|
||||
});
|
||||
}
|
||||
|
||||
info (data) {
|
||||
if (data) {
|
||||
debug('Info', data);
|
||||
this.emit('info', objectAssign({}, data, { id: this.id }));
|
||||
this.plugins = data.plugins;
|
||||
this.url = data.url;
|
||||
}
|
||||
|
||||
return objectAssign({}, data || {}, { id: this.id, url: this.url });
|
||||
}
|
||||
|
||||
// Server commands
|
||||
reload (files) {
|
||||
files.forEach(function (file) {
|
||||
this.send({
|
||||
command: 'reload',
|
||||
path: file,
|
||||
liveCSS: this.options.liveCSS !== false,
|
||||
reloadMissingCSS: this.options.reloadMissingCSS !== false,
|
||||
liveImg: this.options.liveImg !== false
|
||||
});
|
||||
}, this);
|
||||
}
|
||||
|
||||
alert (message) {
|
||||
this.send({
|
||||
command: 'alert',
|
||||
message: message
|
||||
});
|
||||
}
|
||||
|
||||
// Utilities
|
||||
data (event) {
|
||||
let data = {};
|
||||
try {
|
||||
data = JSON.parse(event.data);
|
||||
} catch (e) {}
|
||||
return data;
|
||||
}
|
||||
|
||||
send (data) {
|
||||
if (!this.ws) return;
|
||||
this.ws.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
uniqueId (prefix) {
|
||||
let id = idCounter++;
|
||||
return prefix ? prefix + id : id;
|
||||
}
|
||||
}
|
||||
46
book/node_modules/tiny-lr/lib/index.js
generated
vendored
Normal file
46
book/node_modules/tiny-lr/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
import Server from './server';
|
||||
import Client from './client';
|
||||
|
||||
const debug = require('debug')('tinylr');
|
||||
|
||||
// Need to keep track of LR servers when notifying
|
||||
const servers = [];
|
||||
|
||||
export default tinylr;
|
||||
|
||||
// Expose Server / Client objects
|
||||
tinylr.Server = Server;
|
||||
tinylr.Client = Client;
|
||||
|
||||
// and the middleware helpers
|
||||
tinylr.middleware = middleware;
|
||||
tinylr.changed = changed;
|
||||
|
||||
// Main entry point
|
||||
function tinylr (opts) {
|
||||
const srv = new Server(opts);
|
||||
servers.push(srv);
|
||||
return srv;
|
||||
}
|
||||
|
||||
// A facade to Server#handle
|
||||
function middleware (opts) {
|
||||
const srv = new Server(opts);
|
||||
servers.push(srv);
|
||||
return function tinylr (req, res, next) {
|
||||
srv.handler(req, res, next);
|
||||
};
|
||||
}
|
||||
|
||||
// Changed helper, helps with notifying the server of a file change
|
||||
function changed (done) {
|
||||
const files = [].slice.call(arguments);
|
||||
if (typeof files[files.length - 1] === 'function') done = files.pop();
|
||||
done = typeof done === 'function' ? done : () => {};
|
||||
debug('Notifying %d servers - Files: ', servers.length, files);
|
||||
servers.forEach(srv => {
|
||||
const params = { params: { files: files } };
|
||||
srv && srv.changed(params);
|
||||
});
|
||||
done();
|
||||
}
|
||||
322
book/node_modules/tiny-lr/lib/server.js
generated
vendored
Normal file
322
book/node_modules/tiny-lr/lib/server.js
generated
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
import fs from 'fs';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import events from 'events';
|
||||
import {parse} from 'url';
|
||||
import Client from './client';
|
||||
import config from '../package.json';
|
||||
import anybody from 'body/any';
|
||||
import qs from 'qs';
|
||||
|
||||
const debug = require('debug')('tinylr:server');
|
||||
|
||||
const CONTENT_TYPE = 'content-type';
|
||||
const FORM_TYPE = 'application/x-www-form-urlencoded';
|
||||
|
||||
function buildRootPath (prefix = '/') {
|
||||
let rootUrl = prefix;
|
||||
|
||||
// Add trailing slash
|
||||
if (prefix[prefix.length - 1] !== '/') {
|
||||
rootUrl = `${rootUrl}/`;
|
||||
}
|
||||
|
||||
// Add leading slash
|
||||
if (prefix[0] !== '/') {
|
||||
rootUrl = `/${rootUrl}`;
|
||||
}
|
||||
|
||||
return rootUrl;
|
||||
}
|
||||
|
||||
class Server extends events.EventEmitter {
|
||||
constructor (options = {}) {
|
||||
super();
|
||||
|
||||
this.options = options;
|
||||
|
||||
options.livereload = options.livereload || require.resolve('livereload-js/dist/livereload.js');
|
||||
|
||||
// todo: change falsy check to allow 0 for random port
|
||||
options.port = parseInt(options.port || 35729, 10);
|
||||
|
||||
if (options.errorListener) {
|
||||
this.errorListener = options.errorListener;
|
||||
}
|
||||
|
||||
this.rootPath = buildRootPath(options.prefix);
|
||||
|
||||
this.clients = {};
|
||||
this.configure(options.app);
|
||||
this.routes(options.app);
|
||||
}
|
||||
|
||||
routes () {
|
||||
if (!this.options.dashboard) {
|
||||
this.on(`GET ${this.rootPath}`, this.index.bind(this));
|
||||
}
|
||||
|
||||
this.on(`GET ${this.rootPath}changed`, this.changed.bind(this));
|
||||
this.on(`POST ${this.rootPath}changed`, this.changed.bind(this));
|
||||
this.on(`POST ${this.rootPath}alert`, this.alert.bind(this));
|
||||
this.on(`GET ${this.rootPath}livereload.js`, this.livereload.bind(this));
|
||||
this.on(`GET ${this.rootPath}kill`, this.close.bind(this));
|
||||
}
|
||||
|
||||
configure (app) {
|
||||
debug('Configuring %s', app ? 'connect / express application' : 'HTTP server');
|
||||
|
||||
let handler = this.options.handler || this.handler;
|
||||
|
||||
if (!app) {
|
||||
if ((this.options.key && this.options.cert) || this.options.pfx) {
|
||||
this.server = https.createServer(this.options, handler.bind(this));
|
||||
} else {
|
||||
this.server = http.createServer(handler.bind(this));
|
||||
}
|
||||
|
||||
this.server.on('upgrade', this.websocketify.bind(this));
|
||||
this.server.on('error', this.error.bind(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
this.app = app;
|
||||
this.app.listen = (port, done) => {
|
||||
done = done || function () {};
|
||||
if (port !== this.options.port) {
|
||||
debug('Warn: LiveReload port is not standard (%d). You are listening on %d', this.options.port, port);
|
||||
debug('You\'ll need to rely on the LiveReload snippet');
|
||||
debug('> http://feedback.livereload.com/knowledgebase/articles/86180-how-do-i-add-the-script-tag-manually-');
|
||||
}
|
||||
|
||||
let srv = this.server = http.createServer(app);
|
||||
srv.on('upgrade', this.websocketify.bind(this));
|
||||
srv.on('error', this.error.bind(this));
|
||||
srv.on('close', this.close.bind(this));
|
||||
return srv.listen(port, done);
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
handler (req, res, next) {
|
||||
let middleware = typeof next === 'function';
|
||||
debug('LiveReload handler %s (middleware: %s)', req.url, middleware ? 'on' : 'off');
|
||||
|
||||
next = next || this.defaultHandler.bind(this, res);
|
||||
req.headers[CONTENT_TYPE] = req.headers[CONTENT_TYPE] || FORM_TYPE;
|
||||
return anybody(req, res, (err, body) => {
|
||||
if (err) return next(err);
|
||||
req.body = body;
|
||||
|
||||
if (!req.query) {
|
||||
req.query = req.url.indexOf('?') !== -1
|
||||
? qs.parse(parse(req.url).query)
|
||||
: {};
|
||||
}
|
||||
|
||||
return this.handle(req, res, next);
|
||||
});
|
||||
}
|
||||
|
||||
index (req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.write(JSON.stringify({
|
||||
tinylr: 'Welcome',
|
||||
version: config.version
|
||||
}));
|
||||
|
||||
res.end();
|
||||
}
|
||||
|
||||
handle (req, res, next) {
|
||||
let url = parse(req.url);
|
||||
debug('Request:', req.method, url.href);
|
||||
let middleware = typeof next === 'function';
|
||||
|
||||
// do the routing
|
||||
let route = req.method + ' ' + url.pathname;
|
||||
let respond = this.emit(route, req, res);
|
||||
if (respond) return;
|
||||
|
||||
if (middleware) return next();
|
||||
|
||||
// Only apply content-type on non middleware setup #70
|
||||
return this.notFound(res);
|
||||
}
|
||||
|
||||
defaultHandler (res, err) {
|
||||
if (!err) return this.notFound(res);
|
||||
|
||||
this.error(err);
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.statusCode = 500;
|
||||
res.end('Error: ' + err.stack);
|
||||
}
|
||||
|
||||
notFound (res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.writeHead(404);
|
||||
res.write(JSON.stringify({
|
||||
error: 'not_found',
|
||||
reason: 'no such route'
|
||||
}));
|
||||
res.end();
|
||||
}
|
||||
|
||||
websocketify (req, socket, head) {
|
||||
let client = new Client(req, socket, head, this.options);
|
||||
this.clients[client.id] = client;
|
||||
|
||||
// handle socket error to prevent possible app crash, such as ECONNRESET
|
||||
socket.on('error', (e) => {
|
||||
// ignore frequent ECONNRESET error (seems inevitable when refresh)
|
||||
if (e.code === 'ECONNRESET') return;
|
||||
this.error(e);
|
||||
});
|
||||
|
||||
client.once('info', (data) => {
|
||||
debug('Create client %s (url: %s)', data.id, data.url);
|
||||
this.emit('MSG /create', data.id, data.url);
|
||||
});
|
||||
|
||||
client.once('end', () => {
|
||||
debug('Destroy client %s (url: %s)', client.id, client.url);
|
||||
this.emit('MSG /destroy', client.id, client.url);
|
||||
delete this.clients[client.id];
|
||||
});
|
||||
}
|
||||
|
||||
listen (port, host, fn) {
|
||||
port = port || this.options.port;
|
||||
|
||||
// Last used port for error display
|
||||
this.port = port;
|
||||
|
||||
if (typeof host === 'function') {
|
||||
fn = host;
|
||||
host = undefined;
|
||||
}
|
||||
|
||||
this.server.listen(port, host, fn);
|
||||
}
|
||||
|
||||
close (req, res) {
|
||||
Object.keys(this.clients).forEach(function (id) {
|
||||
this.clients[id].close();
|
||||
}, this);
|
||||
|
||||
if (this.server._handle) this.server.close(this.emit.bind(this, 'close'));
|
||||
|
||||
if (res) res.end();
|
||||
}
|
||||
|
||||
error (e) {
|
||||
if (this.errorListener) {
|
||||
this.errorListener(e);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error();
|
||||
if (typeof e === 'undefined') {
|
||||
console.error('... Uhoh. Got error %s ...', e);
|
||||
} else {
|
||||
console.error('... Uhoh. Got error %s ...', e.message);
|
||||
console.error(e.stack);
|
||||
|
||||
if (e.code !== 'EADDRINUSE') return;
|
||||
console.error();
|
||||
console.error('You already have a server listening on %s', this.port);
|
||||
console.error('You should stop it and try again.');
|
||||
console.error();
|
||||
}
|
||||
}
|
||||
|
||||
// Routes
|
||||
|
||||
livereload (req, res) {
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
fs.createReadStream(this.options.livereload).pipe(res);
|
||||
}
|
||||
|
||||
changed (req, res) {
|
||||
let files = this.param('files', req);
|
||||
|
||||
debug('Changed event (Files: %s)', files.join(' '));
|
||||
let clients = this.notifyClients(files);
|
||||
|
||||
if (!res) return;
|
||||
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.write(JSON.stringify({
|
||||
clients: clients,
|
||||
files: files
|
||||
}));
|
||||
|
||||
res.end();
|
||||
}
|
||||
|
||||
alert (req, res) {
|
||||
let message = this.param('message', req);
|
||||
|
||||
debug('Alert event (Message: %s)', message);
|
||||
let clients = this.alertClients(message);
|
||||
|
||||
if (!res) return;
|
||||
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.write(JSON.stringify({
|
||||
clients: clients,
|
||||
message: message
|
||||
}));
|
||||
|
||||
res.end();
|
||||
}
|
||||
|
||||
notifyClients (files) {
|
||||
let clients = Object.keys(this.clients).map(function (id) {
|
||||
let client = this.clients[id];
|
||||
debug('Reloading client %s (url: %s)', client.id, client.url);
|
||||
client.reload(files);
|
||||
return {
|
||||
id: client.id,
|
||||
url: client.url
|
||||
};
|
||||
}, this);
|
||||
|
||||
return clients;
|
||||
};
|
||||
|
||||
alertClients (message) {
|
||||
let clients = Object.keys(this.clients).map(function (id) {
|
||||
let client = this.clients[id];
|
||||
debug('Alert client %s (url: %s)', client.id, client.url);
|
||||
client.alert(message);
|
||||
return {
|
||||
id: client.id,
|
||||
url: client.url
|
||||
};
|
||||
}, this);
|
||||
|
||||
return clients;
|
||||
}
|
||||
|
||||
// Lookup param from body / params / query.
|
||||
param (name, req) {
|
||||
let param;
|
||||
if (req.body && req.body[name]) param = req.body[name];
|
||||
else if (req.params && req.params[name]) param = req.params[name];
|
||||
else if (req.query && req.query[name]) param = req.query[name];
|
||||
|
||||
// normalize files array
|
||||
if (name === 'files') {
|
||||
param = Array.isArray(param) ? param
|
||||
: typeof param === 'string' ? param.split(/[\s,]/)
|
||||
: [];
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
export default Server;
|
||||
Reference in New Issue
Block a user