diff --git a/packages/docusaurus-plugin-sitemap/src/index.js b/packages/docusaurus-plugin-sitemap/src/index.js index c673cda48e..fd0713291b 100644 --- a/packages/docusaurus-plugin-sitemap/src/index.js +++ b/packages/docusaurus-plugin-sitemap/src/index.js @@ -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}`); + } + }); } } diff --git a/packages/docusaurus/lib/client/serverEntry.js b/packages/docusaurus/lib/client/serverEntry.js index 8c198612ed..2e2a882ff0 100644 --- a/packages/docusaurus/lib/client/serverEntry.js +++ b/packages/docusaurus/lib/client/serverEntry.js @@ -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); diff --git a/packages/docusaurus/lib/commands/build.js b/packages/docusaurus/lib/commands/build.js index b52a19b138..22abbb62c4 100644 --- a/packages/docusaurus/lib/commands/build.js +++ b/packages/docusaurus/lib/commands/build.js @@ -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'); diff --git a/packages/docusaurus/lib/webpack/client.js b/packages/docusaurus/lib/webpack/client.js index f76652d0f7..76d17e2031 100644 --- a/packages/docusaurus/lib/webpack/client.js +++ b/packages/docusaurus/lib/webpack/client.js @@ -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({ diff --git a/packages/docusaurus/lib/webpack/plugins/WaitPlugin.js b/packages/docusaurus/lib/webpack/plugins/WaitPlugin.js new file mode 100644 index 0000000000..102abb3fb3 --- /dev/null +++ b/packages/docusaurus/lib/webpack/plugins/WaitPlugin.js @@ -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; diff --git a/packages/docusaurus/lib/webpack/server.js b/packages/docusaurus/lib/webpack/server.js index 63a9200e90..e9a6b15966 100644 --- a/packages/docusaurus/lib/webpack/server.js +++ b/packages/docusaurus/lib/webpack/server.js @@ -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, }), diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 334eaa31fc..aa00842fdf 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -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",