feat(v2): webpack multicompile client & server (#1363)

* feat(v2): multi-compile server & client

* simplify stuff

* dep

* fix sitemap callback deprecation warning
This commit is contained in:
Endilie Yacop Sucipto 2019-04-13 23:11:35 +07:00 committed by GitHub
parent ecb4f91669
commit f37300a69b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 19 deletions

View file

@ -55,7 +55,11 @@ class DocusaurusPluginSitemap {
// Write sitemap file
const sitemapPath = path.join(outDir, 'sitemap.xml');
return fs.writeFile(sitemapPath, generatedSitemap);
fs.writeFile(sitemapPath, generatedSitemap, err => {
if (err) {
throw new Error(`Sitemap error: ${err}`);
}
});
}
}

View file

@ -13,7 +13,8 @@ import {Helmet} from 'react-helmet';
import {getBundles} from 'react-loadable-ssr-addon';
import Loadable from 'react-loadable';
import manifest from '@generated/assets-manifest.json'; //eslint-disable-line
import path from 'path';
import fs from 'fs';
import routes from '@generated/routes'; // eslint-disable-line
import preload from './preload';
import App from './App';
@ -42,6 +43,10 @@ export default function render(locals) {
];
const metaAttributes = metaStrings.filter(Boolean);
const {outDir} = locals;
const manifestPath = path.join(outDir, 'client-manifest.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
// Get all required assets for this particular page based on client manifest information
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
const bundles = getBundles(manifest, modulesToBeLoaded);

View file

@ -7,7 +7,6 @@
const webpack = require('webpack');
const merge = require('webpack-merge');
const MemoryFS = require('memory-fs');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
const path = require('path');
@ -19,13 +18,9 @@ const createServerConfig = require('../webpack/server');
const createClientConfig = require('../webpack/client');
const {applyConfigureWebpack} = require('../webpack/utils');
function compile(config, isServer) {
function compile(config) {
return new Promise((resolve, reject) => {
const compiler = webpack(config);
if (isServer) {
// Don't output server bundle to disk. Write files to memory instead
compiler.outputFileSystem = new MemoryFS();
}
compiler.run((err, stats) => {
if (err) {
reject(err);
@ -84,13 +79,11 @@ module.exports = async function build(siteDir, cliOptions = {}) {
);
});
// Build the client bundles first.
// We cannot run them in parallel because the server needs to know
// the correct client bundle name.
await compile(clientConfig);
// Run webpack to build js bundle (client) and static html files (server) !!
await compile([clientConfig, serverConfig]);
// Build the server bundles (render the static HTML and pick client bundle),
await compile(serverConfig, true);
// Remove server.bundle.js because it is useless
await fs.unlink(path.join(outDir, serverConfig.output.filename));
// Copy static files.
const staticDir = path.resolve(siteDir, 'static');

View file

@ -15,7 +15,6 @@ module.exports = function createClientConfig(props) {
const isProd = process.env.NODE_ENV === 'production';
const config = createBaseConfig(props);
const {generatedFilesDir} = props;
const clientConfig = merge(config, {
entry: {
main: path.resolve(__dirname, '../client/clientEntry.js'),
@ -26,9 +25,9 @@ module.exports = function createClientConfig(props) {
runtimeChunk: true,
},
plugins: [
// Generate manifests file
// Generate client manifests file
new ReactLoadableSSRAddon({
filename: path.resolve(generatedFilesDir, 'assets-manifest.json'),
filename: 'client-manifest.json',
}),
// Show compilation progress bar and build time.
new WebpackNiceLog({

View file

@ -0,0 +1,38 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const fs = require('fs');
class WaitPlugin {
constructor(options) {
this.timeout = options.timeout || 60000;
this.interval = options.interval || 250;
this.filepath = options.filepath;
}
apply(compiler) {
// Before finishing the compilation step
compiler.hooks.make.tapAsync('WaitPlugin', (compilation, callback) => {
const start = Date.now();
const {filepath, timeout, interval} = this;
// Poll until file exist
function poll() {
if (fs.existsSync(filepath)) {
callback();
} else if (Date.now() - start > timeout) {
throw Error("Maybe it just wasn't meant to be.");
} else {
setTimeout(poll, interval);
}
}
poll();
});
}
}
module.exports = WaitPlugin;

View file

@ -11,9 +11,10 @@ const StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin'
const WebpackNiceLog = require('webpack-nicelog');
const merge = require('webpack-merge');
const createBaseConfig = require('./base');
const WaitPlugin = require('./plugins/WaitPlugin');
module.exports = function createServerConfig(props) {
const {baseUrl, routesPaths} = props;
const {baseUrl, routesPaths, outDir} = props;
const config = createBaseConfig(props, true);
const isProd = process.env.NODE_ENV === 'production';
@ -31,11 +32,17 @@ module.exports = function createServerConfig(props) {
// No need to bundle its node_modules dependencies since we're bundling for static html generation (backend)
externals: [nodeExternals()],
plugins: [
// Wait until client-manifest is generated
new WaitPlugin({
filepath: path.join(outDir, 'client-manifest.json'),
}),
// Static site generator webpack plugin.
new StaticSiteGeneratorPlugin({
entry: 'main',
locals: {
baseUrl,
outDir,
},
paths: routesPaths,
}),

View file

@ -55,7 +55,6 @@
"html-webpack-plugin": "^3.2.0",
"is-wsl": "^1.1.0",
"lodash": "^4.17.11",
"memory-fs": "^0.4.1",
"mini-css-extract-plugin": "^0.4.1",
"portfinder": "^1.0.13",
"react-dev-utils": "^8.0.0",