next.config.js (123 lines of code) (raw):

/* eslint-disable @typescript-eslint/no-var-requires,@typescript-eslint/no-use-before-define,@typescript-eslint/no-empty-function,prefer-template */ const crypto = require('crypto'); const fs = require('fs'); const glob = require('glob'); const path = require('path'); const iniparser = require('iniparser'); const withBundleAnalyzer = require('@next/bundle-analyzer'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const { IgnorePlugin } = require('webpack'); /** * If you are deploying your site under a directory other than `/` e.g. * GitHub pages, then you have to tell Next where the files will be served. * We don't need this during local development, because everything is * available under `/`. */ const usePathPrefix = process.env.PATH_PREFIX === 'true'; const pathPrefix = usePathPrefix ? derivePathPrefix() : ''; const themeConfig = buildThemeConfig(); const nextConfig = { compiler: { emotion: true, }, /** Disable the `X-Powered-By: Next.js` response header. */ poweredByHeader: false, /** * When set to something other than '', this field instructs Next to * expect all paths to have a specific directory prefix. This fact is * transparent to (almost all of) the rest of the application. */ basePath: pathPrefix, images: { loader: 'custom', }, /** * Set custom `process.env.SOMETHING` values to use in the application. * You can do this with Webpack's `DefinePlugin`, but this is more concise. * It's also possible to provide values via `publicRuntimeConfig`, but * this method is preferred as it can be done statically at build time. * * @see https://nextjs.org/docs/api-reference/next.config.js/environment-variables */ env: { PATH_PREFIX: pathPrefix, THEME_CONFIG: JSON.stringify(themeConfig), }, /** * Next.js reports TypeScript errors by default. If you don't want to * leverage this behavior and prefer something else instead, like your * editor's integration, you may want to disable it. */ // typescript: { // ignoreDevErrors: true, // }, /** Customises the build */ webpack(config, { isServer }) { // EUI uses some libraries and features that don't work outside of a // browser by default. We need to configure the build so that these // features are either ignored or replaced with stub implementations. if (isServer) { config.externals = config.externals.map(eachExternal => { if (typeof eachExternal !== 'function') { return eachExternal; } return (context, callback) => { if (context.request.indexOf('@elastic/eui') > -1) { return callback(); } return eachExternal(context, callback); }; }); // Mock HTMLElement on the server-side const definePluginId = config.plugins.findIndex( p => p.constructor.name === 'DefinePlugin' ); config.plugins[definePluginId].definitions = { ...config.plugins[definePluginId].definitions, HTMLElement: function () {}, }; } // Copy theme CSS files into `public` config.plugins.push( new CopyWebpackPlugin({ patterns: themeConfig.copyConfig }), // Moment ships with a large number of locales. Exclude them, leaving // just the default English locale. If you need other locales, see: // https://create-react-app.dev/docs/troubleshooting/#momentjs-locales-are-missing new IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/, }) ); config.resolve.mainFields = ['module', 'main']; return config; }, }; /** * Enhances the Next config with the ability to: * - Analyze the webpack bundle * - Load images from JavaScript. * - Load SCSS files from JavaScript. */ module.exports = withBundleAnalyzer({ enabled: process.env.ANALYZE === 'true', })(nextConfig); /** * Find all EUI themes and construct a theme configuration object. * * The `copyConfig` key is used to configure CopyWebpackPlugin, which * copies the default EUI themes into the `public` directory, injecting a * hash into the filename so that when EUI is updated, new copies of the * themes will be fetched. * * The `availableThemes` key is used in the app to includes the themes in * the app's `<head>` element, and for theme switching. * * @return {ThemeConfig} */ function buildThemeConfig() { const themeFiles = glob.sync( path.join( __dirname, 'node_modules', '@elastic', 'eui', 'dist', 'eui_theme_*.min.css' ) ); const themeConfig = { availableThemes: [], copyConfig: [], }; for (const each of themeFiles) { const basename = path.basename(each, '.min.css'); const themeId = basename.replace(/^eui_theme_/, ''); const themeName = themeId[0].toUpperCase() + themeId.slice(1).replace(/_/g, ' '); const publicPath = `themes/${basename}.${hashFile(each)}.min.css`; const toPath = path.join( __dirname, `public`, `themes`, `${basename}.${hashFile(each)}.min.css` ); themeConfig.availableThemes.push({ id: themeId, name: themeName, publicPath, }); themeConfig.copyConfig.push({ from: each, to: toPath, }); } return themeConfig; } /** * Given a file, calculate a hash and return the first portion. The number * of characters is truncated to match how Webpack generates hashes. * * @param {string} filePath the absolute path to the file to hash. * @return string */ function hashFile(filePath) { const hash = crypto.createHash(`sha256`); const fileData = fs.readFileSync(filePath); hash.update(fileData); const fullHash = hash.digest(`hex`); // Use a hash length that matches what Webpack does return fullHash.substr(0, 20); } /** * This starter assumes that if `usePathPrefix` is true, then you're serving the site * on GitHub pages. If that isn't the case, then you can simply replace the call to * this function with whatever is the correct path prefix. * * The implementation attempts to derive a path prefix for serving up a static site by * looking at the following in order. * * 1. The git config for "origin" * 2. The `name` field in `package.json` * * Really, the first should be sufficient and correct for a GitHub Pages site, because the * repository name is what will be used to serve the site. */ function derivePathPrefix() { const gitConfigPath = path.join(__dirname, '.git', 'config'); if (fs.existsSync(gitConfigPath)) { const gitConfig = iniparser.parseSync(gitConfigPath); if (gitConfig['remote "origin"'] != null) { const originUrl = gitConfig['remote "origin"'].url; // eslint-disable-next-line prettier/prettier return '/' + originUrl.split('/').pop().replace(/\.git$/, ''); } } const packageJsonPath = path.join(__dirname, 'package.json'); if (fs.existsSync(packageJsonPath)) { const { name: packageName } = require(packageJsonPath); // Strip out any username / namespace part. This works even if there is // no username in the package name. return '/' + packageName.split('/').pop(); } throw new Error( "Can't derive path prefix, as neither .git/config nor package.json exists" ); }