commit 2f7d127894fd547700a784c57f83af276b3e24bd
parent 82a54939ee3407ba8cb90cc8111366be01813081
Author: Vladimir Kutepov <frenzzy.man@gmail.com>
Date: Fri, 21 Oct 2016 23:03:55 +0400
Improve build scripts; integrate react-hot-loader v3; remove react-transform-hmr (#918)
First part of #697
Changes:
- Replace [React Transform](https://github.com/gaearon/babel-plugin-react-transform) with [React Hot Loader](https://github.com/gaearon/react-hot-loader) v3, closes #588
- New `deployToGitHubPages` task which deploys static version to [GitHub Pages](https://pages.github.com/)
- Update `clean` task to ignore `build/.git` and `build/public/.git` folders during cleaning up
- Update `copy` task to process files in parallel, and watching for `added`, `renamed` and `removed` files
- Update `render` task to render pages in parallel, add info about render time in milliseconds
- Update `runServer` task to return promise when server is ready
- Update `start` task to prevent restarting the server in case of hot compilation errors which helps to avoid restarting the task manually
- Remove `react-transform-hmr`, `react-transform-catch-errors`, and `babel-plugin-react-transform` dependencies in favor of `react-hot-loader`
- Remove `tools/lib/fetch.js` with `http` dependency in favor of `node-fetch`
- Remove `del` dependency (wrapper for `rimraf`) in favor of `rimraf`
- Remove `ncp` dependency in favor of simple `copyFile` function, see `tools/lib/fs.js`
Diffstat:
13 files changed, 309 insertions(+), 220 deletions(-)
diff --git a/package.json b/package.json
@@ -45,7 +45,6 @@
"babel-core": "^6.14.0",
"babel-eslint": "^6.1.2",
"babel-loader": "^6.2.5",
- "babel-plugin-react-transform": "^2.0.2",
"babel-plugin-rewire": "^1.0.0",
"babel-plugin-transform-react-constant-elements": "^6.9.1",
"babel-plugin-transform-react-inline-elements": "^6.8.0",
@@ -61,7 +60,6 @@
"browser-sync": "^2.16.0",
"chai": "^3.5.0",
"css-loader": "^0.25.0",
- "del": "^2.2.2",
"enzyme": "^2.4.1",
"eslint": "^3.6.0",
"eslint-config-airbnb": "^12.0.0",
@@ -77,7 +75,6 @@
"json-loader": "^0.5.4",
"mkdirp": "^0.5.1",
"mocha": "^3.0.2",
- "ncp": "^2.0.0",
"pixrem": "^3.0.2",
"pleeease-filters": "^3.0.0",
"postcss": "^5.2.2",
@@ -96,9 +93,9 @@
"postcss-selector-not": "^2.0.0",
"raw-loader": "^0.5.1",
"react-addons-test-utils": "15.3.2",
- "react-transform-catch-errors": "^1.0.2",
- "react-transform-hmr": "^1.0.4",
+ "react-hot-loader": "^3.0.0-beta.6",
"redbox-react": "^1.3.1",
+ "rimraf": "^2.5.4",
"sinon": "^2.0.0-pre.2",
"stylelint": "^7.3.1",
"stylelint-config-standard": "^13.0.2",
@@ -173,8 +170,9 @@
"copy": "babel-node tools/run copy",
"bundle": "babel-node tools/run bundle",
"build": "babel-node tools/run build",
- "deploy": "babel-node tools/run deploy",
+ "deploy": "babel-node tools/run deployToAzureWebApps",
"render": "babel-node tools/run render",
+ "serve": "babel-node tools/run runServer",
"start": "babel-node tools/run start"
}
}
diff --git a/tools/README.md b/tools/README.md
@@ -7,8 +7,8 @@
* Launches [Webpack](https://webpack.github.io/) compiler in a watch mode (via [webpack-middleware](https://github.com/kriasoft/webpack-middleware))
* Launches Node.js server from the compiled output folder (`runServer.js`)
* Launches [Browsersync](https://browsersync.io/),
- [HMR](https://webpack.github.io/docs/hot-module-replacement), and
- [React Transform](https://github.com/gaearon/babel-plugin-react-transform)
+ [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement), and
+ [React Hot Loader](https://github.com/gaearon/react-hot-loader)
##### `npm run build` (`build.js`)
diff --git a/tools/clean.js b/tools/clean.js
@@ -7,15 +7,25 @@
* LICENSE.txt file in the root directory of this source tree.
*/
-import del from 'del';
-import fs from './lib/fs';
+import { cleanDir } from './lib/fs';
/**
* Cleans up the output (build) directory.
*/
-async function clean() {
- await del(['.tmp', 'build/*', '!build/.git'], { dot: true });
- await fs.makeDir('build/public');
+function clean() {
+ return Promise.all([
+ cleanDir('build/*', {
+ nosort: true,
+ dot: true,
+ ignore: ['build/.git', 'build/public'],
+ }),
+
+ cleanDir('build/public/*', {
+ nosort: true,
+ dot: true,
+ ignore: ['build/public/.git'],
+ }),
+ ]);
}
export default clean;
diff --git a/tools/copy.js b/tools/copy.js
@@ -9,42 +9,55 @@
import path from 'path';
import gaze from 'gaze';
-import Promise from 'bluebird';
-import fs from './lib/fs';
+import { writeFile, copyFile, makeDir, copyDir, cleanDir } from './lib/fs';
import pkg from '../package.json';
+
/**
* Copies static files such as robots.txt, favicon.ico to the
* output (build) folder.
*/
-async function copy({ watch } = {}) {
- const ncp = Promise.promisify(require('ncp'));
-
+async function copy() {
+ await makeDir('build');
await Promise.all([
- ncp('src/public', 'build/public'),
- ncp('src/content', 'build/content'),
+ writeFile('build/package.json', JSON.stringify({
+ private: true,
+ engines: pkg.engines,
+ dependencies: pkg.dependencies,
+ scripts: {
+ start: 'node server.js',
+ },
+ }, null, 2)),
+ copyFile('LICENSE.txt', 'build/LICENSE.txt'),
+ copyDir('src/content', 'build/content'),
+ copyDir('src/public', 'build/public'),
]);
- await fs.writeFile('./build/package.json', JSON.stringify({
- private: true,
- engines: pkg.engines,
- dependencies: pkg.dependencies,
- scripts: {
- start: 'node server.js',
- },
- }, null, 2));
-
- if (watch) {
+ if (process.argv.includes('--watch')) {
const watcher = await new Promise((resolve, reject) => {
- gaze('src/content/**/*.*', (err, val) => (err ? reject(err) : resolve(val)));
+ gaze([
+ 'src/content/**/*',
+ 'src/public/**/*',
+ ], (err, val) => (err ? reject(err) : resolve(val)));
});
- const cp = async (file) => {
- const relPath = file.substr(path.join(__dirname, '../src/content/').length);
- await ncp(`src/content/${relPath}`, `build/content/${relPath}`);
- };
-
- watcher.on('changed', cp);
- watcher.on('added', cp);
+ watcher.on('all', async (event, filePath) => {
+ const dist = path.join('build/', path.relative('src', filePath));
+ switch (event) {
+ case 'added':
+ case 'renamed':
+ case 'changed':
+ if (filePath.endsWith('/')) return;
+ await makeDir(path.dirname(dist));
+ await copyFile(filePath, dist);
+ break;
+ case 'deleted':
+ cleanDir(dist, { nosort: true, dot: true });
+ break;
+ default:
+ return;
+ }
+ console.log(`[file ${event}] ${dist}`);
+ });
}
}
diff --git a/tools/deploy.js b/tools/deploy.js
@@ -1,57 +0,0 @@
-/**
- * React Starter Kit (https://www.reactstarterkit.com/)
- *
- * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import GitRepo from 'git-repository';
-import run from './run';
-import fetch from './lib/fetch';
-
-// TODO: Update deployment URL
-// For more information visit http://gitolite.com/deploy.html
-const getRemote = (slot) => ({
- name: slot || 'production',
- url: `https://example${slot ? `-${slot}` : ''}.scm.azurewebsites.net:443/example.git`,
- website: `http://example${slot ? `-${slot}` : ''}.azurewebsites.net`,
-});
-
-/**
- * Deploy the contents of the `/build` folder to a remote
- * server via Git. Example: `npm run deploy -- production`
- */
-async function deploy() {
- // By default deploy to the staging deployment slot
- const remote = getRemote(process.argv.includes('--production') ? null : 'staging');
-
- // Initialize a new Git repository inside the `/build` folder
- // if it doesn't exist yet
- const repo = await GitRepo.open('build', { init: true });
- await repo.setRemote(remote.name, remote.url);
-
- // Fetch the remote repository if it exists
- if ((await repo.hasRef(remote.url, 'master'))) {
- await repo.fetch(remote.name);
- await repo.reset(`${remote.name}/master`, { hard: true });
- await repo.clean({ force: true });
- }
-
- // Build the project in RELEASE mode which
- // generates optimized and minimized bundles
- process.argv.push('--release');
- await run(require('./build'));
-
- // Push the contents of the build folder to the remote server via Git
- await repo.add('--all .');
- await repo.commit('Update');
- await repo.push(remote.name, 'master');
-
- // Check if the site was successfully deployed
- const response = await fetch(remote.website);
- console.log(`${remote.website} -> ${response.statusCode}`);
-}
-
-export default deploy;
diff --git a/tools/deployToAzureWebApps.js b/tools/deployToAzureWebApps.js
@@ -0,0 +1,60 @@
+/**
+ * React Starter Kit (https://www.reactstarterkit.com/)
+ *
+ * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+import GitRepo from 'git-repository';
+import fetch from 'node-fetch';
+import run from './run';
+
+// For more information visit http://gitolite.com/deploy.html
+function getRemote(slot) {
+ return {
+ name: slot || 'production',
+ url: `https://example${slot ? `-${slot}` : ''}.scm.azurewebsites.net:443/example.git`,
+ branch: 'master',
+ website: `http://example${slot ? `-${slot}` : ''}.azurewebsites.net`,
+ };
+}
+
+/**
+ * Deploy the contents of the `/build` folder to a remote
+ * server via Git. Example: `npm run deploy -- production`
+ */
+async function deployToAzureWebApps() {
+ // By default deploy to the staging deployment slot
+ const remote = getRemote(process.argv.includes('--production') ? null : 'staging');
+
+ // Initialize a new Git repository inside the `/build` folder
+ // if it doesn't exist yet
+ const repo = await GitRepo.open('build', { init: true });
+ await repo.setRemote(remote.name, remote.url);
+
+ // Fetch the remote repository if it exists
+ const isRefExists = await repo.hasRef(remote.url, remote.branch);
+ if (isRefExists) {
+ await repo.fetch(remote.name);
+ await repo.reset(`${remote.name}/${remote.branch}`, { hard: true });
+ await repo.clean({ force: true });
+ }
+
+ // Build the project in RELEASE mode which
+ // generates optimized and minimized bundles
+ process.argv.push('--release');
+ await run(require('./build'));
+
+ // Push the contents of the build folder to the remote server via Git
+ await repo.add('--all .');
+ await repo.commit(`Update ${new Date().toISOString()}`);
+ await repo.push(remote.name, `master:${remote.branch}`);
+
+ // Check if the site was successfully deployed
+ const response = await fetch(remote.website);
+ console.log(`${remote.website} -> ${response.status}`);
+}
+
+export default deployToAzureWebApps;
diff --git a/tools/deployToGitHubPages.js b/tools/deployToGitHubPages.js
@@ -0,0 +1,46 @@
+/**
+ * React Starter Kit (https://www.reactstarterkit.com/)
+ *
+ * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE.txt file in the root directory of this source tree.
+ */
+
+import GitRepo from 'git-repository';
+import run from './run';
+import build from './build';
+
+const remote = {
+ name: 'github',
+ url: 'https://github.com/{user}/{repo}.git',
+ branch: 'gh-pages',
+};
+
+/**
+ * Deploy the contents of the `/build/public` folder to GitHub Pages.
+ */
+async function deployToGitHubPages() {
+ // Initialize a new Git repository inside the `/build` folder
+ // if it doesn't exist yet
+ const repo = await GitRepo.open('build/public', { init: true });
+ await repo.setRemote(remote.name, remote.url);
+ const isRefExists = await repo.hasRef(remote.url, remote.branch);
+ if (isRefExists) {
+ await repo.fetch(remote.name);
+ await repo.reset(`${remote.name}/${remote.branch}`, { hard: true });
+ await repo.clean({ force: true });
+ }
+
+ // Build the project in RELEASE mode which
+ // generates optimized and minimized bundles
+ process.argv.push('--static', '--release');
+ await run(build);
+
+ // Push the contents of the build folder to the remote server via Git
+ await repo.add('--all .');
+ await repo.commit(`Update ${new Date().toISOString()}`);
+ await repo.push(remote.name, `master:${remote.branch}`);
+}
+
+export default deployToGitHubPages;
diff --git a/tools/lib/fetch.js b/tools/lib/fetch.js
@@ -1,14 +0,0 @@
-/**
- * React Starter Kit (https://www.reactstarterkit.com/)
- *
- * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.txt file in the root directory of this source tree.
- */
-
-import http from 'http';
-
-export default async (url) => new Promise((resolve, reject) =>
- http.get(url, res => resolve(res)).on('error', err => reject(err))
-);
diff --git a/tools/lib/fs.js b/tools/lib/fs.js
@@ -8,14 +8,62 @@
*/
import fs from 'fs';
+import path from 'path';
+import glob from 'glob';
import mkdirp from 'mkdirp';
+import rimraf from 'rimraf';
-const writeFile = (file, contents) => new Promise((resolve, reject) => {
+export const readFile = (file) => new Promise((resolve, reject) => {
+ fs.readFile(file, 'utf8', (err, data) => (err ? reject(err) : resolve(data)));
+});
+
+export const writeFile = (file, contents) => new Promise((resolve, reject) => {
fs.writeFile(file, contents, 'utf8', err => (err ? reject(err) : resolve()));
});
-const makeDir = (name) => new Promise((resolve, reject) => {
+export const copyFile = (source, target) => new Promise((resolve, reject) => {
+ let cbCalled = false;
+ function done(err) {
+ if (!cbCalled) {
+ cbCalled = true;
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ }
+ }
+
+ const rd = fs.createReadStream(source);
+ rd.on('error', err => done(err));
+ const wr = fs.createWriteStream(target);
+ wr.on('error', err => done(err));
+ wr.on('close', err => done(err));
+ rd.pipe(wr);
+});
+
+export const readDir = (pattern, options) => new Promise((resolve, reject) =>
+ glob(pattern, options, (err, result) => (err ? reject(err) : resolve(result)))
+);
+
+export const makeDir = (name) => new Promise((resolve, reject) => {
mkdirp(name, err => (err ? reject(err) : resolve()));
});
-export default { writeFile, makeDir };
+export const copyDir = async (source, target) => {
+ const dirs = await readDir('**/*.*', {
+ cwd: source,
+ nosort: true,
+ dot: true,
+ });
+ await Promise.all(dirs.map(async dir => {
+ const from = path.resolve(source, dir);
+ const to = path.resolve(target, dir);
+ await makeDir(path.dirname(to));
+ await copyFile(from, to);
+ }));
+};
+
+export const cleanDir = (pattern, options) => new Promise((resolve, reject) =>
+ rimraf(pattern, { glob: options }, (err, result) => (err ? reject(err) : resolve(result)))
+);
diff --git a/tools/render.js b/tools/render.js
@@ -7,12 +7,20 @@
* LICENSE.txt file in the root directory of this source tree.
*/
+import path from 'path';
import fetch from 'node-fetch';
+import { writeFile, makeDir } from './lib/fs';
import runServer from './runServer';
-import fs from './lib/fs';
-import { host } from '../src/config';
// Enter your paths here which you want to render as static
+// Example:
+// const routes = [
+// '/', // => build/public/index.html
+// '/page', // => build/public/page.html
+// '/page/', // => build/public/page/index.html
+// '/page/name', // => build/public/page/name.html
+// '/page/name/', // => build/public/page/name/index.html
+// ];
const routes = [
'/',
'/contact',
@@ -24,20 +32,29 @@ const routes = [
];
async function render() {
- let server;
- await new Promise(resolve => (server = runServer(resolve)));
+ const server = await runServer();
- await routes.reduce((promise, route) => promise.then(async () => {
- const url = `http://${host}${route}`;
- const dir = `build/public${route.replace(/[^\/]*$/, '')}`;
- const name = route.endsWith('/') ? 'index.html' : `${route.match(/[^/]+$/)[0]}.html`;
- const dist = `${dir}${name}`;
- const res = await fetch(url);
- const text = await res.text();
- await fs.makeDir(dir);
- await fs.writeFile(dist, text);
- console.log(`${dist} => ${res.status} ${res.statusText}`);
- }), Promise.resolve());
+ // add dynamic routes
+ // const products = await fetch(`http://${server.host}/api/products`).then(res => res.json());
+ // products.forEach(product => routes.push(
+ // `/product/${product.uri}`,
+ // `/product/${product.uri}/specs`
+ // ));
+
+ await Promise.all(routes.map(async (route, index) => {
+ const url = `http://${server.host}${route}`;
+ const fileName = route.endsWith('/') ? 'index.html' : `${path.basename(route, '.html')}.html`;
+ const dirName = path.join('build/public', route.endsWith('/') ? route : path.dirname(route));
+ const dist = `${dirName}${fileName}`;
+ const timeStart = new Date();
+ const response = await fetch(url);
+ const timeEnd = new Date();
+ const text = await response.text();
+ await makeDir(dirName);
+ await writeFile(dist, text);
+ const time = timeEnd.getTime() - timeStart.getTime();
+ console.log(`#${index + 1} ${dist} => ${response.status} ${response.statusText} (${time} ms)`);
+ }));
server.kill('SIGTERM');
}
diff --git a/tools/run.js b/tools/run.js
@@ -15,13 +15,13 @@ function run(fn, options) {
const task = typeof fn.default === 'undefined' ? fn : fn.default;
const start = new Date();
console.log(
- `[${format(start)}] Starting '${task.name}${options ? `(${options})` : ''}'...`
+ `[${format(start)}] Starting '${task.name}${options ? ` (${options})` : ''}'...`
);
return task(options).then(resolution => {
const end = new Date();
const time = end.getTime() - start.getTime();
console.log(
- `[${format(end)}] Finished '${task.name}${options ? `(${options})` : ''}' after ${time} ms`
+ `[${format(end)}] Finished '${task.name}${options ? ` (${options})` : ''}' after ${time} ms`
);
return resolution;
});
diff --git a/tools/runServer.js b/tools/runServer.js
@@ -19,45 +19,48 @@ const { output } = webpackConfig.find(x => x.target === 'node');
const serverPath = path.join(output.path, output.filename);
// Launch or restart the Node.js server
-function runServer(cb) {
- let cbIsPending = !!cb;
+function runServer() {
+ return new Promise(resolve => {
+ let pending = true;
- function onStdOut(data) {
- const time = new Date().toTimeString();
- const match = data.toString('utf8').match(RUNNING_REGEXP);
+ function onStdOut(data) {
+ const time = new Date().toTimeString();
+ const match = data.toString('utf8').match(RUNNING_REGEXP);
- process.stdout.write(time.replace(/.*(\d{2}:\d{2}:\d{2}).*/, '[$1] '));
- process.stdout.write(data);
+ process.stdout.write(time.replace(/.*(\d{2}:\d{2}:\d{2}).*/, '[$1] '));
+ process.stdout.write(data);
- if (match) {
- server.stdout.removeListener('data', onStdOut);
- server.stdout.on('data', x => process.stdout.write(x));
- if (cb) {
- cbIsPending = false;
- cb(null, match[1]);
+ if (match) {
+ server.host = match[1];
+ server.stdout.removeListener('data', onStdOut);
+ server.stdout.on('data', x => process.stdout.write(x));
+ pending = false;
+ resolve(server);
}
}
- }
- if (server) {
- server.kill('SIGTERM');
- }
+ if (server) {
+ server.kill('SIGTERM');
+ }
- server = cp.spawn('node', [serverPath], {
- env: Object.assign({ NODE_ENV: 'development' }, process.env),
- silent: false,
- });
- if (cbIsPending) {
- server.once('exit', (code, signal) => {
- if (cbIsPending) {
- throw new Error(`Server terminated unexpectedly with code: ${code} signal: ${signal}`);
- }
+ server = cp.spawn('node', [serverPath], {
+ env: Object.assign({ NODE_ENV: 'development' }, process.env),
+ silent: false,
});
- }
- server.stdout.on('data', onStdOut);
- server.stderr.on('data', x => process.stderr.write(x));
- return server;
+ if (pending) {
+ server.once('exit', (code, signal) => {
+ if (pending) {
+ throw new Error(`Server terminated unexpectedly with code: ${code} signal: ${signal}`);
+ }
+ });
+ }
+
+ server.stdout.on('data', onStdOut);
+ server.stderr.on('data', x => process.stderr.write(x));
+
+ return server;
+ });
}
process.on('exit', () => {
diff --git a/tools/start.js b/tools/start.js
@@ -7,7 +7,7 @@
* LICENSE.txt file in the root directory of this source tree.
*/
-import Browsersync from 'browser-sync';
+import browserSync from 'browser-sync';
import webpack from 'webpack';
import webpackMiddleware from 'webpack-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
@@ -17,7 +17,8 @@ import webpackConfig from './webpack.config';
import clean from './clean';
import copy from './copy';
-const DEBUG = !process.argv.includes('--release');
+process.argv.push('--watch');
+const [config] = webpackConfig;
/**
* Launches a development web server with "live reload" functionality -
@@ -25,86 +26,50 @@ const DEBUG = !process.argv.includes('--release');
*/
async function start() {
await run(clean);
- await run(copy.bind(undefined, { watch: true }));
+ await run(copy);
await new Promise(resolve => {
- // Patch the client-side bundle configurations
- // to enable Hot Module Replacement (HMR) and React Transform
- webpackConfig.filter(x => x.target !== 'node').forEach(config => {
- /* eslint-disable no-param-reassign */
- config.entry = ['webpack-hot-middleware/client'].concat(config.entry);
+ // Hot Module Replacement (HMR) + React Hot Reload
+ if (config.debug) {
+ config.entry = ['react-hot-loader/patch', 'webpack-hot-middleware/client', config.entry];
config.output.filename = config.output.filename.replace('[chunkhash]', '[hash]');
config.output.chunkFilename = config.output.chunkFilename.replace('[chunkhash]', '[hash]');
+ config.module.loaders.find(x => x.loader === 'babel-loader')
+ .query.plugins.unshift('react-hot-loader/babel');
config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.plugins.push(new webpack.NoErrorsPlugin());
- config
- .module
- .loaders
- .filter(x => x.loader === 'babel-loader')
- .forEach(x => (x.query = {
- ...x.query,
-
- // Wraps all React components into arbitrary transforms
- // https://github.com/gaearon/babel-plugin-react-transform
- plugins: [
- ...(x.query ? x.query.plugins : []),
- ['react-transform', {
- transforms: [
- {
- transform: 'react-transform-hmr',
- imports: ['react'],
- locals: ['module'],
- }, {
- transform: 'react-transform-catch-errors',
- imports: ['react', 'redbox-react'],
- },
- ],
- },
- ],
- ],
- }));
- /* eslint-enable no-param-reassign */
- });
+ }
const bundler = webpack(webpackConfig);
const wpMiddleware = webpackMiddleware(bundler, {
-
// IMPORTANT: webpack middleware can't access config,
// so we should provide publicPath by ourselves
- publicPath: webpackConfig[0].output.publicPath,
+ publicPath: config.output.publicPath,
// Pretty colored output
- stats: webpackConfig[0].stats,
+ stats: config.stats,
// For other settings see
// https://webpack.github.io/docs/webpack-dev-middleware
});
- const hotMiddlewares = bundler
- .compilers
- .filter(compiler => compiler.options.target !== 'node')
- .map(compiler => webpackHotMiddleware(compiler));
+ const hotMiddleware = webpackHotMiddleware(bundler.compilers[0]);
+
+ let handleBundleComplete = async () => {
+ handleBundleComplete = stats => !stats.stats[1].compilation.errors.length && runServer();
- let handleServerBundleComplete = () => {
- runServer((err, host) => {
- if (!err) {
- const bs = Browsersync.create();
- bs.init({
- ...(DEBUG ? {} : { notify: false, ui: false }),
+ const server = await runServer();
+ const bs = browserSync.create();
- proxy: {
- target: host,
- middleware: [wpMiddleware, ...hotMiddlewares],
- },
+ bs.init({
+ ...(config.debug ? {} : { notify: false, ui: false }),
- // no need to watch '*.js' here, webpack will take care of it for us,
- // including full page reloads if HMR won't work
- files: ['build/content/**/*.*'],
- }, resolve);
- handleServerBundleComplete = runServer;
- }
- });
+ proxy: {
+ target: server.host,
+ middleware: [wpMiddleware, hotMiddleware],
+ },
+ }, resolve);
};
- bundler.plugin('done', () => handleServerBundleComplete());
+ bundler.plugin('done', stats => handleBundleComplete(stats));
});
}