Skip to content
This repository has been archived by the owner on Nov 15, 2017. It is now read-only.

Commit

Permalink
Re-synced hot development code with react-universally
Browse files Browse the repository at this point in the history
  • Loading branch information
swernerx committed Feb 7, 2017
1 parent ad49ca4 commit a44fe28
Show file tree
Hide file tree
Showing 7 changed files with 364 additions and 304 deletions.
49 changes: 0 additions & 49 deletions src/webpack/HotClient.js

This file was deleted.

78 changes: 78 additions & 0 deletions src/webpack/HotClientServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import express from 'express';
import createWebpackMiddleware from 'webpack-dev-middleware';
import createWebpackHotMiddleware from 'webpack-hot-middleware';
import ListenerManager from './listenerManager';
import { log } from '../utils';

class HotClientServer {
constructor(compiler) {
const app = express();

const httpPathRegex = /^https?:\/\/(.*):([\d]{1,5})/i;
const httpPath = compiler.options.output.publicPath;
if (!httpPath.startsWith('http') && !httpPathRegex.test(httpPath)) {
throw new Error(
'You must supply an absolute public path to a development build of a web target bundle as it will be hosted on a seperate development server to any node target bundles.',
);
}

// eslint-disable-next-line no-unused-vars
const [_, host, port] = httpPathRegex.exec(httpPath);

this.webpackDevMiddleware = createWebpackMiddleware(compiler, {
quiet: true,
noInfo: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
// Ensure that the public path is taken from the compiler webpack config
// as it will have been created as an absolute path to avoid conflicts
// with an node servers.
publicPath: compiler.options.output.publicPath,
});

app.use(this.webpackDevMiddleware);
app.use(createWebpackHotMiddleware(compiler));

const listener = app.listen(port, host);

this.listenerManager = new ListenerManager(listener, 'client');

compiler.plugin('compile', () => {
log({
title: 'client',
level: 'info',
message: 'Building new bundle...',
});
});

compiler.plugin('done', (stats) => {
if (stats.hasErrors()) {
log({
title: 'client',
level: 'error',
message: 'Build failed, please check the console for more information.',
notify: true,
});
console.error(stats.toString());
} else {
log({
title: 'client',
level: 'info',
message: 'Running with latest changes.',
notify: true,
});
}
});
}

dispose() {
this.webpackDevMiddleware.close();

return this.listenerManager
? this.listenerManager.dispose()
: Promise.resolve();
}
}

export default HotClientServer;
113 changes: 113 additions & 0 deletions src/webpack/HotController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { resolve as pathResolve } from 'path';
import webpack from 'webpack';
import appRootDir from 'app-root-dir';
import { log } from '../utils';
import HotNodeServer from './hotNodeServer';
import HotClientServer from './hotClientServer';
import createVendorDLL from './createVendorDLL';
import webpackConfigFactory from '../webpack/configFactory';
import getConfig from '../../config/get';

const usesDevVendorDLL = bundleConfig =>
bundleConfig.devVendorDLL != null && bundleConfig.devVendorDLL.enabled;

const vendorDLLsFailed = (err) => {
log({
title: 'vendorDLL',
level: 'error',
message: 'Unfortunately an error occured whilst trying to build the vendor dll(s) used by the development server. Please check the console for more information.',
notify: true,
});
if (err) {
console.error(err);
}
};

const initializeBundle = (name, bundleConfig) => {
const createCompiler = () => {
try {
const webpackConfig = webpackConfigFactory({
target: name,
mode: 'development',
});

// Install the vendor DLL config for the client bundle if required.
if (usesDevVendorDLL(bundleConfig)) {
// Install the vendor DLL plugin.
webpackConfig.plugins.push(
new webpack.DllReferencePlugin({
// $FlowFixMe
manifest: require(
pathResolve(
appRootDir.get(),
bundleConfig.outputPath,
`${bundleConfig.devVendorDLL.name}.json`,
),
),
}),
);
}
return webpack(webpackConfig);
} catch (err) {
log({
title: 'development',
level: 'error',
message: 'Webpack config is invalid, please check the console for more information.',
notify: true,
});
console.error(err);
throw err;
}
};
return { name, bundleConfig, createCompiler };
};

class HotDevelopment {


constructor() {
this.hotClientServer = null;
this.hotNodeServers = [];

const clientBundle = initializeBundle('client', getConfig('bundles.client'));
const nodeBundles = initializeBundle('server', getConfig('bundles.server'));

Promise
// First ensure the client dev vendor DLLs is created if needed.
.resolve(
usesDevVendorDLL(getConfig('bundles.client'))
? createVendorDLL('client', getConfig('bundles.client'))
: true,
)
// Then start the client development server.
.then(
() => new Promise((resolve) => {
const { createCompiler } = clientBundle;
const compiler = createCompiler();
compiler.plugin('done', (stats) => {
if (!stats.hasErrors()) {
resolve(compiler);
}
});
this.hotClientServer = new HotClientServer(compiler);
}),
vendorDLLsFailed,
)
// Then start the node development server(s).
.then((clientCompiler) => {
this.hotNodeServers = new HotNodeServer(createCompiler(), clientCompiler)
});
}

dispose() {
const safeDisposer = server =>
(server ? server.dispose() : Promise.resolve());

// First the hot client server.
return safeDisposer(this.hotClientServer)
// Then dispose the hot node server(s).
.then(() => Promise.all(this.hotNodeServers.map(safeDisposer)));
}
}

export default HotDevelopment;
128 changes: 128 additions & 0 deletions src/webpack/HotNodeServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import path from 'path';
import appRootDir from 'app-root-dir';
import { spawn } from 'child_process';
import { log } from '../utils';

class HotNodeServer {
constructor(compiler, clientCompiler) {
const compiledEntryFile = path.resolve(
appRootDir.get(),
compiler.options.output.path,
`${Object.keys(compiler.options.entry)[0]}.js`,
);

const name = "sdajjsad"

const startServer = () => {
if (this.server) {
this.server.kill();
this.server = null;
log({
title: name,
level: 'info',
message: 'Restarting server...',
});
}

const newServer = spawn('node', [compiledEntryFile]);

log({
title: name,
level: 'info',
message: 'Server running with latest changes.',
notify: true,
});

newServer.stdout.on('data', data => console.log(data.toString().trim()));
newServer.stderr.on('data', (data) => {
log({
title: name,
level: 'error',
message: 'Error in server execution, check the console for more info.',
});
console.error(data.toString().trim());
});
this.server = newServer;
};

// We want our node server bundles to only start after a successful client
// build. This avoids any issues with node server bundles depending on
// client bundle assets.
const waitForClientThenStartServer = () => {
if (this.serverCompiling) {
// A new server bundle is building, break this loop.
return;
}
if (this.clientCompiling) {
setTimeout(waitForClientThenStartServer, 50);
} else {
startServer();
}
};

clientCompiler.plugin('compile', () => {
this.clientCompiling = true;
});

clientCompiler.plugin('done', (stats) => {
if (!stats.hasErrors()) {
this.clientCompiling = false;
}
});

compiler.plugin('compile', () => {
this.serverCompiling = true;
log({
title: name,
level: 'info',
message: 'Building new bundle...',
});
});

compiler.plugin('done', (stats) => {
this.serverCompiling = false;

if (this.disposing) {
return;
}

try {
if (stats.hasErrors()) {
log({
title: name,
level: 'error',
message: 'Build failed, check the console for more information.',
notify: true,
});
console.log(stats.toString());
return;
}

waitForClientThenStartServer();
} catch (err) {
log({
title: name,
level: 'error',
message: 'Failed to start, please check the console for more information.',
notify: true,
});
console.error(err);
}
});

// Lets start the compiler.
this.watcher = compiler.watch(null, () => undefined);
}

dispose() {
this.disposing = true;

const stopWatcher = new Promise((resolve) => {
this.watcher.close(resolve);
});

return stopWatcher.then(() => { if (this.server) this.server.kill(); });
}
}

export default HotNodeServer;
Loading

0 comments on commit a44fe28

Please sign in to comment.