This repository has been archived by the owner on Nov 15, 2017. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Re-synced hot development code with react-universally
- Loading branch information
Showing
7 changed files
with
364 additions
and
304 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.