mirror of
https://github.com/facebook/docusaurus.git
synced 2025-12-26 01:33:02 +00:00
Merge branch 'main' into ozaki/randomImageURL
This commit is contained in:
commit
1686134d1c
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
- name: Use Node.js
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: lts/*
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
- name: Installation
|
||||
run: yarn
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
- name: Installation
|
||||
run: yarn
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
- name: Track build size changes
|
||||
uses: preactjs/compressed-size-action@f780fd104362cfce9e118f9198df2ee37d12946c # v2
|
||||
|
|
@ -64,6 +64,7 @@ jobs:
|
|||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
- name: Installation
|
||||
run: yarn
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
- name: Prepare git
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
|
||||
- name: Installation
|
||||
run: yarn
|
||||
|
||||
|
|
@ -32,4 +38,4 @@ jobs:
|
|||
yarn create-docusaurus template/docusaurus-classic-ts classic --typescript -p npm
|
||||
|
||||
- name: Release
|
||||
run: npx pkg-pr-new publish './packages/*' --template './template/*' --compact --comment=off
|
||||
run: npx pkg-pr-new@0.0.20 publish './packages/*' --template './template/*' --compact --comment=off
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Use Node.js
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: lts/*
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
- name: Installation
|
||||
run: yarn
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Set up Node
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: lts/*
|
||||
cache: yarn
|
||||
- name: Installation
|
||||
run: yarn
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['18.0', '20', '22.4']
|
||||
node: ['18.0', '20', '22']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['18.0', '20', '22.4']
|
||||
node: ['18.0', '20', '22']
|
||||
steps:
|
||||
- name: Support longpaths
|
||||
run: git config --system core.longpaths true
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['18.0', '20', '22.4']
|
||||
node: ['18.0', '20', '22']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ website/_dogfooding/_swizzle_theme_tests
|
|||
|
||||
CrowdinTranslations_*.zip
|
||||
|
||||
website/.cpu-prof
|
||||
|
||||
website/i18n/**/*
|
||||
#!website/i18n/fr
|
||||
#!website/i18n/fr/**/*
|
||||
|
|
|
|||
14
package.json
14
package.json
|
|
@ -28,6 +28,8 @@
|
|||
"build:website:deployPreview:build": "cross-env NETLIFY=true CONTEXT='deploy-preview' yarn workspace website build",
|
||||
"build:website:deployPreview": "yarn build:website:deployPreview:testWrap && yarn build:website:deployPreview:build",
|
||||
"build:website:fast": "yarn workspace website build:fast",
|
||||
"build:website:fast:rsdoctor": "yarn workspace website build:fast:rsdoctor",
|
||||
"build:website:fast:profile": "yarn workspace website build:fast:profile",
|
||||
"build:website:en": "yarn workspace website build --locale en",
|
||||
"clear:website": "yarn workspace website clear",
|
||||
"serve:website": "yarn workspace website serve",
|
||||
|
|
@ -69,11 +71,11 @@
|
|||
"devDependencies": {
|
||||
"@crowdin/cli": "^3.13.0",
|
||||
"@prettier/plugin-xml": "^2.2.0",
|
||||
"@swc/core": "1.2.197",
|
||||
"@swc/jest": "^0.2.26",
|
||||
"@swc/core": "^1.7.14",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.14.197",
|
||||
"@types/node": "^18.16.19",
|
||||
"@types/prompts": "^2.4.4",
|
||||
|
|
@ -98,9 +100,9 @@
|
|||
"eslint-plugin-regexp": "^1.15.0",
|
||||
"husky": "^8.0.3",
|
||||
"image-size": "^1.0.2",
|
||||
"jest": "^29.6.1",
|
||||
"jest-environment-jsdom": "^29.6.1",
|
||||
"jest-serializer-ansi-escapes": "^2.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-serializer-ansi-escapes": "^3.0.0",
|
||||
"jest-serializer-react-helmet-async": "^1.0.21",
|
||||
"lerna": "^6.6.2",
|
||||
"lerna-changelog": "^2.2.0",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
.tsbuildinfo*
|
||||
tsconfig*
|
||||
__tests__
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# `@docusaurus/faster`
|
||||
|
||||
Docusaurus experimental package exposing new modern dependencies to make the build faster.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "@docusaurus/faster",
|
||||
"version": "3.5.2",
|
||||
"description": "Docusaurus experimental package exposing new modern dependencies to make the build faster.",
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/docusaurus.git",
|
||||
"directory": "packages/docusaurus-faster"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"webpack": "^5.88.1",
|
||||
"@swc/core": "^1.7.14",
|
||||
"swc-loader": "^0.2.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/types": "*"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import type {RuleSetRule} from 'webpack';
|
||||
|
||||
export function getSwcJsLoaderFactory({
|
||||
isServer,
|
||||
}: {
|
||||
isServer: boolean;
|
||||
}): RuleSetRule {
|
||||
return {
|
||||
loader: require.resolve('swc-loader'),
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
target: 'es2017',
|
||||
},
|
||||
module: {
|
||||
type: isServer ? 'commonjs' : 'es6',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"sourceMap": true,
|
||||
"declarationMap": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["**/__tests__/**"]
|
||||
}
|
||||
|
|
@ -28,10 +28,6 @@ import type {LoaderContext} from 'webpack';
|
|||
// See https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1517839391
|
||||
type Pluggable = any; // TODO fix this asap
|
||||
|
||||
const {
|
||||
loaders: {inlineMarkdownAssetImageFileLoader},
|
||||
} = getFileLoaderUtils();
|
||||
|
||||
export type MDXPlugin = Pluggable;
|
||||
|
||||
export type Options = Partial<MDXOptions> & {
|
||||
|
|
@ -72,7 +68,13 @@ async function readMetadataPath(metadataPath: string) {
|
|||
*
|
||||
* `{image: "./myImage.png"}` => `{image: require("./myImage.png")}`
|
||||
*/
|
||||
function createAssetsExportCode(assets: unknown) {
|
||||
function createAssetsExportCode({
|
||||
assets,
|
||||
inlineMarkdownAssetImageFileLoader,
|
||||
}: {
|
||||
assets: unknown;
|
||||
inlineMarkdownAssetImageFileLoader: string;
|
||||
}) {
|
||||
if (
|
||||
typeof assets !== 'object' ||
|
||||
!assets ||
|
||||
|
|
@ -245,13 +247,23 @@ ${JSON.stringify(frontMatter, null, 2)}`;
|
|||
? options.createAssets({frontMatter, metadata})
|
||||
: undefined;
|
||||
|
||||
const fileLoaderUtils = getFileLoaderUtils(compilerName === 'server');
|
||||
|
||||
// TODO use remark plugins to insert extra exports instead of string concat?
|
||||
// cf how the toc is exported
|
||||
const exportsCode = `
|
||||
export const frontMatter = ${stringifyObject(frontMatter)};
|
||||
export const contentTitle = ${stringifyObject(contentTitle)};
|
||||
${metadataJsonString ? `export const metadata = ${metadataJsonString};` : ''}
|
||||
${assets ? `export const assets = ${createAssetsExportCode(assets)};` : ''}
|
||||
${
|
||||
assets
|
||||
? `export const assets = ${createAssetsExportCode({
|
||||
assets,
|
||||
inlineMarkdownAssetImageFileLoader:
|
||||
fileLoaderUtils.loaders.inlineMarkdownAssetImageFileLoader,
|
||||
})};`
|
||||
: ''
|
||||
}
|
||||
`;
|
||||
|
||||
const code = `
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import {
|
|||
toMessageRelativeFilePath,
|
||||
posixPath,
|
||||
escapePath,
|
||||
getFileLoaderUtils,
|
||||
findAsyncSequential,
|
||||
getFileLoaderUtils,
|
||||
} from '@docusaurus/utils';
|
||||
import escapeHtml from 'escape-html';
|
||||
import sizeOf from 'image-size';
|
||||
|
|
@ -27,10 +27,6 @@ import type {MdxJsxTextElement} from 'mdast-util-mdx';
|
|||
import type {Image} from 'mdast';
|
||||
import type {Parent} from 'unist';
|
||||
|
||||
const {
|
||||
loaders: {inlineMarkdownImageFileLoader},
|
||||
} = getFileLoaderUtils();
|
||||
|
||||
type PluginOptions = {
|
||||
staticDirs: string[];
|
||||
siteDir: string;
|
||||
|
|
@ -38,6 +34,7 @@ type PluginOptions = {
|
|||
|
||||
type Context = PluginOptions & {
|
||||
filePath: string;
|
||||
inlineMarkdownImageFileLoader: string;
|
||||
};
|
||||
|
||||
type Target = [node: Image, index: number, parent: Parent];
|
||||
|
|
@ -45,21 +42,21 @@ type Target = [node: Image, index: number, parent: Parent];
|
|||
async function toImageRequireNode(
|
||||
[node]: Target,
|
||||
imagePath: string,
|
||||
filePath: string,
|
||||
context: Context,
|
||||
) {
|
||||
// MdxJsxTextElement => see https://github.com/facebook/docusaurus/pull/8288#discussion_r1125871405
|
||||
const jsxNode = node as unknown as MdxJsxTextElement;
|
||||
const attributes: MdxJsxTextElement['attributes'] = [];
|
||||
|
||||
let relativeImagePath = posixPath(
|
||||
path.relative(path.dirname(filePath), imagePath),
|
||||
path.relative(path.dirname(context.filePath), imagePath),
|
||||
);
|
||||
relativeImagePath = `./${relativeImagePath}`;
|
||||
|
||||
const parsedUrl = url.parse(node.url);
|
||||
const hash = parsedUrl.hash ?? '';
|
||||
const search = parsedUrl.search ?? '';
|
||||
const requireString = `${inlineMarkdownImageFileLoader}${
|
||||
const requireString = `${context.inlineMarkdownImageFileLoader}${
|
||||
escapePath(relativeImagePath) + search
|
||||
}`;
|
||||
if (node.alt) {
|
||||
|
|
@ -186,21 +183,26 @@ async function processImageNode(target: Target, context: Context) {
|
|||
// We try to convert image urls without protocol to images with require calls
|
||||
// going through webpack ensures that image assets exist at build time
|
||||
const imagePath = await getImageAbsolutePath(parsedUrl.pathname, context);
|
||||
await toImageRequireNode(target, imagePath, context.filePath);
|
||||
await toImageRequireNode(target, imagePath, context);
|
||||
}
|
||||
|
||||
export default function plugin(options: PluginOptions): Transformer {
|
||||
return async (root, vfile) => {
|
||||
const {visit} = await import('unist-util-visit');
|
||||
|
||||
const fileLoaderUtils = getFileLoaderUtils(
|
||||
vfile.data.compilerName === 'server',
|
||||
);
|
||||
const context: Context = {
|
||||
...options,
|
||||
filePath: vfile.path!,
|
||||
inlineMarkdownImageFileLoader:
|
||||
fileLoaderUtils.loaders.inlineMarkdownImageFileLoader,
|
||||
};
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
visit(root, 'image', (node: Image, index, parent) => {
|
||||
promises.push(
|
||||
processImageNode([node, index, parent!], {
|
||||
...options,
|
||||
filePath: vfile.path!,
|
||||
}),
|
||||
);
|
||||
promises.push(processImageNode([node, index, parent!], context));
|
||||
});
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import {
|
|||
toMessageRelativeFilePath,
|
||||
posixPath,
|
||||
escapePath,
|
||||
getFileLoaderUtils,
|
||||
findAsyncSequential,
|
||||
getFileLoaderUtils,
|
||||
} from '@docusaurus/utils';
|
||||
import escapeHtml from 'escape-html';
|
||||
import {assetRequireAttributeValue, transformNode} from '../utils';
|
||||
|
|
@ -24,10 +24,6 @@ import type {MdxJsxTextElement} from 'mdast-util-mdx';
|
|||
import type {Parent} from 'unist';
|
||||
import type {Link, Literal} from 'mdast';
|
||||
|
||||
const {
|
||||
loaders: {inlineMarkdownLinkFileLoader},
|
||||
} = getFileLoaderUtils();
|
||||
|
||||
type PluginOptions = {
|
||||
staticDirs: string[];
|
||||
siteDir: string;
|
||||
|
|
@ -35,6 +31,7 @@ type PluginOptions = {
|
|||
|
||||
type Context = PluginOptions & {
|
||||
filePath: string;
|
||||
inlineMarkdownLinkFileLoader: string;
|
||||
};
|
||||
|
||||
type Target = [node: Link, index: number, parent: Parent];
|
||||
|
|
@ -45,7 +42,7 @@ type Target = [node: Link, index: number, parent: Parent];
|
|||
async function toAssetRequireNode(
|
||||
[node]: Target,
|
||||
assetPath: string,
|
||||
filePath: string,
|
||||
context: Context,
|
||||
) {
|
||||
// MdxJsxTextElement => see https://github.com/facebook/docusaurus/pull/8288#discussion_r1125871405
|
||||
const jsxNode = node as unknown as MdxJsxTextElement;
|
||||
|
|
@ -53,7 +50,7 @@ async function toAssetRequireNode(
|
|||
|
||||
// require("assets/file.pdf") means requiring from a package called assets
|
||||
const relativeAssetPath = `./${posixPath(
|
||||
path.relative(path.dirname(filePath), assetPath),
|
||||
path.relative(path.dirname(context.filePath), assetPath),
|
||||
)}`;
|
||||
|
||||
const parsedUrl = url.parse(node.url);
|
||||
|
|
@ -65,7 +62,9 @@ async function toAssetRequireNode(
|
|||
path.extname(relativeAssetPath) === '.json'
|
||||
? `${relativeAssetPath.replace('.json', '.raw')}!=`
|
||||
: ''
|
||||
}${inlineMarkdownLinkFileLoader}${escapePath(relativeAssetPath) + search}`;
|
||||
}${context.inlineMarkdownLinkFileLoader}${
|
||||
escapePath(relativeAssetPath) + search
|
||||
}`;
|
||||
|
||||
attributes.push({
|
||||
type: 'mdxJsxAttribute',
|
||||
|
|
@ -196,7 +195,7 @@ async function processLinkNode(target: Target, context: Context) {
|
|||
context,
|
||||
);
|
||||
if (assetPath) {
|
||||
await toAssetRequireNode(target, assetPath, context.filePath);
|
||||
await toAssetRequireNode(target, assetPath, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,14 +203,19 @@ export default function plugin(options: PluginOptions): Transformer {
|
|||
return async (root, vfile) => {
|
||||
const {visit} = await import('unist-util-visit');
|
||||
|
||||
const fileLoaderUtils = getFileLoaderUtils(
|
||||
vfile.data.compilerName === 'server',
|
||||
);
|
||||
const context: Context = {
|
||||
...options,
|
||||
filePath: vfile.path!,
|
||||
inlineMarkdownLinkFileLoader:
|
||||
fileLoaderUtils.loaders.inlineMarkdownLinkFileLoader,
|
||||
};
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
visit(root, 'link', (node: Link, index, parent) => {
|
||||
promises.push(
|
||||
processLinkNode([node, index, parent!], {
|
||||
...options,
|
||||
filePath: vfile.path!,
|
||||
}),
|
||||
);
|
||||
promises.push(processLinkNode([node, index, parent!], context));
|
||||
});
|
||||
await Promise.all(promises);
|
||||
};
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -13,7 +13,10 @@ import {isMatch} from 'picomatch';
|
|||
import commander from 'commander';
|
||||
import webpack from 'webpack';
|
||||
import {loadContext} from '@docusaurus/core/src/server/site';
|
||||
import {applyConfigureWebpack} from '@docusaurus/core/src/webpack/configure';
|
||||
import {
|
||||
applyConfigureWebpack,
|
||||
createConfigureWebpackUtils,
|
||||
} from '@docusaurus/core/src/webpack/configure';
|
||||
import {sortRoutes} from '@docusaurus/core/src/server/plugins/routeConfig';
|
||||
import {posixPath} from '@docusaurus/utils';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
|
|
@ -22,7 +25,7 @@ import pluginContentDocs from '../index';
|
|||
import {toSidebarsProp} from '../props';
|
||||
import {DefaultSidebarItemsGenerator} from '../sidebars/generator';
|
||||
import {DisabledSidebars} from '../sidebars';
|
||||
import * as cliDocs from '../cli';
|
||||
import cliDocs from '../cli';
|
||||
import {validateOptions} from '../options';
|
||||
|
||||
import type {RouteConfig, Validate, Plugin} from '@docusaurus/types';
|
||||
|
|
@ -273,19 +276,23 @@ describe('simple website', () => {
|
|||
|
||||
const content = await plugin.loadContent?.();
|
||||
|
||||
const config = applyConfigureWebpack(
|
||||
plugin.configureWebpack as NonNullable<Plugin['configureWebpack']>,
|
||||
{
|
||||
const config = applyConfigureWebpack({
|
||||
configureWebpack: plugin.configureWebpack as NonNullable<
|
||||
Plugin['configureWebpack']
|
||||
>,
|
||||
config: {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
},
|
||||
false,
|
||||
undefined,
|
||||
isServer: false,
|
||||
utils: createConfigureWebpackUtils({
|
||||
siteConfig: {webpack: {jsLoader: 'babel'}},
|
||||
}),
|
||||
content,
|
||||
);
|
||||
});
|
||||
const errors = webpack.validate(config);
|
||||
expect(errors).toBeUndefined();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ async function createVersionedSidebarFile({
|
|||
}
|
||||
|
||||
// Tests depend on non-default export for mocking.
|
||||
export async function cliDocsVersionCommand(
|
||||
async function cliDocsVersionCommand(
|
||||
version: unknown,
|
||||
{id: pluginId, path: docsPath, sidebarPath}: PluginOptions,
|
||||
{siteDir, i18n}: LoadContext,
|
||||
|
|
@ -142,3 +142,17 @@ export async function cliDocsVersionCommand(
|
|||
|
||||
logger.success`name=${pluginIdLogPrefix}: version name=${version} created!`;
|
||||
}
|
||||
|
||||
// TODO try to remove this workaround
|
||||
// Why use a default export instead of named exports here?
|
||||
// This is only to make Jest mocking happy
|
||||
// After upgrading Jest/SWC we got this weird mocking error in extendCli tests
|
||||
// "spyOn: Cannot redefine property cliDocsVersionCommand"
|
||||
// I tried various workarounds, and it's the only one that worked :/
|
||||
// See also:
|
||||
// - https://pyk.sh/fixing-typeerror-cannot-redefine-property-x-error-in-jest-tests#heading-solution-2-using-barrel-imports
|
||||
// - https://github.com/aelbore/esbuild-jest/issues/26
|
||||
// - https://stackoverflow.com/questions/67872622/jest-spyon-not-working-on-index-file-cannot-redefine-property/69951703#69951703
|
||||
export default {
|
||||
cliDocsVersionCommand,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import {
|
|||
readVersionsMetadata,
|
||||
toFullVersion,
|
||||
} from './versions';
|
||||
import {cliDocsVersionCommand} from './cli';
|
||||
import cliDocs from './cli';
|
||||
import {VERSIONS_JSON_FILE} from './constants';
|
||||
import {toGlobalDataVersion} from './globalData';
|
||||
import {
|
||||
|
|
@ -134,7 +134,7 @@ export default async function pluginContentDocs(
|
|||
.arguments('<version>')
|
||||
.description(commandDescription)
|
||||
.action((version: unknown) =>
|
||||
cliDocsVersionCommand(version, options, context),
|
||||
cliDocs.cliDocsVersionCommand(version, options, context),
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"copy-text-to-clipboard": "^3.2.0",
|
||||
"infima": "0.2.0-alpha.44",
|
||||
"infima": "0.2.0-alpha.45",
|
||||
"lodash": "^4.17.21",
|
||||
"nprogress": "^0.2.0",
|
||||
"postcss": "^8.4.26",
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export default function BlogAuthor({
|
|||
<AuthorName name={name} as={as} />
|
||||
</MaybeLink>
|
||||
)}
|
||||
{count && <AuthorBlogPostCount count={count} />}
|
||||
{count !== undefined && <AuthorBlogPostCount count={count} />}
|
||||
</div>
|
||||
{!!title && <AuthorTitle title={title} />}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
import {
|
||||
useBlogAuthorPageTitle,
|
||||
BlogAuthorsListViewAllLabel,
|
||||
BlogAuthorNoPostsLabel,
|
||||
} from '@docusaurus/theme-common/internal';
|
||||
import Link from '@docusaurus/Link';
|
||||
import {useBlogMetadata} from '@docusaurus/plugin-content-blog/client';
|
||||
|
|
@ -52,9 +53,17 @@ function Content({author, items, sidebar, listMetadata}: Props): JSX.Element {
|
|||
{author.description && <p>{author.description}</p>}
|
||||
<ViewAllAuthorsLink />
|
||||
</header>
|
||||
<hr />
|
||||
<BlogPostItems items={items} />
|
||||
<BlogListPaginator metadata={listMetadata} />
|
||||
{items.length === 0 ? (
|
||||
<p>
|
||||
<BlogAuthorNoPostsLabel />
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<hr />
|
||||
<BlogPostItems items={items} />
|
||||
<BlogListPaginator metadata={listMetadata} />
|
||||
</>
|
||||
)}
|
||||
</BlogLayout>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,4 +96,5 @@ export {
|
|||
useBlogAuthorPageTitle,
|
||||
translateBlogAuthorsListPageTitle,
|
||||
BlogAuthorsListViewAllLabel,
|
||||
BlogAuthorNoPostsLabel,
|
||||
} from './translations/blogTranslations';
|
||||
|
|
|
|||
|
|
@ -77,3 +77,13 @@ export function BlogAuthorsListViewAllLabel(): ReactNode {
|
|||
</Translate>
|
||||
);
|
||||
}
|
||||
|
||||
export function BlogAuthorNoPostsLabel(): ReactNode {
|
||||
return (
|
||||
<Translate
|
||||
id="theme.blog.author.noPosts"
|
||||
description="The text for authors with 0 blog post">
|
||||
This author has not written any posts yet.
|
||||
</Translate>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "أرشيف",
|
||||
"theme.blog.archive.title": "أرشيف",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@
|
|||
"theme.blog.archive.description___DESCRIPTION": "The page & hero description of the blog archive page",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.archive.title___DESCRIPTION": "The page & hero title of the blog archive page",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.noPosts___DESCRIPTION": "The text for authors with 0 blog post",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.author.pageTitle___DESCRIPTION": "The title of the page for a blog author",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "Внимание",
|
||||
"theme.blog.archive.description": "Архив",
|
||||
"theme.blog.archive.title": "Архив",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warnung",
|
||||
"theme.blog.archive.description": "Archiv",
|
||||
"theme.blog.archive.title": "Archiv",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "aviso",
|
||||
"theme.blog.archive.description": "Archivo",
|
||||
"theme.blog.archive.title": "Archivo",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "hoiatus",
|
||||
"theme.blog.archive.description": "Arhiiv",
|
||||
"theme.blog.archive.title": "Arhiiv",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "هشدار",
|
||||
"theme.blog.archive.description": "آرشیو",
|
||||
"theme.blog.archive.title": "آرشیو",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "attention",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "vigyázat",
|
||||
"theme.blog.archive.description": "Archívum",
|
||||
"theme.blog.archive.title": "Archívum",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "peringatan",
|
||||
"theme.blog.archive.description": "Arsip",
|
||||
"theme.blog.archive.title": "Arsip",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "aðvörun",
|
||||
"theme.blog.archive.description": "Skjalasafn",
|
||||
"theme.blog.archive.title": "Skjalasafn",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archivio",
|
||||
"theme.blog.archive.title": "Archivio",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "警告",
|
||||
"theme.blog.archive.description": "アーカイブ",
|
||||
"theme.blog.archive.title": "アーカイブ",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "경고",
|
||||
"theme.blog.archive.description": "게시물 목록",
|
||||
"theme.blog.archive.title": "게시물 목록",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Arkiv",
|
||||
"theme.blog.archive.title": "Arkiv",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archief",
|
||||
"theme.blog.archive.title": "Archief",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archiwum",
|
||||
"theme.blog.archive.title": "Archiwum",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "atenção",
|
||||
"theme.blog.archive.description": "Arquivo",
|
||||
"theme.blog.archive.title": "Arquivo",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Архив",
|
||||
"theme.blog.archive.title": "Архив",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Arhiv",
|
||||
"theme.blog.archive.title": "Arhiv",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Архива",
|
||||
"theme.blog.archive.title": "Архива",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Arkiv",
|
||||
"theme.blog.archive.title": "Arkiv",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "duýduryş",
|
||||
"theme.blog.archive.description": "Arhiw",
|
||||
"theme.blog.archive.title": "Arhiw",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Arşiv",
|
||||
"theme.blog.archive.title": "Arşiv",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Архів",
|
||||
"theme.blog.archive.title": "Архів",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "warning",
|
||||
"theme.blog.archive.description": "Lưu trữ",
|
||||
"theme.blog.archive.title": "Lưu trữ",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "注意",
|
||||
"theme.blog.archive.description": "历史博文",
|
||||
"theme.blog.archive.title": "历史博文",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"theme.admonition.warning": "注意",
|
||||
"theme.blog.archive.description": "歷史文章",
|
||||
"theme.blog.archive.title": "歷史文章",
|
||||
"theme.blog.author.noPosts": "This author has not written any posts yet.",
|
||||
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
|
||||
"theme.blog.authorsList.pageTitle": "Authors",
|
||||
"theme.blog.authorsList.viewAll": "View All Authors",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type {SiteStorage} from './context';
|
||||
import type {RuleSetRule} from 'webpack';
|
||||
import type {Required as RequireKeys, DeepPartial} from 'utility-types';
|
||||
import type {DeepPartial, Overwrite} from 'utility-types';
|
||||
import type {I18nConfig} from './i18n';
|
||||
import type {PluginConfig, PresetConfig, HtmlTagObject} from './plugin';
|
||||
|
||||
|
|
@ -123,7 +123,13 @@ export type StorageConfig = {
|
|||
namespace: boolean | string;
|
||||
};
|
||||
|
||||
export type FasterConfig = {
|
||||
swcJsLoader: boolean;
|
||||
};
|
||||
|
||||
export type FutureConfig = {
|
||||
experimental_faster: FasterConfig;
|
||||
|
||||
experimental_storage: StorageConfig;
|
||||
|
||||
/**
|
||||
|
|
@ -416,6 +422,9 @@ export type DocusaurusConfig = {
|
|||
* Babel loader and preset; otherwise, you can provide your custom Webpack
|
||||
* rule set.
|
||||
*/
|
||||
// TODO Docusaurus v4
|
||||
// Use an object type ({isServer}) so that it conforms to jsLoaderFactory
|
||||
// Eventually deprecate this if swc loader becomes stable?
|
||||
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule);
|
||||
};
|
||||
/** Markdown-related options. */
|
||||
|
|
@ -423,11 +432,21 @@ export type DocusaurusConfig = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Docusaurus config, as provided by the user (partial/unnormalized). This type
|
||||
* Docusaurus config, as provided by the user (partial/un-normalized). This type
|
||||
* is used to provide type-safety / IDE auto-complete on the config file.
|
||||
* @see https://docusaurus.io/docs/typescript-support
|
||||
*/
|
||||
export type Config = RequireKeys<
|
||||
export type Config = Overwrite<
|
||||
DeepPartial<DocusaurusConfig>,
|
||||
'title' | 'url' | 'baseUrl'
|
||||
{
|
||||
title: DocusaurusConfig['title'];
|
||||
url: DocusaurusConfig['url'];
|
||||
baseUrl: DocusaurusConfig['baseUrl'];
|
||||
future?: Overwrite<
|
||||
DeepPartial<FutureConfig>,
|
||||
{
|
||||
experimental_faster?: boolean | FasterConfig;
|
||||
}
|
||||
>;
|
||||
}
|
||||
>;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,9 @@ export type ConfigureWebpackUtils = {
|
|||
) => RuleSetRule[];
|
||||
getJSLoader: (options: {
|
||||
isServer: boolean;
|
||||
babelOptions?: {[key: string]: unknown};
|
||||
// TODO Docusaurus v4 remove?
|
||||
// not ideal because JS Loader might not use Babel...
|
||||
babelOptions?: string | {[key: string]: unknown};
|
||||
}) => RuleSetRule;
|
||||
};
|
||||
|
||||
|
|
@ -122,8 +124,9 @@ export type Plugin<Content = unknown> = {
|
|||
head: {[location: string]: HelmetServerState};
|
||||
},
|
||||
) => Promise<void> | void;
|
||||
// TODO refactor the configureWebpack API surface: use an object instead of
|
||||
// multiple params (requires breaking change)
|
||||
// TODO Docusaurus v4 ?
|
||||
// refactor the configureWebpack API surface: use an object instead of
|
||||
// multiple params (requires breaking change)
|
||||
configureWebpack?: (
|
||||
config: WebpackConfiguration,
|
||||
isServer: boolean,
|
||||
|
|
|
|||
|
|
@ -50,12 +50,13 @@ type FileLoaderUtils = {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns unified loader configurations to be used for various file types.
|
||||
*
|
||||
* Inspired by https://github.com/gatsbyjs/gatsby/blob/8e6e021014da310b9cc7d02e58c9b3efe938c665/packages/gatsby/src/utils/webpack-utils.ts#L447
|
||||
*/
|
||||
export function getFileLoaderUtils(): FileLoaderUtils {
|
||||
// TODO this historical code is quite messy
|
||||
// We should try to get rid of it and move to assets pipeline
|
||||
function createFileLoaderUtils({
|
||||
isServer,
|
||||
}: {
|
||||
isServer: boolean;
|
||||
}): FileLoaderUtils {
|
||||
// Files/images < urlLoaderLimit will be inlined as base64 strings directly in
|
||||
// the html
|
||||
const urlLoaderLimit = WEBPACK_URL_LOADER_LIMIT;
|
||||
|
|
@ -72,6 +73,7 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
|||
loader: require.resolve(`file-loader`),
|
||||
options: {
|
||||
name: fileLoaderFileName(options.folder),
|
||||
emitFile: !isServer,
|
||||
},
|
||||
}),
|
||||
url: (options: {folder: AssetFolder}) => ({
|
||||
|
|
@ -80,6 +82,7 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
|||
limit: urlLoaderLimit,
|
||||
name: fileLoaderFileName(options.folder),
|
||||
fallback: require.resolve('file-loader'),
|
||||
emitFile: !isServer,
|
||||
},
|
||||
}),
|
||||
|
||||
|
|
@ -92,13 +95,19 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
|||
require.resolve('url-loader'),
|
||||
)}?limit=${urlLoaderLimit}&name=${fileLoaderFileName(
|
||||
'images',
|
||||
)}&fallback=${escapePath(require.resolve('file-loader'))}!`,
|
||||
)}&fallback=${escapePath(require.resolve('file-loader'))}${
|
||||
isServer ? `&emitFile=false` : ''
|
||||
}!`,
|
||||
inlineMarkdownAssetImageFileLoader: `!${escapePath(
|
||||
require.resolve('file-loader'),
|
||||
)}?name=${fileLoaderFileName('images')}!`,
|
||||
)}?name=${fileLoaderFileName('images')}${
|
||||
isServer ? `&emitFile=false` : ''
|
||||
}!`,
|
||||
inlineMarkdownLinkFileLoader: `!${escapePath(
|
||||
require.resolve('file-loader'),
|
||||
)}?name=${fileLoaderFileName('files')}!`,
|
||||
)}?name=${fileLoaderFileName('files')}${
|
||||
isServer ? `&emitFile=false` : ''
|
||||
}!`,
|
||||
};
|
||||
|
||||
const rules: FileLoaderUtils['rules'] = {
|
||||
|
|
@ -173,3 +182,16 @@ export function getFileLoaderUtils(): FileLoaderUtils {
|
|||
|
||||
return {loaders, rules};
|
||||
}
|
||||
|
||||
const FileLoaderUtilsMap = {
|
||||
server: createFileLoaderUtils({isServer: true}),
|
||||
client: createFileLoaderUtils({isServer: false}),
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns unified loader configurations to be used for various file types.
|
||||
* Inspired by https://github.com/gatsbyjs/gatsby/blob/8e6e021014da310b9cc7d02e58c9b3efe938c665/packages/gatsby/src/utils/webpack-utils.ts#L447
|
||||
*/
|
||||
export function getFileLoaderUtils(isServer: boolean): FileLoaderUtils {
|
||||
return isServer ? FileLoaderUtilsMap.server : FileLoaderUtilsMap.client;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,10 +118,16 @@
|
|||
"tree-node-cli": "^1.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@docusaurus/faster": "3.5.2",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@docusaurus/faster": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,10 @@ import {handleBrokenLinks} from '../server/brokenLinks';
|
|||
|
||||
import {createBuildClientConfig} from '../webpack/client';
|
||||
import createServerConfig from '../webpack/server';
|
||||
import {executePluginsConfigureWebpack} from '../webpack/configure';
|
||||
import {
|
||||
createConfigureWebpackUtils,
|
||||
executePluginsConfigureWebpack,
|
||||
} from '../webpack/configure';
|
||||
import {compile} from '../webpack/utils';
|
||||
import {PerfLogger} from '../utils';
|
||||
|
||||
|
|
@ -196,10 +199,7 @@ async function buildLocale({
|
|||
}),
|
||||
);
|
||||
|
||||
// Remove server.bundle.js because it is not needed.
|
||||
await PerfLogger.async('Deleting server bundle', () =>
|
||||
ensureUnlink(serverBundlePath),
|
||||
);
|
||||
await cleanupServerBundle(serverBundlePath);
|
||||
|
||||
// Plugin Lifecycle - postBuild.
|
||||
await PerfLogger.async('postBuild()', () =>
|
||||
|
|
@ -341,7 +341,9 @@ async function getBuildClientConfig({
|
|||
plugins,
|
||||
config,
|
||||
isServer: false,
|
||||
jsLoader: props.siteConfig.webpack?.jsLoader,
|
||||
utils: await createConfigureWebpackUtils({
|
||||
siteConfig: props.siteConfig,
|
||||
}),
|
||||
});
|
||||
return {clientConfig: config, clientManifestPath: result.clientManifestPath};
|
||||
}
|
||||
|
|
@ -356,13 +358,24 @@ async function getBuildServerConfig({props}: {props: Props}) {
|
|||
plugins,
|
||||
config,
|
||||
isServer: true,
|
||||
jsLoader: props.siteConfig.webpack?.jsLoader,
|
||||
utils: await createConfigureWebpackUtils({
|
||||
siteConfig: props.siteConfig,
|
||||
}),
|
||||
});
|
||||
return {serverConfig: config, serverBundlePath: result.serverBundlePath};
|
||||
}
|
||||
|
||||
async function ensureUnlink(filepath: string) {
|
||||
if (await fs.pathExists(filepath)) {
|
||||
await fs.unlink(filepath);
|
||||
// Remove /build/server server.bundle.js because it is not needed.
|
||||
async function cleanupServerBundle(serverBundlePath: string) {
|
||||
if (process.env.DOCUSAURUS_KEEP_SERVER_BUNDLE === 'true') {
|
||||
logger.warn(
|
||||
"Will NOT delete server bundle because DOCUSAURUS_KEEP_SERVER_BUNDLE is set to 'true'",
|
||||
);
|
||||
} else {
|
||||
await PerfLogger.async('Deleting server bundle', async () => {
|
||||
// For now we assume server entry is at the root of the server out dir
|
||||
const serverDir = path.dirname(serverBundlePath);
|
||||
await fs.rm(serverDir, {recursive: true, force: true});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ import {
|
|||
getHttpsConfig,
|
||||
printStatsWarnings,
|
||||
} from '../../webpack/utils';
|
||||
import {executePluginsConfigureWebpack} from '../../webpack/configure';
|
||||
import {
|
||||
createConfigureWebpackUtils,
|
||||
executePluginsConfigureWebpack,
|
||||
} from '../../webpack/configure';
|
||||
import {createStartClientConfig} from '../../webpack/client';
|
||||
import type {StartCLIOptions} from './start';
|
||||
import type {Props} from '@docusaurus/types';
|
||||
|
|
@ -139,7 +142,7 @@ async function getStartClientConfig({
|
|||
plugins,
|
||||
config,
|
||||
isServer: false,
|
||||
jsLoader: siteConfig.webpack?.jsLoader,
|
||||
utils: await createConfigureWebpackUtils({siteConfig}),
|
||||
});
|
||||
return config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import type {ConfigureWebpackUtils} from '@docusaurus/types';
|
||||
|
||||
async function importFaster() {
|
||||
return import('@docusaurus/faster');
|
||||
}
|
||||
|
||||
async function ensureFaster() {
|
||||
try {
|
||||
return await importFaster();
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
'Your Docusaurus site need to add the @docusaurus/faster package as a dependency.',
|
||||
{cause: error},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSwcJsLoaderFactory(): Promise<
|
||||
ConfigureWebpackUtils['getJSLoader']
|
||||
> {
|
||||
const faster = await ensureFaster();
|
||||
return faster.getSwcJsLoaderFactory;
|
||||
}
|
||||
|
|
@ -8,6 +8,9 @@ exports[`loadSiteConfig website with .cjs siteConfig 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -69,6 +72,9 @@ exports[`loadSiteConfig website with ts + js config 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -130,6 +136,9 @@ exports[`loadSiteConfig website with valid JS CJS config 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -191,6 +200,9 @@ exports[`loadSiteConfig website with valid JS ESM config 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -252,6 +264,9 @@ exports[`loadSiteConfig website with valid TypeScript CJS config 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -313,6 +328,9 @@ exports[`loadSiteConfig website with valid TypeScript ESM config 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -374,6 +392,9 @@ exports[`loadSiteConfig website with valid async config 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -437,6 +458,9 @@ exports[`loadSiteConfig website with valid async config creator function 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -500,6 +524,9 @@ exports[`loadSiteConfig website with valid config creator function 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
@ -566,6 +593,9 @@ exports[`loadSiteConfig website with valid siteConfig 1`] = `
|
|||
"customFields": {},
|
||||
"favicon": "img/docusaurus.ico",
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ exports[`load loads props for site with custom i18n path 1`] = `
|
|||
"clientModules": [],
|
||||
"customFields": {},
|
||||
"future": {
|
||||
"experimental_faster": {
|
||||
"swcJsLoader": false,
|
||||
},
|
||||
"experimental_router": "browser",
|
||||
"experimental_storage": {
|
||||
"namespace": false,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,17 @@
|
|||
import {
|
||||
ConfigSchema,
|
||||
DEFAULT_CONFIG,
|
||||
DEFAULT_FASTER_CONFIG,
|
||||
DEFAULT_FASTER_CONFIG_TRUE,
|
||||
DEFAULT_FUTURE_CONFIG,
|
||||
DEFAULT_STORAGE_CONFIG,
|
||||
validateConfig,
|
||||
} from '../configValidation';
|
||||
import type {StorageConfig} from '@docusaurus/types/src/config';
|
||||
import type {
|
||||
FasterConfig,
|
||||
FutureConfig,
|
||||
StorageConfig,
|
||||
} from '@docusaurus/types/src/config';
|
||||
import type {Config, DocusaurusConfig, PluginConfig} from '@docusaurus/types';
|
||||
import type {DeepPartial} from 'utility-types';
|
||||
|
||||
|
|
@ -38,6 +45,9 @@ describe('normalizeConfig', () => {
|
|||
...DEFAULT_CONFIG,
|
||||
...baseConfig,
|
||||
future: {
|
||||
experimental_faster: {
|
||||
swcJsLoader: true,
|
||||
},
|
||||
experimental_storage: {
|
||||
type: 'sessionStorage',
|
||||
namespace: true,
|
||||
|
|
@ -350,7 +360,7 @@ describe('markdown', () => {
|
|||
});
|
||||
|
||||
it('accepts valid markdown object', () => {
|
||||
const markdown: DocusaurusConfig['markdown'] = {
|
||||
const markdown: Config['markdown'] = {
|
||||
format: 'md',
|
||||
mermaid: true,
|
||||
parseFrontMatter: async (params) =>
|
||||
|
|
@ -378,7 +388,7 @@ describe('markdown', () => {
|
|||
});
|
||||
|
||||
it('accepts partial markdown object', () => {
|
||||
const markdown: DeepPartial<DocusaurusConfig['markdown']> = {
|
||||
const markdown: DeepPartial<Config['markdown']> = {
|
||||
mdx1Compat: {
|
||||
admonitions: true,
|
||||
headingIds: false,
|
||||
|
|
@ -705,12 +715,18 @@ describe('presets', () => {
|
|||
});
|
||||
|
||||
describe('future', () => {
|
||||
function futureContaining(future: Partial<FutureConfig>) {
|
||||
return expect.objectContaining({
|
||||
future: expect.objectContaining(future),
|
||||
});
|
||||
}
|
||||
|
||||
it('accepts future - undefined', () => {
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: undefined,
|
||||
}),
|
||||
).toEqual(expect.objectContaining({future: DEFAULT_CONFIG.future}));
|
||||
).toEqual(futureContaining(DEFAULT_FUTURE_CONFIG));
|
||||
});
|
||||
|
||||
it('accepts future - empty', () => {
|
||||
|
|
@ -718,11 +734,14 @@ describe('future', () => {
|
|||
normalizeConfig({
|
||||
future: {},
|
||||
}),
|
||||
).toEqual(expect.objectContaining({future: DEFAULT_CONFIG.future}));
|
||||
).toEqual(futureContaining(DEFAULT_FUTURE_CONFIG));
|
||||
});
|
||||
|
||||
it('accepts future', () => {
|
||||
it('accepts future - full', () => {
|
||||
const future: DocusaurusConfig['future'] = {
|
||||
experimental_faster: {
|
||||
swcJsLoader: true,
|
||||
},
|
||||
experimental_storage: {
|
||||
type: 'sessionStorage',
|
||||
namespace: 'myNamespace',
|
||||
|
|
@ -733,11 +752,11 @@ describe('future', () => {
|
|||
normalizeConfig({
|
||||
future,
|
||||
}),
|
||||
).toEqual(expect.objectContaining({future}));
|
||||
).toEqual(futureContaining(future));
|
||||
});
|
||||
|
||||
it('rejects future - unknown key', () => {
|
||||
const future: DocusaurusConfig['future'] = {
|
||||
const future: Config['future'] = {
|
||||
// @ts-expect-error: invalid
|
||||
doesNotExistKey: {
|
||||
type: 'sessionStorage',
|
||||
|
|
@ -763,11 +782,7 @@ describe('future', () => {
|
|||
experimental_router: undefined,
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({experimental_router: 'browser'}),
|
||||
}),
|
||||
);
|
||||
).toEqual(futureContaining({experimental_router: 'browser'}));
|
||||
});
|
||||
|
||||
it('accepts router - hash', () => {
|
||||
|
|
@ -777,11 +792,7 @@ describe('future', () => {
|
|||
experimental_router: 'hash',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({experimental_router: 'hash'}),
|
||||
}),
|
||||
);
|
||||
).toEqual(futureContaining({experimental_router: 'hash'}));
|
||||
});
|
||||
|
||||
it('accepts router - browser', () => {
|
||||
|
|
@ -791,17 +802,12 @@ describe('future', () => {
|
|||
experimental_router: 'browser',
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({experimental_router: 'browser'}),
|
||||
}),
|
||||
);
|
||||
).toEqual(futureContaining({experimental_router: 'browser'}));
|
||||
});
|
||||
|
||||
it('rejects router - invalid enum value', () => {
|
||||
// @ts-expect-error: invalid
|
||||
const router: DocusaurusConfig['future']['experimental_router'] =
|
||||
'badRouter';
|
||||
const router: Config['future']['experimental_router'] = 'badRouter';
|
||||
expect(() =>
|
||||
normalizeConfig({
|
||||
future: {
|
||||
|
|
@ -816,7 +822,7 @@ describe('future', () => {
|
|||
|
||||
it('rejects router - null', () => {
|
||||
// @ts-expect-error: bad value
|
||||
const router: DocusaurusConfig['future']['experimental_router'] = null;
|
||||
const router: Config['future']['experimental_router'] = null;
|
||||
expect(() =>
|
||||
normalizeConfig({
|
||||
future: {
|
||||
|
|
@ -832,7 +838,7 @@ describe('future', () => {
|
|||
|
||||
it('rejects router - number', () => {
|
||||
// @ts-expect-error: invalid
|
||||
const router: DocusaurusConfig['future']['experimental_router'] = 42;
|
||||
const router: Config['future']['experimental_router'] = 42;
|
||||
expect(() =>
|
||||
normalizeConfig({
|
||||
future: {
|
||||
|
|
@ -848,6 +854,12 @@ describe('future', () => {
|
|||
});
|
||||
|
||||
describe('storage', () => {
|
||||
function storageContaining(storage: Partial<StorageConfig>) {
|
||||
return futureContaining({
|
||||
experimental_storage: expect.objectContaining(storage),
|
||||
});
|
||||
}
|
||||
|
||||
it('accepts storage - undefined', () => {
|
||||
expect(
|
||||
normalizeConfig({
|
||||
|
|
@ -855,7 +867,7 @@ describe('future', () => {
|
|||
experimental_storage: undefined,
|
||||
},
|
||||
}),
|
||||
).toEqual(expect.objectContaining({future: DEFAULT_CONFIG.future}));
|
||||
).toEqual(futureContaining(DEFAULT_FUTURE_CONFIG));
|
||||
});
|
||||
|
||||
it('accepts storage - empty', () => {
|
||||
|
|
@ -863,7 +875,7 @@ describe('future', () => {
|
|||
normalizeConfig({
|
||||
future: {experimental_storage: {}},
|
||||
}),
|
||||
).toEqual(expect.objectContaining({future: DEFAULT_CONFIG.future}));
|
||||
).toEqual(futureContaining(DEFAULT_FUTURE_CONFIG));
|
||||
});
|
||||
|
||||
it('accepts storage - full', () => {
|
||||
|
|
@ -877,13 +889,7 @@ describe('future', () => {
|
|||
experimental_storage: storage,
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({
|
||||
experimental_storage: storage,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
).toEqual(storageContaining(storage));
|
||||
});
|
||||
|
||||
it('rejects storage - boolean', () => {
|
||||
|
|
@ -928,13 +934,9 @@ describe('future', () => {
|
|||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({
|
||||
experimental_storage: {
|
||||
...DEFAULT_STORAGE_CONFIG,
|
||||
...storage,
|
||||
},
|
||||
}),
|
||||
storageContaining({
|
||||
...DEFAULT_STORAGE_CONFIG,
|
||||
...storage,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
@ -949,16 +951,7 @@ describe('future', () => {
|
|||
experimental_storage: storage,
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({
|
||||
experimental_storage: {
|
||||
...DEFAULT_STORAGE_CONFIG,
|
||||
type: 'localStorage',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
).toEqual(storageContaining({type: 'localStorage'}));
|
||||
});
|
||||
|
||||
it('rejects type - null', () => {
|
||||
|
|
@ -971,10 +964,10 @@ describe('future', () => {
|
|||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_storage.type" must be one of [localStorage, sessionStorage]
|
||||
"future.experimental_storage.type" must be a string
|
||||
"
|
||||
`);
|
||||
""future.experimental_storage.type" must be one of [localStorage, sessionStorage]
|
||||
"future.experimental_storage.type" must be a string
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects type - number', () => {
|
||||
|
|
@ -987,10 +980,10 @@ describe('future', () => {
|
|||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_storage.type" must be one of [localStorage, sessionStorage]
|
||||
"future.experimental_storage.type" must be a string
|
||||
"
|
||||
`);
|
||||
""future.experimental_storage.type" must be one of [localStorage, sessionStorage]
|
||||
"future.experimental_storage.type" must be a string
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects type - invalid enum value', () => {
|
||||
|
|
@ -1003,9 +996,9 @@ describe('future', () => {
|
|||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_storage.type" must be one of [localStorage, sessionStorage]
|
||||
"
|
||||
`);
|
||||
""future.experimental_storage.type" must be one of [localStorage, sessionStorage]
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1020,16 +1013,7 @@ describe('future', () => {
|
|||
experimental_storage: storage,
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({
|
||||
experimental_storage: {
|
||||
...DEFAULT_STORAGE_CONFIG,
|
||||
...storage,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
).toEqual(storageContaining(storage));
|
||||
});
|
||||
|
||||
it('accepts namespace - string', () => {
|
||||
|
|
@ -1042,16 +1026,7 @@ describe('future', () => {
|
|||
experimental_storage: storage,
|
||||
},
|
||||
}),
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
future: expect.objectContaining({
|
||||
experimental_storage: {
|
||||
...DEFAULT_STORAGE_CONFIG,
|
||||
...storage,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
);
|
||||
).toEqual(storageContaining(storage));
|
||||
});
|
||||
|
||||
it('rejects namespace - null', () => {
|
||||
|
|
@ -1064,9 +1039,9 @@ describe('future', () => {
|
|||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_storage.namespace" must be one of [string, boolean]
|
||||
"
|
||||
`);
|
||||
""future.experimental_storage.namespace" must be one of [string, boolean]
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects namespace - number', () => {
|
||||
|
|
@ -1079,9 +1054,150 @@ describe('future', () => {
|
|||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_storage.namespace" must be one of [string, boolean]
|
||||
""future.experimental_storage.namespace" must be one of [string, boolean]
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('faster', () => {
|
||||
function fasterContaining(faster: Partial<FasterConfig>) {
|
||||
return futureContaining({
|
||||
experimental_faster: expect.objectContaining(faster),
|
||||
});
|
||||
}
|
||||
|
||||
it('accepts faster - undefined', () => {
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: undefined,
|
||||
},
|
||||
}),
|
||||
).toEqual(futureContaining(DEFAULT_FUTURE_CONFIG));
|
||||
});
|
||||
|
||||
it('accepts faster - empty', () => {
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {experimental_faster: {}},
|
||||
}),
|
||||
).toEqual(futureContaining(DEFAULT_FUTURE_CONFIG));
|
||||
});
|
||||
|
||||
it('accepts faster - full', () => {
|
||||
const faster: FasterConfig = {
|
||||
swcJsLoader: true,
|
||||
};
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: faster,
|
||||
},
|
||||
}),
|
||||
).toEqual(fasterContaining(faster));
|
||||
});
|
||||
|
||||
it('accepts faster - false', () => {
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {experimental_faster: false},
|
||||
}),
|
||||
).toEqual(fasterContaining(DEFAULT_FASTER_CONFIG));
|
||||
});
|
||||
|
||||
it('accepts faster - true', () => {
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {experimental_faster: true},
|
||||
}),
|
||||
).toEqual(fasterContaining(DEFAULT_FASTER_CONFIG_TRUE));
|
||||
});
|
||||
|
||||
it('rejects faster - number', () => {
|
||||
// @ts-expect-error: invalid
|
||||
const faster: Partial<FasterConfig> = 42;
|
||||
expect(() =>
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: faster,
|
||||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_faster" must be one of [object, boolean]
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
describe('swcJsLoader', () => {
|
||||
it('accepts - undefined', () => {
|
||||
const faster: Partial<FasterConfig> = {
|
||||
swcJsLoader: undefined,
|
||||
};
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: faster,
|
||||
},
|
||||
}),
|
||||
).toEqual(fasterContaining({swcJsLoader: false}));
|
||||
});
|
||||
|
||||
it('accepts - true', () => {
|
||||
const faster: Partial<FasterConfig> = {
|
||||
swcJsLoader: true,
|
||||
};
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: faster,
|
||||
},
|
||||
}),
|
||||
).toEqual(fasterContaining({swcJsLoader: true}));
|
||||
});
|
||||
|
||||
it('accepts - false', () => {
|
||||
const faster: Partial<FasterConfig> = {
|
||||
swcJsLoader: false,
|
||||
};
|
||||
expect(
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: faster,
|
||||
},
|
||||
}),
|
||||
).toEqual(fasterContaining({swcJsLoader: false}));
|
||||
});
|
||||
|
||||
it('rejects - null', () => {
|
||||
// @ts-expect-error: invalid
|
||||
const faster: Partial<FasterConfig> = {swcJsLoader: 42};
|
||||
expect(() =>
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: faster,
|
||||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_faster.swcJsLoader" must be a boolean
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('rejects - number', () => {
|
||||
// @ts-expect-error: invalid
|
||||
const faster: Partial<FasterConfig> = {swcJsLoader: 42};
|
||||
expect(() =>
|
||||
normalizeConfig({
|
||||
future: {
|
||||
experimental_faster: faster,
|
||||
},
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`
|
||||
""future.experimental_faster.swcJsLoader" must be a boolean
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ import {
|
|||
addLeadingSlash,
|
||||
removeTrailingSlash,
|
||||
} from '@docusaurus/utils-common';
|
||||
import type {FutureConfig, StorageConfig} from '@docusaurus/types/src/config';
|
||||
import type {
|
||||
FasterConfig,
|
||||
FutureConfig,
|
||||
StorageConfig,
|
||||
} from '@docusaurus/types/src/config';
|
||||
import type {
|
||||
DocusaurusConfig,
|
||||
I18nConfig,
|
||||
|
|
@ -37,7 +41,17 @@ export const DEFAULT_STORAGE_CONFIG: StorageConfig = {
|
|||
namespace: false,
|
||||
};
|
||||
|
||||
export const DEFAULT_FASTER_CONFIG: FasterConfig = {
|
||||
swcJsLoader: false,
|
||||
};
|
||||
|
||||
// When using the "faster: true" shortcut
|
||||
export const DEFAULT_FASTER_CONFIG_TRUE: FasterConfig = {
|
||||
swcJsLoader: true,
|
||||
};
|
||||
|
||||
export const DEFAULT_FUTURE_CONFIG: FutureConfig = {
|
||||
experimental_faster: DEFAULT_FASTER_CONFIG,
|
||||
experimental_storage: DEFAULT_STORAGE_CONFIG,
|
||||
experimental_router: 'browser',
|
||||
};
|
||||
|
|
@ -194,6 +208,20 @@ const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
|
|||
.optional()
|
||||
.default(DEFAULT_I18N_CONFIG);
|
||||
|
||||
const FASTER_CONFIG_SCHEMA = Joi.alternatives()
|
||||
.try(
|
||||
Joi.object<FasterConfig>({
|
||||
swcJsLoader: Joi.boolean().default(DEFAULT_FASTER_CONFIG.swcJsLoader),
|
||||
}),
|
||||
Joi.boolean()
|
||||
.required()
|
||||
.custom((bool) =>
|
||||
bool ? DEFAULT_FASTER_CONFIG_TRUE : DEFAULT_FASTER_CONFIG,
|
||||
),
|
||||
)
|
||||
.optional()
|
||||
.default(DEFAULT_FASTER_CONFIG);
|
||||
|
||||
const STORAGE_CONFIG_SCHEMA = Joi.object({
|
||||
type: Joi.string()
|
||||
.equal('localStorage', 'sessionStorage')
|
||||
|
|
@ -206,6 +234,7 @@ const STORAGE_CONFIG_SCHEMA = Joi.object({
|
|||
.default(DEFAULT_STORAGE_CONFIG);
|
||||
|
||||
const FUTURE_CONFIG_SCHEMA = Joi.object<FutureConfig>({
|
||||
experimental_faster: FASTER_CONFIG_SCHEMA,
|
||||
experimental_storage: STORAGE_CONFIG_SCHEMA,
|
||||
experimental_router: Joi.string()
|
||||
.equal('browser', 'hash')
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import _ from 'lodash';
|
|||
import * as utils from '@docusaurus/utils/lib/webpackUtils';
|
||||
import {posixPath} from '@docusaurus/utils';
|
||||
import {excludeJS, clientDir, createBaseConfig} from '../base';
|
||||
import {DEFAULT_FUTURE_CONFIG} from '../../server/configValidation';
|
||||
import type {Props} from '@docusaurus/types';
|
||||
|
||||
describe('babel transpilation exclude logic', () => {
|
||||
|
|
@ -66,7 +67,7 @@ describe('base webpack config', () => {
|
|||
const props = {
|
||||
outDir: '',
|
||||
siteDir: path.resolve(__dirname, '__fixtures__', 'base_test_site'),
|
||||
siteConfig: {staticDirectories: ['static'], future: {}},
|
||||
siteConfig: {staticDirectories: ['static'], future: DEFAULT_FUTURE_CONFIG},
|
||||
baseUrl: '',
|
||||
generatedFilesDir: '',
|
||||
routesPaths: [''],
|
||||
|
|
|
|||
|
|
@ -12,10 +12,17 @@ import {
|
|||
applyConfigureWebpack,
|
||||
applyConfigurePostCss,
|
||||
executePluginsConfigureWebpack,
|
||||
createConfigureWebpackUtils,
|
||||
} from '../configure';
|
||||
import type {Configuration} from 'webpack';
|
||||
import type {LoadedPlugin, Plugin} from '@docusaurus/types';
|
||||
|
||||
const utils = createConfigureWebpackUtils({
|
||||
siteConfig: {webpack: {jsLoader: 'babel'}},
|
||||
});
|
||||
|
||||
const isServer = false;
|
||||
|
||||
describe('extending generated webpack config', () => {
|
||||
it('direct mutation on generated webpack config object', async () => {
|
||||
// Fake generated webpack config
|
||||
|
|
@ -29,9 +36,9 @@ describe('extending generated webpack config', () => {
|
|||
// @ts-expect-error: Testing an edge-case that we did not write types for
|
||||
const configureWebpack: NonNullable<Plugin['configureWebpack']> = (
|
||||
generatedConfig,
|
||||
isServer,
|
||||
isServerParam,
|
||||
) => {
|
||||
if (!isServer) {
|
||||
if (!isServerParam) {
|
||||
generatedConfig.entry = 'entry.js';
|
||||
generatedConfig.output = {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
|
|
@ -41,8 +48,14 @@ describe('extending generated webpack config', () => {
|
|||
// Implicitly returning undefined to test null-safety
|
||||
};
|
||||
|
||||
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
||||
content: 42,
|
||||
config = applyConfigureWebpack({
|
||||
configureWebpack,
|
||||
config,
|
||||
isServer,
|
||||
utils,
|
||||
content: {
|
||||
content: 42,
|
||||
},
|
||||
});
|
||||
expect(config).toEqual({
|
||||
entry: 'entry.js',
|
||||
|
|
@ -71,8 +84,14 @@ describe('extending generated webpack config', () => {
|
|||
},
|
||||
});
|
||||
|
||||
config = applyConfigureWebpack(configureWebpack, config, false, undefined, {
|
||||
content: 42,
|
||||
config = applyConfigureWebpack({
|
||||
configureWebpack,
|
||||
config,
|
||||
isServer,
|
||||
utils,
|
||||
content: {
|
||||
content: 42,
|
||||
},
|
||||
});
|
||||
expect(config).toEqual({
|
||||
entry: 'entry.js',
|
||||
|
|
@ -103,39 +122,41 @@ describe('extending generated webpack config', () => {
|
|||
mergeStrategy,
|
||||
});
|
||||
|
||||
const defaultStrategyMergeConfig = applyConfigureWebpack(
|
||||
createConfigureWebpack(),
|
||||
const defaultStrategyMergeConfig = applyConfigureWebpack({
|
||||
configureWebpack: createConfigureWebpack(),
|
||||
config,
|
||||
false,
|
||||
undefined,
|
||||
{content: 42},
|
||||
);
|
||||
isServer,
|
||||
utils,
|
||||
content: {content: 42},
|
||||
});
|
||||
expect(defaultStrategyMergeConfig).toEqual({
|
||||
module: {
|
||||
rules: [{use: 'xxx'}, {use: 'yyy'}, {use: 'zzz'}],
|
||||
},
|
||||
});
|
||||
|
||||
const prependRulesStrategyConfig = applyConfigureWebpack(
|
||||
createConfigureWebpack({'module.rules': 'prepend'}),
|
||||
const prependRulesStrategyConfig = applyConfigureWebpack({
|
||||
configureWebpack: createConfigureWebpack({'module.rules': 'prepend'}),
|
||||
config,
|
||||
false,
|
||||
undefined,
|
||||
{content: 42},
|
||||
);
|
||||
isServer,
|
||||
utils,
|
||||
content: {content: 42},
|
||||
});
|
||||
expect(prependRulesStrategyConfig).toEqual({
|
||||
module: {
|
||||
rules: [{use: 'zzz'}, {use: 'xxx'}, {use: 'yyy'}],
|
||||
},
|
||||
});
|
||||
|
||||
const uselessMergeStrategyConfig = applyConfigureWebpack(
|
||||
createConfigureWebpack({uselessAttributeName: 'append'}),
|
||||
const uselessMergeStrategyConfig = applyConfigureWebpack({
|
||||
configureWebpack: createConfigureWebpack({
|
||||
uselessAttributeName: 'append',
|
||||
}),
|
||||
config,
|
||||
false,
|
||||
undefined,
|
||||
{content: 42},
|
||||
);
|
||||
isServer,
|
||||
utils,
|
||||
content: {content: 42},
|
||||
});
|
||||
expect(uselessMergeStrategyConfig).toEqual({
|
||||
module: {
|
||||
rules: [{use: 'xxx'}, {use: 'yyy'}, {use: 'zzz'}],
|
||||
|
|
@ -275,8 +296,8 @@ describe('executePluginsConfigureWebpack', () => {
|
|||
it('can merge Webpack aliases of 2 plugins into base config', () => {
|
||||
const config = executePluginsConfigureWebpack({
|
||||
config: {resolve: {alias: {'initial-alias': 'initial-alias-value'}}},
|
||||
isServer: false,
|
||||
jsLoader: 'babel',
|
||||
isServer,
|
||||
utils,
|
||||
plugins: [
|
||||
fakePlugin({
|
||||
configureWebpack: () => {
|
||||
|
|
@ -310,8 +331,8 @@ describe('executePluginsConfigureWebpack', () => {
|
|||
it('can configurePostCSS() for all loaders added through configureWebpack()', () => {
|
||||
const config = executePluginsConfigureWebpack({
|
||||
config: {},
|
||||
isServer: false,
|
||||
jsLoader: 'babel',
|
||||
isServer,
|
||||
utils,
|
||||
plugins: [
|
||||
fakePlugin({
|
||||
configurePostCss: (postCssOptions) => {
|
||||
|
|
|
|||
|
|
@ -6,40 +6,76 @@
|
|||
*/
|
||||
|
||||
import path from 'path';
|
||||
import {getCustomizableJSLoader, getHttpsConfig} from '../utils';
|
||||
import {createJsLoaderFactory, getHttpsConfig} from '../utils';
|
||||
import {DEFAULT_FUTURE_CONFIG} from '../../server/configValidation';
|
||||
import type {RuleSetRule} from 'webpack';
|
||||
|
||||
describe('customize JS loader', () => {
|
||||
it('getCustomizableJSLoader defaults to babel loader', () => {
|
||||
expect(getCustomizableJSLoader()({isServer: true}).loader).toBe(
|
||||
function testJsLoaderFactory(
|
||||
siteConfig?: Parameters<typeof createJsLoaderFactory>[0]['siteConfig'],
|
||||
) {
|
||||
return createJsLoaderFactory({
|
||||
siteConfig: {
|
||||
...siteConfig,
|
||||
webpack: {
|
||||
jsLoader: 'babel',
|
||||
...siteConfig?.webpack,
|
||||
},
|
||||
future: {
|
||||
...DEFAULT_FUTURE_CONFIG,
|
||||
...siteConfig?.future,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
it('createJsLoaderFactory defaults to babel loader', async () => {
|
||||
const createJsLoader = await testJsLoaderFactory();
|
||||
expect(createJsLoader({isServer: true}).loader).toBe(
|
||||
require.resolve('babel-loader'),
|
||||
);
|
||||
expect(getCustomizableJSLoader()({isServer: false}).loader).toBe(
|
||||
expect(createJsLoader({isServer: false}).loader).toBe(
|
||||
require.resolve('babel-loader'),
|
||||
);
|
||||
});
|
||||
|
||||
it('getCustomizableJSLoader accepts loaders with preset', () => {
|
||||
expect(getCustomizableJSLoader('babel')({isServer: true}).loader).toBe(
|
||||
require.resolve('babel-loader'),
|
||||
);
|
||||
expect(getCustomizableJSLoader('babel')({isServer: false}).loader).toBe(
|
||||
require.resolve('babel-loader'),
|
||||
);
|
||||
it('createJsLoaderFactory accepts loaders with preset', async () => {
|
||||
const createJsLoader = await testJsLoaderFactory({
|
||||
webpack: {jsLoader: 'babel'},
|
||||
});
|
||||
|
||||
expect(
|
||||
createJsLoader({
|
||||
isServer: true,
|
||||
}).loader,
|
||||
).toBe(require.resolve('babel-loader'));
|
||||
expect(
|
||||
createJsLoader({
|
||||
isServer: false,
|
||||
}).loader,
|
||||
).toBe(require.resolve('babel-loader'));
|
||||
});
|
||||
|
||||
it('getCustomizableJSLoader allows customization', () => {
|
||||
it('createJsLoaderFactory allows customization', async () => {
|
||||
const customJSLoader = (isServer: boolean): RuleSetRule => ({
|
||||
loader: 'my-fast-js-loader',
|
||||
options: String(isServer),
|
||||
});
|
||||
|
||||
expect(getCustomizableJSLoader(customJSLoader)({isServer: true})).toEqual(
|
||||
customJSLoader(true),
|
||||
);
|
||||
expect(getCustomizableJSLoader(customJSLoader)({isServer: false})).toEqual(
|
||||
customJSLoader(false),
|
||||
);
|
||||
const createJsLoader = await testJsLoaderFactory({
|
||||
webpack: {jsLoader: customJSLoader},
|
||||
});
|
||||
|
||||
expect(
|
||||
createJsLoader({
|
||||
isServer: true,
|
||||
}),
|
||||
).toEqual(customJSLoader(true));
|
||||
expect(
|
||||
createJsLoader({
|
||||
isServer: false,
|
||||
}),
|
||||
).toEqual(customJSLoader(false));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import path from 'path';
|
|||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import {md5Hash, getFileLoaderUtils} from '@docusaurus/utils';
|
||||
import {
|
||||
getCustomizableJSLoader,
|
||||
createJsLoaderFactory,
|
||||
getStyleLoaders,
|
||||
getCustomBabelConfigFilePath,
|
||||
} from './utils';
|
||||
|
|
@ -77,13 +77,15 @@ export async function createBaseConfig({
|
|||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const minimizeEnabled = minify && isProd;
|
||||
|
||||
const fileLoaderUtils = getFileLoaderUtils();
|
||||
const fileLoaderUtils = getFileLoaderUtils(isServer);
|
||||
|
||||
const name = isServer ? 'server' : 'client';
|
||||
const mode = isProd ? 'production' : 'development';
|
||||
|
||||
const themeAliases = await loadThemeAliases({siteDir, plugins});
|
||||
|
||||
const createJsLoader = await createJsLoaderFactory({siteConfig});
|
||||
|
||||
return {
|
||||
mode,
|
||||
name,
|
||||
|
|
@ -211,7 +213,7 @@ export async function createBaseConfig({
|
|||
test: /\.[jt]sx?$/i,
|
||||
exclude: excludeJS,
|
||||
use: [
|
||||
getCustomizableJSLoader(siteConfig.webpack?.jsLoader)({
|
||||
createJsLoader({
|
||||
isServer,
|
||||
babelOptions: await getCustomBabelConfigFilePath(siteDir),
|
||||
}),
|
||||
|
|
@ -231,9 +233,9 @@ export async function createBaseConfig({
|
|||
test: CSS_MODULE_REGEX,
|
||||
use: getStyleLoaders(isServer, {
|
||||
modules: {
|
||||
localIdentName: isProd
|
||||
? `[local]_[contenthash:base64:4]`
|
||||
: `[local]_[path][name]`,
|
||||
// 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,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
customizeArray,
|
||||
customizeObject,
|
||||
} from 'webpack-merge';
|
||||
import {getCustomizableJSLoader, getStyleLoaders} from './utils';
|
||||
import {createJsLoaderFactory, getStyleLoaders} from './utils';
|
||||
|
||||
import type {Configuration, RuleSetRule} from 'webpack';
|
||||
import type {
|
||||
|
|
@ -20,27 +20,43 @@ import type {
|
|||
LoadedPlugin,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
/**
|
||||
* Creates convenient utils to inject into the configureWebpack() lifecycle
|
||||
* @param config the Docusaurus config
|
||||
*/
|
||||
export async function createConfigureWebpackUtils({
|
||||
siteConfig,
|
||||
}: {
|
||||
siteConfig: Parameters<typeof createJsLoaderFactory>[0]['siteConfig'];
|
||||
}): Promise<ConfigureWebpackUtils> {
|
||||
return {
|
||||
getStyleLoaders,
|
||||
getJSLoader: await createJsLoaderFactory({siteConfig}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to modify webpack config
|
||||
* @param configureWebpack a webpack config or a function to modify config
|
||||
* @param config initial webpack config
|
||||
* @param isServer indicates if this is a server webpack configuration
|
||||
* @param jsLoader custom js loader config
|
||||
* @param utils the <code>ConfigureWebpackUtils</code> utils to inject into the configureWebpack() lifecycle
|
||||
* @param content content loaded by the plugin
|
||||
* @returns final/ modified webpack config
|
||||
*/
|
||||
export function applyConfigureWebpack(
|
||||
configureWebpack: NonNullable<Plugin['configureWebpack']>,
|
||||
config: Configuration,
|
||||
isServer: boolean,
|
||||
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) | undefined,
|
||||
content: unknown,
|
||||
): Configuration {
|
||||
// Export some utility functions
|
||||
const utils: ConfigureWebpackUtils = {
|
||||
getStyleLoaders,
|
||||
getJSLoader: getCustomizableJSLoader(jsLoader),
|
||||
};
|
||||
export function applyConfigureWebpack({
|
||||
configureWebpack,
|
||||
config,
|
||||
isServer,
|
||||
utils,
|
||||
content,
|
||||
}: {
|
||||
configureWebpack: NonNullable<Plugin['configureWebpack']>;
|
||||
config: Configuration;
|
||||
isServer: boolean;
|
||||
utils: ConfigureWebpackUtils;
|
||||
content: unknown;
|
||||
}): Configuration {
|
||||
if (typeof configureWebpack === 'function') {
|
||||
const {mergeStrategy, ...res} =
|
||||
configureWebpack(config, isServer, utils, content) ?? {};
|
||||
|
|
@ -116,27 +132,28 @@ function executePluginsConfigurePostCss({
|
|||
// Plugin Lifecycle - configureWebpack()
|
||||
export function executePluginsConfigureWebpack({
|
||||
plugins,
|
||||
config,
|
||||
config: configInput,
|
||||
isServer,
|
||||
jsLoader,
|
||||
utils,
|
||||
}: {
|
||||
plugins: LoadedPlugin[];
|
||||
config: Configuration;
|
||||
isServer: boolean;
|
||||
jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) | undefined;
|
||||
utils: ConfigureWebpackUtils;
|
||||
}): Configuration {
|
||||
let config = configInput;
|
||||
|
||||
// Step1 - Configure Webpack
|
||||
let resultConfig = config;
|
||||
plugins.forEach((plugin) => {
|
||||
const {configureWebpack} = plugin;
|
||||
if (configureWebpack) {
|
||||
resultConfig = applyConfigureWebpack(
|
||||
configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
||||
resultConfig,
|
||||
config = applyConfigureWebpack({
|
||||
configureWebpack: configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
||||
config,
|
||||
isServer,
|
||||
jsLoader,
|
||||
plugin.content,
|
||||
);
|
||||
utils,
|
||||
content: plugin.content,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -146,11 +163,11 @@ export function executePluginsConfigureWebpack({
|
|||
// See https://github.com/facebook/docusaurus/issues/10106
|
||||
// Note: it's useless to configure postCSS for the server
|
||||
if (!isServer) {
|
||||
resultConfig = executePluginsConfigurePostCss({
|
||||
config = executePluginsConfigurePostCss({
|
||||
plugins,
|
||||
config: resultConfig,
|
||||
config,
|
||||
});
|
||||
}
|
||||
|
||||
return resultConfig;
|
||||
return config;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ export default async function createServerConfig(params: {
|
|||
});
|
||||
|
||||
const outputFilename = 'server.bundle.js';
|
||||
const serverBundlePath = path.join(props.outDir, outputFilename);
|
||||
const outputDir = path.join(props.outDir, '__server');
|
||||
const serverBundlePath = path.join(outputDir, outputFilename);
|
||||
|
||||
const config = merge(baseConfig, {
|
||||
target: `node${NODE_MAJOR_VERSION}.${NODE_MINOR_VERSION}`,
|
||||
|
|
@ -35,6 +36,7 @@ export default async function createServerConfig(params: {
|
|||
main: path.resolve(__dirname, '../client/serverEntry.js'),
|
||||
},
|
||||
output: {
|
||||
path: outputDir,
|
||||
filename: outputFilename,
|
||||
libraryTarget: 'commonjs2',
|
||||
// Workaround for Webpack 4 Bug (https://github.com/webpack/webpack/issues/6522)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import {BABEL_CONFIG_FILE_NAME} from '@docusaurus/utils';
|
|||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import webpack, {type Configuration, type RuleSetRule} from 'webpack';
|
||||
import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages';
|
||||
import {getSwcJsLoaderFactory} from '../faster';
|
||||
import type {ConfigureWebpackUtils, DocusaurusConfig} from '@docusaurus/types';
|
||||
import type {TransformOptions} from '@babel/core';
|
||||
|
||||
export function formatStatsErrorMessage(
|
||||
|
|
@ -142,33 +144,49 @@ export function getBabelOptions({
|
|||
};
|
||||
}
|
||||
|
||||
// Name is generic on purpose
|
||||
// we want to support multiple js loader implementations (babel + esbuild)
|
||||
function getDefaultBabelLoader({
|
||||
const BabelJsLoaderFactory: ConfigureWebpackUtils['getJSLoader'] = ({
|
||||
isServer,
|
||||
babelOptions,
|
||||
}: {
|
||||
isServer: boolean;
|
||||
babelOptions?: TransformOptions | string;
|
||||
}): RuleSetRule {
|
||||
}) => {
|
||||
return {
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: getBabelOptions({isServer, babelOptions}),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const getCustomizableJSLoader =
|
||||
(jsLoader: 'babel' | ((isServer: boolean) => RuleSetRule) = 'babel') =>
|
||||
({
|
||||
isServer,
|
||||
babelOptions,
|
||||
}: {
|
||||
isServer: boolean;
|
||||
babelOptions?: TransformOptions | string;
|
||||
}): RuleSetRule =>
|
||||
jsLoader === 'babel'
|
||||
? getDefaultBabelLoader({isServer, babelOptions})
|
||||
: jsLoader(isServer);
|
||||
// Confusing: function that creates a function that creates actual js loaders
|
||||
// This is done on purpose because the js loader factory is a public API
|
||||
// It is injected in configureWebpack plugin lifecycle for plugin authors
|
||||
export async function createJsLoaderFactory({
|
||||
siteConfig,
|
||||
}: {
|
||||
siteConfig: {
|
||||
webpack?: DocusaurusConfig['webpack'];
|
||||
future?: {
|
||||
experimental_faster: DocusaurusConfig['future']['experimental_faster'];
|
||||
};
|
||||
};
|
||||
}): Promise<ConfigureWebpackUtils['getJSLoader']> {
|
||||
const jsLoader = siteConfig.webpack?.jsLoader ?? 'babel';
|
||||
if (
|
||||
jsLoader instanceof Function &&
|
||||
siteConfig.future?.experimental_faster.swcJsLoader
|
||||
) {
|
||||
throw new Error(
|
||||
"You can't use a custom webpack.jsLoader and experimental_faster.swcJsLoader at the same time",
|
||||
);
|
||||
}
|
||||
if (jsLoader instanceof Function) {
|
||||
return ({isServer}) => jsLoader(isServer);
|
||||
}
|
||||
if (siteConfig.future?.experimental_faster.swcJsLoader) {
|
||||
return getSwcJsLoaderFactory();
|
||||
}
|
||||
if (jsLoader === 'babel') {
|
||||
return BabelJsLoaderFactory;
|
||||
}
|
||||
throw new Error(`Docusaurus bug: unexpected jsLoader value${jsLoader}`);
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Error {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ In [#10375](https://github.com/facebook/docusaurus/pull/10375), we added the `on
|
|||
## Translations
|
||||
|
||||
- 🇪🇪 [#10339](https://github.com/facebook/docusaurus/pull/10339): Add Estonian theme translations.
|
||||
- 🇮🇩 [#10325](https://github.com/facebook/docusaurus/pull/10325): Add Estonian theme translations.
|
||||
- 🇮🇩 [#10325](https://github.com/facebook/docusaurus/pull/10325): Add Indonesian theme translations.
|
||||
- 🇪🇸 [#10360](https://github.com/facebook/docusaurus/pull/10360): Improve Spanish theme translations.
|
||||
- 🇩🇪 [#10235](https://github.com/facebook/docusaurus/pull/10235): Improve German theme translations.
|
||||
- 🇨🇳 [#10257](https://github.com/facebook/docusaurus/pull/10257): Improve Traditional Chinese (zh-Hant) theme translations.
|
||||
|
|
|
|||
|
|
@ -197,6 +197,9 @@ Example:
|
|||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
future: {
|
||||
experimental_faster: {
|
||||
swcJsLoader: true,
|
||||
},
|
||||
experimental_storage: {
|
||||
type: 'localStorage',
|
||||
namespace: true,
|
||||
|
|
@ -206,6 +209,8 @@ export default {
|
|||
};
|
||||
```
|
||||
|
||||
- `experimental_faster`: An object containing feature flags to make the Docusaurus build faster. This requires adding the `@docusaurus/faster` package to your site's dependencies. Use `true` as a shorthand to enable all flags.
|
||||
- `swcJsLoader`: Use `true` to replace the default [Babel](https://babeljs.io/) JS loader by [SWC](https://swc.rs/) loader to speed up the bundling phase.
|
||||
- `experimental_storage`: Site-wide browser storage options that theme authors should strive to respect.
|
||||
- `type`: The browser storage theme authors should use. Possible values are `localStorage` and `sessionStorage`. Defaults to `localStorage`.
|
||||
- `namespace`: Whether to namespace the browser storage keys to avoid storage key conflicts when Docusaurus sites are hosted under the same domain, or on localhost. Possible values are `string | boolean`. The namespace is appended at the end of the storage keys `key-namespace`. Use `true` to automatically generate a random namespace from your site `url + baseUrl`. Defaults to `false` (no namespace, historical behavior).
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ If you want to add CSS to any element, you can open the DevTools in your browser
|
|||
|
||||
- **Theme class names**. These class names are listed exhaustively in [the next subsection](#theme-class-names). They don't have any default properties. You should always prioritize targeting those stable class names in your custom CSS.
|
||||
- **Infima class names**. These class names are found in the classic theme and usually follow the [BEM convention](http://getbem.com/naming/) of `block__element--modifier`. They are usually stable but are still considered implementation details, so you should generally avoid targeting them. However, you can [modify Infima CSS variables](#styling-your-site-with-infima).
|
||||
- **CSS module class names**. These class names have a hash in production (`codeBlockContainer_RIuc`) and are appended with a long file path in development. They are considered implementation details and you should almost always avoid targeting them in your custom CSS. If you must, you can use an [attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) (`[class*='codeBlockContainer']`) that ignores the hash.
|
||||
- **CSS module class names**. These class names end with a hash which may change over time (`codeBlockContainer_RIuc`). They are considered implementation details and you should almost always avoid targeting them in your custom CSS. If you must, you can use an [attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) (`[class*='codeBlockContainer']`) that ignores the hash.
|
||||
|
||||
### Theme Class Names {#theme-class-names}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,14 @@ function getNextVersionName() {
|
|||
// Test with: DOCUSAURUS_CRASH_TEST=true yarn build:website:fast
|
||||
const crashTest = process.env.DOCUSAURUS_CRASH_TEST === 'true';
|
||||
|
||||
// By default, we use Docusaurus Faster
|
||||
// DOCUSAURUS_SLOWER=true is useful for benchmarking faster against slower
|
||||
// hyperfine --prepare 'yarn clear:website' --runs 3 'DOCUSAURUS_SLOWER=true yarn build:website:fast' 'yarn build:website:fast'
|
||||
const isSlower = process.env.DOCUSAURUS_SLOWER === 'true';
|
||||
if (isSlower) {
|
||||
console.log('🐢 Using slower Docusaurus build');
|
||||
}
|
||||
|
||||
const router = process.env
|
||||
.DOCUSAURUS_ROUTER as DocusaurusConfig['future']['experimental_router'];
|
||||
|
||||
|
|
@ -152,6 +160,7 @@ export default async function createConfigAsync() {
|
|||
baseUrlIssueBanner: true,
|
||||
url: 'https://docusaurus.io',
|
||||
future: {
|
||||
experimental_faster: !isSlower,
|
||||
experimental_storage: {
|
||||
namespace: true,
|
||||
},
|
||||
|
|
@ -180,28 +189,6 @@ export default async function createConfigAsync() {
|
|||
: // Production locales
|
||||
[defaultLocale, 'fr', 'pt-BR', 'ko', 'zh-CN'],
|
||||
},
|
||||
webpack: {
|
||||
jsLoader: (isServer) => ({
|
||||
loader: require.resolve('swc-loader'),
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
target: 'es2017',
|
||||
},
|
||||
module: {
|
||||
type: isServer ? 'commonjs' : 'es6',
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
markdown: {
|
||||
format: 'detect',
|
||||
mermaid: true,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
"start:blogOnly": "cross-env yarn start --config=docusaurus.config-blog-only.js",
|
||||
"build:blogOnly": "cross-env yarn build --config=docusaurus.config-blog-only.js",
|
||||
"build:fast": "cross-env BUILD_FAST=true yarn build --locale en",
|
||||
"build:fast:rsdoctor": "cross-env BUILD_FAST=true RSDOCTOR=true yarn build --locale en",
|
||||
"build:fast:profile": "cross-env BUILD_FAST=true node --cpu-prof --cpu-prof-dir .cpu-prof ./node_modules/.bin/docusaurus build --locale en",
|
||||
"netlify:build:production": "yarn docusaurus write-translations && yarn netlify:crowdin:delay && yarn netlify:crowdin:uploadSources && yarn netlify:crowdin:downloadTranslations && yarn build && yarn test:css-order",
|
||||
"netlify:build:branchDeploy": "yarn build && yarn test:css-order",
|
||||
"netlify:build:deployPreview": "yarn build && yarn test:css-order",
|
||||
|
|
@ -49,7 +51,6 @@
|
|||
"@docusaurus/theme-mermaid": "3.5.2",
|
||||
"@docusaurus/utils": "3.5.2",
|
||||
"@docusaurus/utils-common": "3.5.2",
|
||||
"@swc/core": "1.2.197",
|
||||
"clsx": "^2.0.0",
|
||||
"color": "^4.2.3",
|
||||
"fs-extra": "^11.1.1",
|
||||
|
|
@ -63,7 +64,6 @@
|
|||
"recma-mdx-displayname": "^0.4.1",
|
||||
"rehype-katex": "^7.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"swc-loader": "^0.2.3",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"webpack": "^5.88.1",
|
||||
"workbox-routing": "^7.0.0",
|
||||
|
|
|
|||
Loading…
Reference in New Issue