mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-03 03:12:35 +02:00
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:
parent
ecb4f91669
commit
f37300a69b
7 changed files with 64 additions and 19 deletions
|
@ -55,7 +55,11 @@ class DocusaurusPluginSitemap {
|
||||||
|
|
||||||
// Write sitemap file
|
// Write sitemap file
|
||||||
const sitemapPath = path.join(outDir, 'sitemap.xml');
|
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}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@ import {Helmet} from 'react-helmet';
|
||||||
import {getBundles} from 'react-loadable-ssr-addon';
|
import {getBundles} from 'react-loadable-ssr-addon';
|
||||||
import Loadable from 'react-loadable';
|
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 routes from '@generated/routes'; // eslint-disable-line
|
||||||
import preload from './preload';
|
import preload from './preload';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
@ -42,6 +43,10 @@ export default function render(locals) {
|
||||||
];
|
];
|
||||||
const metaAttributes = metaStrings.filter(Boolean);
|
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
|
// Get all required assets for this particular page based on client manifest information
|
||||||
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
|
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
|
||||||
const bundles = getBundles(manifest, modulesToBeLoaded);
|
const bundles = getBundles(manifest, modulesToBeLoaded);
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const merge = require('webpack-merge');
|
const merge = require('webpack-merge');
|
||||||
const MemoryFS = require('memory-fs');
|
|
||||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||||
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
|
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
@ -19,13 +18,9 @@ const createServerConfig = require('../webpack/server');
|
||||||
const createClientConfig = require('../webpack/client');
|
const createClientConfig = require('../webpack/client');
|
||||||
const {applyConfigureWebpack} = require('../webpack/utils');
|
const {applyConfigureWebpack} = require('../webpack/utils');
|
||||||
|
|
||||||
function compile(config, isServer) {
|
function compile(config) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const compiler = webpack(config);
|
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) => {
|
compiler.run((err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
@ -84,13 +79,11 @@ module.exports = async function build(siteDir, cliOptions = {}) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Build the client bundles first.
|
// Run webpack to build js bundle (client) and static html files (server) !!
|
||||||
// We cannot run them in parallel because the server needs to know
|
await compile([clientConfig, serverConfig]);
|
||||||
// the correct client bundle name.
|
|
||||||
await compile(clientConfig);
|
|
||||||
|
|
||||||
// Build the server bundles (render the static HTML and pick client bundle),
|
// Remove server.bundle.js because it is useless
|
||||||
await compile(serverConfig, true);
|
await fs.unlink(path.join(outDir, serverConfig.output.filename));
|
||||||
|
|
||||||
// Copy static files.
|
// Copy static files.
|
||||||
const staticDir = path.resolve(siteDir, 'static');
|
const staticDir = path.resolve(siteDir, 'static');
|
||||||
|
|
|
@ -15,7 +15,6 @@ module.exports = function createClientConfig(props) {
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
const config = createBaseConfig(props);
|
const config = createBaseConfig(props);
|
||||||
|
|
||||||
const {generatedFilesDir} = props;
|
|
||||||
const clientConfig = merge(config, {
|
const clientConfig = merge(config, {
|
||||||
entry: {
|
entry: {
|
||||||
main: path.resolve(__dirname, '../client/clientEntry.js'),
|
main: path.resolve(__dirname, '../client/clientEntry.js'),
|
||||||
|
@ -26,9 +25,9 @@ module.exports = function createClientConfig(props) {
|
||||||
runtimeChunk: true,
|
runtimeChunk: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
// Generate manifests file
|
// Generate client manifests file
|
||||||
new ReactLoadableSSRAddon({
|
new ReactLoadableSSRAddon({
|
||||||
filename: path.resolve(generatedFilesDir, 'assets-manifest.json'),
|
filename: 'client-manifest.json',
|
||||||
}),
|
}),
|
||||||
// Show compilation progress bar and build time.
|
// Show compilation progress bar and build time.
|
||||||
new WebpackNiceLog({
|
new WebpackNiceLog({
|
||||||
|
|
38
packages/docusaurus/lib/webpack/plugins/WaitPlugin.js
Normal file
38
packages/docusaurus/lib/webpack/plugins/WaitPlugin.js
Normal 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;
|
|
@ -11,9 +11,10 @@ const StaticSiteGeneratorPlugin = require('static-site-generator-webpack-plugin'
|
||||||
const WebpackNiceLog = require('webpack-nicelog');
|
const WebpackNiceLog = require('webpack-nicelog');
|
||||||
const merge = require('webpack-merge');
|
const merge = require('webpack-merge');
|
||||||
const createBaseConfig = require('./base');
|
const createBaseConfig = require('./base');
|
||||||
|
const WaitPlugin = require('./plugins/WaitPlugin');
|
||||||
|
|
||||||
module.exports = function createServerConfig(props) {
|
module.exports = function createServerConfig(props) {
|
||||||
const {baseUrl, routesPaths} = props;
|
const {baseUrl, routesPaths, outDir} = props;
|
||||||
const config = createBaseConfig(props, true);
|
const config = createBaseConfig(props, true);
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
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)
|
// No need to bundle its node_modules dependencies since we're bundling for static html generation (backend)
|
||||||
externals: [nodeExternals()],
|
externals: [nodeExternals()],
|
||||||
plugins: [
|
plugins: [
|
||||||
|
// Wait until client-manifest is generated
|
||||||
|
new WaitPlugin({
|
||||||
|
filepath: path.join(outDir, 'client-manifest.json'),
|
||||||
|
}),
|
||||||
|
|
||||||
// Static site generator webpack plugin.
|
// Static site generator webpack plugin.
|
||||||
new StaticSiteGeneratorPlugin({
|
new StaticSiteGeneratorPlugin({
|
||||||
entry: 'main',
|
entry: 'main',
|
||||||
locals: {
|
locals: {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
|
outDir,
|
||||||
},
|
},
|
||||||
paths: routesPaths,
|
paths: routesPaths,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -55,7 +55,6 @@
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"is-wsl": "^1.1.0",
|
"is-wsl": "^1.1.0",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"memory-fs": "^0.4.1",
|
|
||||||
"mini-css-extract-plugin": "^0.4.1",
|
"mini-css-extract-plugin": "^0.4.1",
|
||||||
"portfinder": "^1.0.13",
|
"portfinder": "^1.0.13",
|
||||||
"react-dev-utils": "^8.0.0",
|
"react-dev-utils": "^8.0.0",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue