@docusaurus/core
Version:
Easy to Maintain Open Source Documentation Websites
300 lines (299 loc) • 13.8 kB
JavaScript
;
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.clientDir = void 0;
exports.excludeJS = excludeJS;
exports.createBaseConfig = createBaseConfig;
const tslib_1 = require("tslib");
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
const path_1 = tslib_1.__importDefault(require("path"));
const babel_1 = require("@docusaurus/babel");
const bundler_1 = require("@docusaurus/bundler");
const utils_1 = require("@docusaurus/utils");
const aliases_1 = require("./aliases");
const BundlerCPUProfilerPlugin_1 = require("./plugins/BundlerCPUProfilerPlugin");
const CSS_REGEX = /\.css$/i;
const CSS_MODULE_REGEX = /\.module\.css$/i;
exports.clientDir = path_1.default.join(__dirname, '..', 'client');
const LibrariesToTranspile = [
'copy-text-to-clipboard', // Contains optional catch binding, incompatible with recent versions of Edge
];
const LibrariesToTranspileRegex = new RegExp(LibrariesToTranspile.map((libName) => `(node_modules/${libName})`).join('|'));
function getReactAliases(siteDir) {
// Escape hatch
if (process.env.DOCUSAURUS_NO_REACT_ALIASES) {
return {};
}
const resolveSitePkg = (id) => require.resolve(id, { paths: [siteDir] });
return {
react: path_1.default.dirname(resolveSitePkg('react/package.json')),
'react-dom': path_1.default.dirname(resolveSitePkg('react-dom/package.json')),
'@mdx-js/react': path_1.default.dirname(resolveSitePkg('@mdx-js/react')),
};
}
function excludeJS(modulePath) {
// Always transpile client dir
if (modulePath.startsWith(exports.clientDir)) {
return false;
}
// Don't transpile node_modules except any docusaurus npm package
return (modulePath.includes('node_modules') &&
!/docusaurus(?:(?!node_modules).)*\.jsx?$/.test(modulePath) &&
!LibrariesToTranspileRegex.test(modulePath));
}
async function createBaseConfig({ props, isServer, minify, faster, configureWebpackUtils, }) {
const { outDir, siteDir, siteConfig, siteConfigPath, baseUrl, generatedFilesDir, routesPaths, siteMetadata, plugins, } = props;
const totalPages = routesPaths.length;
const isProd = process.env.NODE_ENV === 'production';
const minimizeEnabled = minify && isProd;
const fileLoaderUtils = (0, utils_1.getFileLoaderUtils)(isServer);
const name = isServer ? 'server' : 'client';
const mode = isProd ? 'production' : 'development';
const themeAliases = await (0, aliases_1.loadThemeAliases)({ siteDir, plugins });
const createJsLoader = await (0, bundler_1.createJsLoaderFactory)({ siteConfig });
const CSSExtractPlugin = await (0, bundler_1.getCSSExtractPlugin)({
currentBundler: props.currentBundler,
});
// Can we share the same cache across locales?
// Exploring that question at https://github.com/webpack/webpack/issues/13034
function getCacheName() {
return `${name}-${mode}-${props.i18n.currentLocale}`;
}
// When the version string changes, the cache is evicted
function getCacheVersion() {
// Because Webpack does not evict the cache on alias/swizzle changes,
// See https://github.com/webpack/webpack/issues/13627
const themeAliasesHash = (0, utils_1.md5Hash)(JSON.stringify(themeAliases));
return `${siteMetadata.docusaurusVersion}-${themeAliasesHash}`;
}
// When one of those modules/dependencies change (including transitive
// deps), cache is invalidated
function getCacheBuildDependencies() {
return [
__filename,
path_1.default.join(__dirname, isServer ? 'server.js' : 'client.js'),
// Docusaurus config changes can affect MDX/JSX compilation, so we'd
// rather evict the cache.
// See https://github.com/questdb/questdb.io/issues/493
siteConfigPath,
];
}
function getCache() {
// Use default: memory cache in dev, nothing in prod
// See https://rspack.dev/config/cache#cache
const disabledPersistentCacheValue = undefined;
if (process.env.DOCUSAURUS_NO_PERSISTENT_CACHE) {
return disabledPersistentCacheValue;
}
if (props.currentBundler.name === 'rspack') {
if (props.siteConfig.future.experimental_faster.rspackPersistentCache) {
// Use cache: true + experiments.cache.type: "persistent"
// See https://rspack.dev/config/experiments#persistent-cache
return true;
}
else {
return disabledPersistentCacheValue;
}
}
return {
type: 'filesystem',
name: getCacheName(),
version: getCacheVersion(),
buildDependencies: {
config: getCacheBuildDependencies(),
},
};
}
function getExperiments() {
if (props.currentBundler.name === 'rspack') {
const PersistentCacheAttributes = process.env
.DOCUSAURUS_NO_PERSISTENT_CACHE
? {}
: {
cache: {
type: 'persistent',
// Rspack doesn't have "cache.name" like Webpack
// This is not ideal but work around is to merge name/version
// See https://github.com/web-infra-dev/rspack/pull/8920#issuecomment-2658938695
version: `${getCacheName()}-${getCacheVersion()}`,
buildDependencies: getCacheBuildDependencies(),
},
};
// TODO find a way to type this
return {
// This is mostly useful in dev
// See https://rspack.dev/config/experiments#experimentsincremental
// Produces warnings in production builds
// See https://github.com/web-infra-dev/rspack/pull/8311#issuecomment-2476014664
// We use the same integration as Rspress, with ability to disable
// See https://github.com/web-infra-dev/rspress/pull/1631
// See https://github.com/facebook/docusaurus/issues/10646
// @ts-expect-error: Rspack-only, not available in Webpack typedefs
incremental: !isProd && !process.env.DISABLE_RSPACK_INCREMENTAL,
// TODO re-enable later, there's an Rspack performance issue
// see https://github.com/facebook/docusaurus/pull/11178
parallelCodeSplitting: false,
...PersistentCacheAttributes,
};
}
return undefined;
}
return {
mode,
name,
cache: getCache(),
experiments: getExperiments(),
output: {
pathinfo: false,
path: outDir,
filename: isProd ? 'assets/js/[name].[contenthash:8].js' : '[name].js',
chunkFilename: isProd
? 'assets/js/[name].[contenthash:8].js'
: '[name].js',
publicPath: siteConfig.future.experimental_router === 'hash' ? 'auto' : baseUrl,
hashFunction: 'xxhash64',
},
// Don't throw warning when asset created is over 250kb
performance: {
hints: false,
},
devtool: isProd ? undefined : 'eval-cheap-module-source-map',
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
symlinks: true, // See https://github.com/facebook/docusaurus/issues/3272
roots: [
// Allow resolution of url("/fonts/xyz.ttf") by webpack
// See https://webpack.js.org/configuration/resolve/#resolveroots
// See https://github.com/webpack-contrib/css-loader/issues/1256
...siteConfig.staticDirectories.map((dir) => path_1.default.resolve(siteDir, dir)),
siteDir,
process.cwd(),
],
alias: {
...getReactAliases(siteDir),
'@site': siteDir,
'@generated': generatedFilesDir,
...(await (0, aliases_1.loadDocusaurusAliases)()),
...themeAliases,
},
// This allows you to set a fallback for where Webpack should look for
// modules. We want `@docusaurus/core` own dependencies/`node_modules` to
// "win" if there is conflict. Example: if there is core-js@3 in user's
// own node_modules, but core depends on core-js@2, we should use
// core-js@2.
modules: [
path_1.default.resolve(__dirname, '..', '..', 'node_modules'),
'node_modules',
path_1.default.resolve(await fs_extra_1.default.realpath(process.cwd()), 'node_modules'),
],
},
resolveLoader: {
modules: ['node_modules', path_1.default.join(siteDir, 'node_modules')],
},
optimization: {
// The optimization.mergeDuplicateChunks is expensive
// - On the server, it's not useful to run it at all
// - On the client, we compared assets/js before/after and see 0 change
// `du -sk js-before js-after` => the JS assets have the exact same size
// See also https://github.com/facebook/docusaurus/pull/11176
mergeDuplicateChunks: false,
// Only minimize client bundle in production because server bundle is only
// used for static site generation
minimize: minimizeEnabled,
minimizer: minimizeEnabled
? await (0, bundler_1.getMinimizers)({ faster, currentBundler: props.currentBundler })
: undefined,
splitChunks: isServer
? false
: {
// Since the chunk name includes all origin chunk names it's
// recommended for production builds with long term caching to NOT
// include [name] in the filenames
name: false,
cacheGroups: {
// Disable the built-in cacheGroups
default: false,
common: {
name: 'common',
minChunks: totalPages > 2 ? totalPages * 0.5 : 2,
priority: 40,
},
// Only create one CSS file to avoid
// problems with code-split CSS loading in different orders
// causing inconsistent/non-deterministic styling
// See https://github.com/facebook/docusaurus/issues/2006
styles: {
name: 'styles',
type: 'css/mini-extract',
chunks: `all`,
enforce: true,
priority: 50,
},
},
},
},
module: {
rules: [
fileLoaderUtils.rules.images(),
fileLoaderUtils.rules.svgs(),
fileLoaderUtils.rules.fonts(),
fileLoaderUtils.rules.media(),
fileLoaderUtils.rules.otherAssets(),
{
test: /\.[jt]sx?$/i,
exclude: excludeJS,
use: [
createJsLoader({
isServer,
babelOptions: await (0, babel_1.getCustomBabelConfigFilePath)(siteDir),
}),
],
},
{
test: CSS_REGEX,
exclude: CSS_MODULE_REGEX,
use: configureWebpackUtils.getStyleLoaders(isServer, {
importLoaders: 1,
sourceMap: !isProd,
}),
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: CSS_MODULE_REGEX,
use: configureWebpackUtils.getStyleLoaders(isServer, {
modules: {
// Using the same CSS Module class pattern in dev/prod on purpose
// See https://github.com/facebook/docusaurus/pull/10423
localIdentName: `[local]_[contenthash:base64:4]`,
exportOnlyLocals: isServer,
},
importLoaders: 1,
sourceMap: !isProd,
}),
},
],
},
plugins: [
new CSSExtractPlugin({
filename: isProd
? 'assets/css/[name].[contenthash:8].css'
: '[name].css',
chunkFilename: isProd
? 'assets/css/[name].[contenthash:8].css'
: '[name].css',
// Remove css order warnings if css imports are not sorted
// alphabetically. See https://github.com/webpack-contrib/mini-css-extract-plugin/pull/422
// for more reasoning
ignoreOrder: true,
}),
process.env.DOCUSAURUS_BUNDLER_CPU_PROFILE &&
new BundlerCPUProfilerPlugin_1.BundlerCPUProfilerPlugin(),
].filter(Boolean),
};
}