diff --git a/packages/documentation-framework/app.js b/packages/documentation-framework/app.js index 58bb924916..bf7285a132 100644 --- a/packages/documentation-framework/app.js +++ b/packages/documentation-framework/app.js @@ -1,5 +1,5 @@ import React from 'react'; -import ReactDOM from 'react-dom'; +import { createRoot, hydrateRoot } from 'react-dom/client'; import { Router, useLocation } from '@reach/router'; import 'client-styles'; // Webpack replaces this import: patternfly-docs.css.js import { SideNavLayout } from '@patternfly/documentation-framework/layouts'; @@ -107,8 +107,12 @@ const isPrerender = process.env.PRERENDER; // Don't use ReactDOM in SSR if (!isPrerender) { function render() { - const renderFn = isProd ? ReactDOM.hydrate : ReactDOM.render; - renderFn(, document.getElementById('root')); + const container = document.getElementById('root'); + if (isProd) { + hydrateRoot(container, ); + } else { + createRoot(container).render(); + } } // On first load, await promise for the current page to avoid flashing a "Loading..." state const Component = getAsyncComponent(null); diff --git a/packages/documentation-framework/package.json b/packages/documentation-framework/package.json index 0b22bc015d..108eebee8c 100644 --- a/packages/documentation-framework/package.json +++ b/packages/documentation-framework/package.json @@ -22,19 +22,19 @@ "@babel/preset-env": "7.18.2", "@mdx-js/util": "1.6.16", "@patternfly/ast-helpers": "^0.4.57", - "@reach/router": "1.3.4", + "@reach/router": "npm:@gatsbyjs/reach-router@1.3.9", "autoprefixer": "9.8.6", - "babel-loader": "8.2.5", + "babel-loader": "9.1.2", "camelcase-css": "2.0.1", "chokidar": "3.5.3", - "clean-webpack-plugin": "3.0.0", + "clean-webpack-plugin": "4.0.0", "codesandbox": "2.2.0", "commander": "4.1.1", - "copy-webpack-plugin": "6.1.0", - "css-loader": "4.3.0", + "copy-webpack-plugin": "11.0.0", + "css-loader": "6.7.3", "detab": "2.0.3", "express": "4.18.1", - "file-loader": "6.1.0", + "file-loader": "6.2.0", "file-saver": "1.3.8", "fs-extra": "9.0.1", "glob": "8.0.3", @@ -42,29 +42,30 @@ "hast-to-hyperscript": "9.0.0", "hast-util-to-text": "2.0.0", "html-formatter": "0.1.9", - "html-webpack-plugin": "4.4.1", + "html-webpack-plugin": "5.5.0", "js-yaml": "3.14.0", "mdast-util-to-hast": "9.1.1", "mdurl": "1.0.1", - "mini-css-extract-plugin": "1.3.9", - "monaco-editor": "0.21.3", - "monaco-editor-webpack-plugin": "2.1.0", - "null-loader": "4.0.0", + "mini-css-extract-plugin": "2.7.5", + "monaco-editor": "0.34.1", + "monaco-editor-webpack-plugin": "7.0.1", "parse-entities": "2.0.0", + "path-browserify": "1.0.1", "postcss": "7.0.32", - "postcss-loader": "4.2.0", + "postcss-loader": "7.1.0", + "process": "^0.11.10", "puppeteer": "14.3.0", "puppeteer-cluster": "0.23.0", "react-docgen": "5.3.1", - "react-monaco-editor": "0.48.0", - "react-ssr-prepass": "1.2.1", + "react-monaco-editor": "^0.51.0", + "react-ssr-prepass": "1.5.0", "remark-footnotes": "1.0.0", "remark-frontmatter": "2.0.0", "remark-mdx": "2.0.0-next.8", "remark-mdxjs": "2.0.0-next.8", "remark-parse": "8.0.3", "remark-squeeze-paragraphs": "4.0.0", - "responsive-loader": "2.1.1", + "responsive-loader": "3.1.2", "sharp": "0.30.6", "style-to-object": "0.3.0", "to-vfile": "6.1.0", @@ -74,10 +75,10 @@ "unist-util-visit": "2.0.3", "url-loader": "4.1.0", "vfile-reporter": "6.0.1", - "webpack": "4.44.1", - "webpack-bundle-analyzer": "3.8.0", - "webpack-cli": "3.3.12", - "webpack-dev-server": "3.11.0", + "webpack": "5.76.3", + "webpack-bundle-analyzer": "4.8.0", + "webpack-cli": "5.0.1", + "webpack-dev-server": "4.13.1", "webpack-merge": "5.8.0" }, "peerDependencies": { diff --git a/packages/documentation-framework/scripts/cli/start.js b/packages/documentation-framework/scripts/cli/start.js index 4b89272cc9..988f8908c7 100644 --- a/packages/documentation-framework/scripts/cli/start.js +++ b/packages/documentation-framework/scripts/cli/start.js @@ -6,17 +6,15 @@ const { getConfig } = require('./helpers'); const { watchMD } = require('../md/parseMD'); function startWebpackDevServer(webpackConfig) { - webpackConfig.devServer.filename = webpackConfig.output.filename; - webpackConfig.devServer.publicPath = webpackConfig.output.publicPath; + webpackConfig.devServer.static.publicPath = webpackConfig.output.publicPath; const { port } = webpackConfig.devServer; const compiler = webpack(webpackConfig); - const server = new WebpackDevServer(compiler, webpackConfig.devServer); + const server = new WebpackDevServer(webpackConfig.devServer, compiler); - server.listen(port, 'localhost', err => { - if (err) { - console.log(err); - } - }); + (async () => { + await server.start(); + console.log(`Dev server is listening on port ${port}`); + })(); } async function start(options) { diff --git a/packages/documentation-framework/scripts/webpack/webpack.base.config.js b/packages/documentation-framework/scripts/webpack/webpack.base.config.js index 1ede2f1a39..44c927961b 100644 --- a/packages/documentation-framework/scripts/webpack/webpack.base.config.js +++ b/packages/documentation-framework/scripts/webpack/webpack.base.config.js @@ -27,7 +27,8 @@ module.exports = (_env, argv) => { output: { publicPath: isProd ? `${pathPrefix}/` : '/', pathinfo: false, // https://webpack.js.org/guides/build-performance/#output-without-path-info, - hashDigestLength: 8 + hashDigestLength: 8, + clean: true, // Clean the output directory before emit. }, amd: false, // We don't use any AMD modules, helps performance mode: isProd ? 'production' : 'development', @@ -82,35 +83,28 @@ module.exports = (_env, argv) => { }, { test: /\.(gif|svg)$/, - use: { - loader: 'file-loader', - options: { - name: '[name].[contenthash].[ext]', - outputPath: 'images/' - }, + type: 'asset/resource', + dependency: { not: ['url'] }, + generator: { + filename: 'images/[hash][ext][query]' } }, { test: /\.(pdf)$/, - use: { - loader: 'file-loader', - options: { - name: '[name].[contenthash].[ext]', - } + type: 'asset/resource', + dependency: { not: ['url'] }, + generator: { + filename: '[hash][ext][query]' } }, { test: /.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/, - use: [ - { - loader: 'file-loader', - options: { - name: '[name].[ext]', - outputPath: 'fonts/' - } - } - ] - } + type: 'asset/resource', + dependency: { not: ['url'] }, + generator: { + filename: 'fonts/[name][ext][query]' + } + }, ] }, resolve: { @@ -118,18 +112,25 @@ module.exports = (_env, argv) => { alias: { 'client-styles': path.resolve(process.cwd(), 'patternfly-docs/patternfly-docs.css.js'), './routes-client': path.resolve(process.cwd(), 'patternfly-docs/patternfly-docs.routes.js'), - './routes-generated': path.resolve(process.cwd(), 'patternfly-docs/generated/index.js') + './routes-generated': path.resolve(process.cwd(), 'patternfly-docs/generated/index.js'), + process: "process/browser" }, modules: [ 'node_modules', ...module.paths, - ] + ], + fallback: { + "path": require.resolve("path-browserify") + }, }, // Use this module's node_modules first (for use in Core/React workspaces) resolveLoader: { modules: module.paths, }, plugins: [ + new webpack.ProvidePlugin({ + process: 'process/browser', + }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(mode), 'process.env.pathPrefix': JSON.stringify(isProd ? pathPrefix : ''), @@ -150,13 +151,9 @@ module.exports = (_env, argv) => { { from: path.join(__dirname, '../../assets'), to: 'assets' } ] }), - new MonacoWebpackPlugin(), - ...(isProd - ? [ - new CleanWebpackPlugin() - ] - : [] - ) + new MonacoWebpackPlugin({ + globalAPI: true, + }) ], stats: 'minimal' }; diff --git a/packages/documentation-framework/scripts/webpack/webpack.client.config.js b/packages/documentation-framework/scripts/webpack/webpack.client.config.js index 4600d90017..28537feb5d 100644 --- a/packages/documentation-framework/scripts/webpack/webpack.client.config.js +++ b/packages/documentation-framework/scripts/webpack/webpack.client.config.js @@ -5,6 +5,7 @@ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPl const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const CopyPlugin = require('copy-webpack-plugin'); +const webpack = require('webpack'); const baseConfig = require('./webpack.base.config'); const { getHtmlWebpackPlugins } = require('./getHtmlWebpackPlugins'); @@ -23,18 +24,20 @@ const reactJSRegex = /react-([^\\/]*)[\\/]dist[\\/].*\.js$/ const clientConfig = async (env, argv) => { const isProd = argv.mode === 'production'; - return { output: { path: argv.output ? path.resolve(argv.output) : path.resolve('public'), - filename: '[name].[hash].bundle.js' + filename: '[name].[contenthash].bundle.js' }, devServer: { hot: true, historyApiFallback: true, + compress: true, port: argv.port, - clientLogLevel: 'info', - stats: 'minimal' + client: { + logging: 'info', + }, + static: {} }, optimization: { splitChunks: { @@ -71,10 +74,7 @@ const clientConfig = async (env, argv) => { }, minimize: isProd ? true : false, minimizer: [ - new TerserPlugin({ - cache: path.join(process.cwd(), '.cache/terser'), - ...(process.env.CI ? { parallel: 2 } : {}) - }), + new TerserPlugin(), ], runtimeChunk: 'single', }, @@ -82,7 +82,6 @@ const clientConfig = async (env, argv) => { rules: [ { test: /\.css$/, - exclude: reactCSSRegex, use: [ { loader: MiniCssExtractPlugin.loader, @@ -106,13 +105,12 @@ const clientConfig = async (env, argv) => { } ] }, - { - test: reactCSSRegex, - use: 'null-loader' - }, ] }, plugins: [ + new webpack.DefinePlugin({ + 'process.env.PRERENDER': false, + }), new MiniCssExtractPlugin(!isProd ? {} : { filename: '[name].[contenthash].css', chunkFilename: '[name].[contenthash].css', diff --git a/packages/documentation-framework/scripts/webpack/webpack.server.config.js b/packages/documentation-framework/scripts/webpack/webpack.server.config.js index dd959d5792..036c592c4e 100644 --- a/packages/documentation-framework/scripts/webpack/webpack.server.config.js +++ b/packages/documentation-framework/scripts/webpack/webpack.server.config.js @@ -2,8 +2,11 @@ const path = require('path'); const webpack = require('webpack'); const { merge } = require('webpack-merge'); const baseConfig = require('./webpack.base.config'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const reactCSSRegex = /(react-[\w-]+\/dist|react-styles\/css)\/.*\.css$/; -const serverConfig = () => { +const serverConfig = async (env, argv) => { + const isProd = argv.mode === 'production'; return { output: { path: path.resolve('.cache/ssr-build'), // Don't bloat `public` dir @@ -25,13 +28,26 @@ const serverConfig = () => { rules: [ { test: /\.css$/, - use: 'null-loader' + use: [ + { + loader: 'css-loader' + }, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [ + require('autoprefixer')({ + env: '>0.25%, not ie 11, not op_mini all', + flexbox: false, + grid: false + }) + ] + } + } + } + ] }, - // This does weird things to document - { - test: /(novnc-core|@novnc\/novnc)\/.*\.js/, - use: 'null-loader' - } ] }, resolve: { @@ -41,7 +57,7 @@ const serverConfig = () => { // The maintainer will not allow his bundle to be required from a node context // https://github.com/xtermjs/xterm.js/pull/3134 'xterm': '@patternfly/documentation-framework/helpers/xterm', - 'xterm-addon-fit': '@patternfly/documentation-framework/helpers/xterm-addon-fit' + 'xterm-addon-fit': '@patternfly/documentation-framework/helpers/xterm-addon-fit', }, }, // Load in prerender.js instead @@ -49,7 +65,7 @@ const serverConfig = () => { }; } -module.exports = (env = {}, argv) => merge( +module.exports = async (env = {}, argv) => merge( baseConfig(env, argv), - serverConfig(env, argv) + await serverConfig(env, argv) ); diff --git a/packages/documentation-framework/templates/html.ejs b/packages/documentation-framework/templates/html.ejs index df2bc487ed..51fef81070 100644 --- a/packages/documentation-framework/templates/html.ejs +++ b/packages/documentation-framework/templates/html.ejs @@ -16,9 +16,7 @@ <%= htmlWebpackPlugin.tags.headTags %> -
- <%= prerendering %> -
+
<%= prerendering %>
<%= htmlWebpackPlugin.tags.bodyTags %> <% if (algolia) { %>