Skip to content

Commit

Permalink
refactor: insert client implementation from within Dev Server API (#1933
Browse files Browse the repository at this point in the history
)
  • Loading branch information
knagaitsev authored and evilebottnawi committed Jun 3, 2019
1 parent 6973e4c commit eecc1e4
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 12 deletions.
27 changes: 15 additions & 12 deletions client-src/default/socket.js
@@ -1,24 +1,28 @@
'use strict';

const SockJS = require('sockjs-client/dist/sockjs');
/* global __webpack_dev_server_client__ */
/* eslint-disable
camelcase
*/
const Client = __webpack_dev_server_client__;

let retries = 0;
let sock = null;
let client = null;

const socket = function initSocket(url, handlers) {
sock = new SockJS(url);
client = new Client(url);

sock.onopen = function onopen() {
client.onOpen(() => {
retries = 0;
};
});

sock.onclose = function onclose() {
client.onClose(() => {
if (retries === 0) {
handlers.close();
}

// Try to reconnect.
sock = null;
client = null;

// After 10 retries stop trying, to prevent logspam.
if (retries <= 10) {
Expand All @@ -32,15 +36,14 @@ const socket = function initSocket(url, handlers) {
socket(url, handlers);
}, retryInMs);
}
};
});

sock.onmessage = function onmessage(e) {
// This assumes that all data sent via the websocket is JSON.
const msg = JSON.parse(e.data);
client.onMessage((data) => {
const msg = JSON.parse(data);
if (handlers[msg.type]) {
handlers[msg.type](msg.data);
}
};
});
};

module.exports = socket;
10 changes: 10 additions & 0 deletions lib/clients/BaseClient.js
@@ -0,0 +1,10 @@
'use strict';

/* eslint-disable
no-unused-vars
*/
module.exports = class BaseClient {
static getClientPath(options) {
throw new Error('Client needs implementation');
}
};
33 changes: 33 additions & 0 deletions lib/clients/SockJSClient.js
@@ -0,0 +1,33 @@
'use strict';

/* eslint-disable
no-unused-vars
*/
const SockJS = require('sockjs-client/dist/sockjs');
const BaseClient = require('./BaseClient');

module.exports = class SockJSClient extends BaseClient {
constructor(url) {
super();
this.sock = new SockJS(url);
}

static getClientPath(options) {
return require.resolve('./SockJSClient');
}

onOpen(f) {
this.sock.onopen = f;
}

onClose(f) {
this.sock.onclose = f;
}

// call f with the message string as the first argument
onMessage(f) {
this.sock.onmessage = (e) => {
f(e.data);
};
}
};
5 changes: 5 additions & 0 deletions lib/clients/WebsocketClient.js
@@ -0,0 +1,5 @@
'use strict';

const BaseClient = require('./BaseClient');

module.exports = class WebsocketClient extends BaseClient {};
6 changes: 6 additions & 0 deletions lib/utils/updateCompiler.js
Expand Up @@ -6,6 +6,7 @@
*/
const webpack = require('webpack');
const addEntries = require('./addEntries');
const SockJSClient = require('./../clients/SockJSClient');

function updateCompiler(compiler, options) {
if (options.inline !== false) {
Expand Down Expand Up @@ -48,6 +49,11 @@ function updateCompiler(compiler, options) {
compilers.forEach((compiler) => {
const config = compiler.options;
compiler.hooks.entryOption.call(config.context, config.entry);

const providePlugin = new webpack.ProvidePlugin({
__webpack_dev_server_client__: SockJSClient.getClientPath(options),
});
providePlugin.apply(compiler);
});

// do not apply the plugin unless it didn't exist before.
Expand Down
59 changes: 59 additions & 0 deletions test/SockJSClient.test.js
@@ -0,0 +1,59 @@
'use strict';

const http = require('http');
const express = require('express');
const sockjs = require('sockjs');
const SockJSClient = require('../lib/clients/SockJSClient');

describe('SockJSClient', () => {
let socketServer;
let listeningApp;

beforeAll((done) => {
// eslint-disable-next-line new-cap
const app = new express();
listeningApp = http.createServer(app);
listeningApp.listen(8080, 'localhost', () => {
socketServer = sockjs.createServer();
socketServer.installHandlers(listeningApp, {
prefix: '/sockjs-node',
});
done();
});
});

describe('client', () => {
it('should open, recieve message, and close', (done) => {
socketServer.on('connection', (connection) => {
connection.write('hello world');
setTimeout(() => {
connection.close();
}, 1000);
});
const client = new SockJSClient('http://localhost:8080/sockjs-node');
const data = [];
client.onOpen(() => {
data.push('open');
});
client.onClose(() => {
data.push('close');
});
client.onMessage((msg) => {
data.push(msg);
});
setTimeout(() => {
expect(data.length).toEqual(3);
expect(data[0]).toEqual('open');
expect(data[1]).toEqual('hello world');
expect(data[2]).toEqual('close');
done();
}, 3000);
});
});

afterAll((done) => {
listeningApp.close(() => {
done();
});
});
});
80 changes: 80 additions & 0 deletions test/e2e/ProvidePlugin.test.js
@@ -0,0 +1,80 @@
'use strict';

const testServer = require('../helpers/test-server');
const config = require('../fixtures/provide-plugin-config/webpack.config');
const runBrowser = require('../helpers/run-browser');

describe('ProvidePlugin', () => {
describe('inline', () => {
beforeAll((done) => {
const options = {
port: 9000,
host: '0.0.0.0',
inline: true,
watchOptions: {
poll: true,
},
};
testServer.startAwaitingCompilation(config, options, done);
});

afterAll(testServer.close);

describe('on browser client', () => {
jest.setTimeout(30000);

it('should inject SockJS client implementation', (done) => {
runBrowser().then(({ page, browser }) => {
page.waitForNavigation({ waitUntil: 'load' }).then(() => {
page
.evaluate(() => {
return window.injectedClient === window.expectedClient;
})
.then((isCorrectClient) => {
expect(isCorrectClient).toBeTruthy();
browser.close().then(done);
});
});
page.goto('http://localhost:9000/main');
});
});
});
});

describe('not inline', () => {
beforeAll((done) => {
const options = {
port: 9000,
host: '0.0.0.0',
inline: false,
watchOptions: {
poll: true,
},
};
testServer.startAwaitingCompilation(config, options, done);
});

afterAll(testServer.close);

describe('on browser client', () => {
jest.setTimeout(30000);

it('should not inject client implementation', (done) => {
runBrowser().then(({ page, browser }) => {
page.waitForNavigation({ waitUntil: 'load' }).then(() => {
page
.evaluate(() => {
// eslint-disable-next-line no-undefined
return window.injectedClient === undefined;
})
.then((isCorrectClient) => {
expect(isCorrectClient).toBeTruthy();
browser.close().then(done);
});
});
page.goto('http://localhost:9000/main');
});
});
});
});
});
7 changes: 7 additions & 0 deletions test/fixtures/provide-plugin-config/foo.js
@@ -0,0 +1,7 @@
'use strict';

const SockJSClient = require('../../../lib/clients/SockJSClient');

window.expectedClient = SockJSClient;
// eslint-disable-next-line camelcase, no-undef
window.injectedClient = __webpack_dev_server_client__;
11 changes: 11 additions & 0 deletions test/fixtures/provide-plugin-config/webpack.config.js
@@ -0,0 +1,11 @@
'use strict';

module.exports = {
mode: 'development',
context: __dirname,
entry: './foo.js',
output: {
path: '/',
},
node: false,
};

0 comments on commit eecc1e4

Please sign in to comment.