feat: React 18 + automatic JSX runtime + build --dev (#8961)

This commit is contained in:
Sébastien Lorber 2023-06-08 19:40:15 +02:00 committed by GitHub
parent 76f920359b
commit 187e5aa218
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
69 changed files with 404 additions and 209 deletions

5
.eslintrc.js vendored
View file

@ -66,6 +66,8 @@ module.exports = {
'@docusaurus', '@docusaurus',
], ],
rules: { rules: {
'react/jsx-uses-react': OFF, // JSX runtime: automatic
'react/react-in-jsx-scope': OFF, // JSX runtime: automatic
'array-callback-return': WARNING, 'array-callback-return': WARNING,
camelcase: WARNING, camelcase: WARNING,
'class-methods-use-this': OFF, // It's a way of allowing private variables. 'class-methods-use-this': OFF, // It's a way of allowing private variables.
@ -259,6 +261,9 @@ module.exports = {
}, },
{pattern: '@jest/globals', group: 'builtin', position: 'before'}, {pattern: '@jest/globals', group: 'builtin', position: 'before'},
{pattern: 'react', group: 'builtin', position: 'before'}, {pattern: 'react', group: 'builtin', position: 'before'},
{pattern: 'react-dom', group: 'builtin', position: 'before'},
{pattern: 'react-dom/**', group: 'builtin', position: 'before'},
{pattern: 'stream', group: 'builtin', position: 'before'},
{pattern: 'fs-extra', group: 'builtin'}, {pattern: 'fs-extra', group: 'builtin'},
{pattern: 'lodash', group: 'external', position: 'before'}, {pattern: 'lodash', group: 'external', position: 'before'},
{pattern: 'clsx', group: 'external', position: 'before'}, {pattern: 'clsx', group: 'external', position: 'before'},

1
jest.config.mjs vendored
View file

@ -32,6 +32,7 @@ const ignorePatterns = [
export default { export default {
rootDir: fileURLToPath(new URL('.', import.meta.url)), rootDir: fileURLToPath(new URL('.', import.meta.url)),
verbose: true, verbose: true,
setupFiles: ['./jest/setup.js'],
testEnvironmentOptions: { testEnvironmentOptions: {
url: 'https://docusaurus.io/', url: 'https://docusaurus.io/',
}, },

11
jest/setup.js vendored Normal file
View file

@ -0,0 +1,11 @@
/**
* 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 {TextEncoder} from 'util';
// Required for RTL renderHook SSR tests with React-18
// See also https://github.com/testing-library/react-testing-library/issues/1120#issuecomment-1516132279
global.TextEncoder = TextEncoder;

View file

@ -105,10 +105,10 @@
"lint-staged": "^13.1.2", "lint-staged": "^13.1.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"react": "^17.0.2", "react": "^18.0.0",
"react-dom": "^17.0.2", "react-dom": "^18.0.0",
"react-helmet-async": "^1.3.0", "react-helmet-async": "^1.3.0",
"react-test-renderer": "^17.0.2", "react-test-renderer": "^18.0.0",
"remark-parse": "^8.0.2", "remark-parse": "^8.0.2",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sharp": "^0.31.3", "sharp": "^0.31.3",

View file

@ -17,11 +17,11 @@
"dependencies": { "dependencies": {
"@docusaurus/core": "^3.0.0-alpha.0", "@docusaurus/core": "^3.0.0-alpha.0",
"@docusaurus/preset-classic": "^3.0.0-alpha.0", "@docusaurus/preset-classic": "^3.0.0-alpha.0",
"@mdx-js/react": "^2.1.5", "@mdx-js/react": "^2.3.0",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"prism-react-renderer": "^1.3.5", "prism-react-renderer": "^1.3.5",
"react": "^17.0.2", "react": "^18.0.0",
"react-dom": "^17.0.2" "react-dom": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "^3.0.0-alpha.0", "@docusaurus/module-type-aliases": "^3.0.0-alpha.0",

View file

@ -16,11 +16,11 @@
"dependencies": { "dependencies": {
"@docusaurus/core": "^3.0.0-alpha.0", "@docusaurus/core": "^3.0.0-alpha.0",
"@docusaurus/preset-classic": "^3.0.0-alpha.0", "@docusaurus/preset-classic": "^3.0.0-alpha.0",
"@mdx-js/react": "^2.1.5", "@mdx-js/react": "^2.3.0",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"prism-react-renderer": "^1.3.5", "prism-react-renderer": "^1.3.5",
"react": "^17.0.2", "react": "^18.0.0",
"react-dom": "^17.0.2" "react-dom": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "^3.0.0-alpha.0" "@docusaurus/module-type-aliases": "^3.0.0-alpha.0"

View file

@ -59,8 +59,8 @@
"unist-util-remove-position": "^3.0.0" "unist-util-remove-position": "^3.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -242,6 +242,12 @@ declare module '@docusaurus/router' {
export {useHistory, useLocation, Redirect, matchPath} from 'react-router-dom'; export {useHistory, useLocation, Redirect, matchPath} from 'react-router-dom';
} }
declare module '@docusaurus/useIsomorphicLayoutEffect' {
import {useLayoutEffect} from 'react';
export = useLayoutEffect;
}
declare module '@docusaurus/useDocusaurusContext' { declare module '@docusaurus/useDocusaurusContext' {
import type {DocusaurusContext} from '@docusaurus/types'; import type {DocusaurusContext} from '@docusaurus/types';

View file

@ -32,8 +32,8 @@
"@docusaurus/types": "^3.0.0-alpha.0" "@docusaurus/types": "^3.0.0-alpha.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -36,8 +36,8 @@
"webpack": "^5.76.0" "webpack": "^5.76.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -60,8 +60,8 @@
"shelljs": "^0.8.5" "shelljs": "^0.8.5"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -28,8 +28,8 @@
"webpack": "^5.76.0" "webpack": "^5.76.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -28,8 +28,8 @@
"tslib": "^2.5.0" "tslib": "^2.5.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -24,8 +24,8 @@
"tslib": "^2.5.0" "tslib": "^2.5.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -25,8 +25,8 @@
"tslib": "^2.5.0" "tslib": "^2.5.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -24,8 +24,8 @@
"tslib": "^2.5.0" "tslib": "^2.5.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -26,7 +26,7 @@
"@docusaurus/theme-translations": "^3.0.0-alpha.0", "@docusaurus/theme-translations": "^3.0.0-alpha.0",
"@docusaurus/types": "^3.0.0-alpha.0", "@docusaurus/types": "^3.0.0-alpha.0",
"@docusaurus/utils-validation": "^3.0.0-alpha.0", "@docusaurus/utils-validation": "^3.0.0-alpha.0",
"@endiliey/react-ideal-image": "^0.0.11", "@slorber/react-ideal-image": "^0.0.12",
"react-waypoint": "^10.3.0", "react-waypoint": "^10.3.0",
"sharp": "^0.31.3", "sharp": "^0.31.3",
"tslib": "^2.5.0", "tslib": "^2.5.0",
@ -38,8 +38,8 @@
}, },
"peerDependencies": { "peerDependencies": {
"jimp": "*", "jimp": "*",
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"jimp": { "jimp": {

View file

@ -12,7 +12,7 @@
* Note: the original type definition is WRONG. getIcon & getMessage receive * Note: the original type definition is WRONG. getIcon & getMessage receive
* full state object. * full state object.
*/ */
declare module '@endiliey/react-ideal-image' { declare module '@slorber/react-ideal-image' {
import type {ComponentProps, ComponentType, CSSProperties} from 'react'; import type {ComponentProps, ComponentType, CSSProperties} from 'react';
export type LoadingState = 'initial' | 'loading' | 'loaded' | 'error'; export type LoadingState = 'initial' | 'loading' | 'loaded' | 'error';

View file

@ -9,7 +9,7 @@ import React from 'react';
import ReactIdealImage, { import ReactIdealImage, {
type IconKey, type IconKey,
type State, type State,
} from '@endiliey/react-ideal-image'; } from '@slorber/react-ideal-image';
import {translate} from '@docusaurus/Translate'; import {translate} from '@docusaurus/Translate';
import type {Props} from '@theme/IdealImage'; import type {Props} from '@theme/IdealImage';

View file

@ -45,8 +45,8 @@
"fs-extra": "^11.1.0" "fs-extra": "^11.1.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -72,8 +72,14 @@ async function getIsAppInstalledRelatedApps() {
if (!('getInstalledRelatedApps' in window.navigator)) { if (!('getInstalledRelatedApps' in window.navigator)) {
return false; return false;
} }
const relatedApps = await navigator.getInstalledRelatedApps(); try {
return relatedApps.some((app) => app.platform === 'webapp'); const relatedApps = await navigator.getInstalledRelatedApps();
return relatedApps.some((app) => app.platform === 'webapp');
} catch (e) {
// Error might be thrown when Docusaurus is embedded in an iframe:
// registerSW failed DOMException: Failed to execute 'getInstalledRelatedApps' on 'Navigator': getInstalledRelatedApps() is only supported in top-level browsing contexts.
return false;
}
} }
function isStandaloneDisplayMode() { function isStandaloneDisplayMode() {
return window.matchMedia('(display-mode: standalone)').matches; return window.matchMedia('(display-mode: standalone)').matches;

View file

@ -29,8 +29,8 @@
"tslib": "^2.5.0" "tslib": "^2.5.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -33,8 +33,8 @@
"@docusaurus/types": "^3.0.0-alpha.0" "@docusaurus/types": "^3.0.0-alpha.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -52,12 +52,12 @@
"@types/prismjs": "^1.26.0", "@types/prismjs": "^1.26.0",
"@types/rtlcss": "^3.5.0", "@types/rtlcss": "^3.5.0",
"fs-extra": "^11.1.0", "fs-extra": "^11.1.0",
"react-test-renderer": "^17.0.2", "react-test-renderer": "^18.0.0",
"utility-types": "^3.10.0" "utility-types": "^3.10.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -54,8 +54,8 @@
"lodash": "^4.17.21" "lodash": "^4.17.21"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -10,13 +10,13 @@ import React, {
useEffect, useEffect,
useRef, useRef,
useCallback, useCallback,
useLayoutEffect,
type RefObject, type RefObject,
type Dispatch, type Dispatch,
type SetStateAction, type SetStateAction,
type ReactNode, type ReactNode,
} from 'react'; } from 'react';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import useIsomorphicLayoutEffect from '@docusaurus/useIsomorphicLayoutEffect';
import {prefersReducedMotion} from '../../utils/accessibilityUtils'; import {prefersReducedMotion} from '../../utils/accessibilityUtils';
const DefaultAnimationEasing = 'ease-in-out'; const DefaultAnimationEasing = 'ease-in-out';
@ -231,13 +231,13 @@ function CollapsibleLazy({collapsed, ...props}: CollapsibleBaseProps) {
// Updated in effect so that first expansion transition can work // Updated in effect so that first expansion transition can work
const [lazyCollapsed, setLazyCollapsed] = useState(collapsed); const [lazyCollapsed, setLazyCollapsed] = useState(collapsed);
useLayoutEffect(() => { useIsomorphicLayoutEffect(() => {
if (!collapsed) { if (!collapsed) {
setMounted(true); setMounted(true);
} }
}, [collapsed]); }, [collapsed]);
useLayoutEffect(() => { useIsomorphicLayoutEffect(() => {
if (mounted) { if (mounted) {
setLazyCollapsed(collapsed); setLazyCollapsed(collapsed);
} }

View file

@ -48,7 +48,6 @@ export {ThemeClassNames} from './utils/ThemeClassNames';
export {prefersReducedMotion} from './utils/accessibilityUtils'; export {prefersReducedMotion} from './utils/accessibilityUtils';
export { export {
useIsomorphicLayoutEffect,
useEvent, useEvent,
usePrevious, usePrevious,
composeProviders, composeProviders,

View file

@ -7,26 +7,12 @@
import React, { import React, {
useCallback, useCallback,
useEffect,
useLayoutEffect,
useMemo, useMemo,
useRef, useRef,
type ComponentType, type ComponentType,
type ReactNode, type ReactNode,
} from 'react'; } from 'react';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import useIsomorphicLayoutEffect from '@docusaurus/useIsomorphicLayoutEffect';
/**
* This hook is like `useLayoutEffect`, but without the SSR warning.
* It seems hacky but it's used in many React libs (Redux, Formik...).
* Also mentioned here: https://github.com/facebook/react/issues/16956
*
* It is useful when you need to update a ref as soon as possible after a React
* render (before `useEffect`).
*/
export const useIsomorphicLayoutEffect = ExecutionEnvironment.canUseDOM
? useLayoutEffect
: useEffect;
/** /**
* Temporary userland implementation until an official hook is implemented * Temporary userland implementation until an official hook is implemented

View file

@ -9,13 +9,13 @@ import React, {
useCallback, useCallback,
useContext, useContext,
useEffect, useEffect,
useLayoutEffect,
useMemo, useMemo,
useRef, useRef,
type ReactNode, type ReactNode,
} from 'react'; } from 'react';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import useIsBrowser from '@docusaurus/useIsBrowser'; import useIsBrowser from '@docusaurus/useIsBrowser';
import useIsomorphicLayoutEffect from '@docusaurus/useIsomorphicLayoutEffect';
import {useEvent, ReactContextError} from './reactUtils'; import {useEvent, ReactContextError} from './reactUtils';
type ScrollController = { type ScrollController = {
@ -221,7 +221,7 @@ export function useScrollPositionBlocker(): {
[scrollController, scrollPositionSaver], [scrollController, scrollPositionSaver],
); );
useLayoutEffect(() => { useIsomorphicLayoutEffect(() => {
// Queuing permits to restore scroll position after all useLayoutEffect // Queuing permits to restore scroll position after all useLayoutEffect
// have run, and yet preserve the sync nature of the scroll restoration // have run, and yet preserve the sync nature of the scroll restoration
// See https://github.com/facebook/docusaurus/issues/8625 // See https://github.com/facebook/docusaurus/issues/8625

View file

@ -12,9 +12,9 @@ import React, {
useMemo, useMemo,
type ReactNode, type ReactNode,
type ReactElement, type ReactElement,
useLayoutEffect,
} from 'react'; } from 'react';
import {useHistory} from '@docusaurus/router'; import {useHistory} from '@docusaurus/router';
import useIsomorphicLayoutEffect from '@docusaurus/useIsomorphicLayoutEffect';
import {useQueryStringValue} from '@docusaurus/theme-common/internal'; import {useQueryStringValue} from '@docusaurus/theme-common/internal';
import {duplicates, useStorageSlot} from '../index'; import {duplicates, useStorageSlot} from '../index';
@ -252,7 +252,7 @@ export function useTabs(props: TabsProps): {
})(); })();
// Sync in a layout/sync effect is important, for useScrollPositionBlocker // Sync in a layout/sync effect is important, for useScrollPositionBlocker
// See https://github.com/facebook/docusaurus/issues/8625 // See https://github.com/facebook/docusaurus/issues/8625
useLayoutEffect(() => { useIsomorphicLayoutEffect(() => {
if (valueToSync) { if (valueToSync) {
setSelectedValue(valueToSync); setSelectedValue(valueToSync);
} }

View file

@ -38,8 +38,8 @@
"@types/buble": "^0.20.1" "@types/buble": "^0.20.1"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -43,11 +43,11 @@
}, },
"devDependencies": { "devDependencies": {
"@types/mdx-js__react": "^1.5.5", "@types/mdx-js__react": "^1.5.5",
"react-test-renderer": "^17.0.2" "react-test-renderer": "^18.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -54,8 +54,8 @@
"@docusaurus/module-type-aliases": "^3.0.0-alpha.0" "@docusaurus/module-type-aliases": "^3.0.0-alpha.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -23,7 +23,7 @@
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
} }
} }

View file

@ -37,6 +37,10 @@ cli.version(DOCUSAURUS_VERSION).usage('<command> [options]');
cli cli
.command('build [siteDir]') .command('build [siteDir]')
.description('Build website.') .description('Build website.')
.option(
'--dev',
'Builds the website in dev mode, including full React error messages.',
)
.option( .option(
'--bundle-analyzer', '--bundle-analyzer',
'visualize size of webpack output files with an interactive zoomable tree map (default: false)', 'visualize size of webpack output files with an interactive zoomable tree map (default: false)',

View file

@ -116,13 +116,13 @@
"@types/update-notifier": "^6.0.2", "@types/update-notifier": "^6.0.2",
"@types/wait-on": "^5.3.1", "@types/wait-on": "^5.3.1",
"@types/webpack-bundle-analyzer": "^4.6.0", "@types/webpack-bundle-analyzer": "^4.6.0",
"react-test-renderer": "^17.0.2", "react-test-renderer": "^18.0.0",
"tmp-promise": "^3.0.3", "tmp-promise": "^3.0.3",
"tree-node-cli": "^1.6.0" "tree-node-cli": "^1.6.0"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.8.4 || ^17.0.0", "react": "^18.0.0",
"react-dom": "^16.8.4 || ^17.0.0" "react-dom": "^18.0.0"
}, },
"engines": { "engines": {
"node": ">=16.14" "node": ">=16.14"

View file

@ -38,7 +38,12 @@ function getTransformOptions(isServer: boolean): TransformOptions {
exclude: ['transform-typeof-symbol'], exclude: ['transform-typeof-symbol'],
}, },
], ],
require.resolve('@babel/preset-react'), [
require.resolve('@babel/preset-react'),
{
runtime: 'automatic',
},
],
require.resolve('@babel/preset-typescript'), require.resolve('@babel/preset-typescript'),
], ],
plugins: [ plugins: [

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import React, {useLayoutEffect} from 'react'; import React from 'react';
import {useLocation} from '@docusaurus/router'; import {useLocation} from '@docusaurus/router';
import Head from '@docusaurus/Head'; import Head from '@docusaurus/Head';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
@ -21,8 +21,6 @@ const BannerId = '__docusaurus-base-url-issue-banner';
const SuggestionContainerId = const SuggestionContainerId =
'__docusaurus-base-url-issue-banner-suggestion-container'; '__docusaurus-base-url-issue-banner-suggestion-container';
const InsertBannerWindowAttribute = '__DOCUSAURUS_INSERT_BASEURL_BANNER';
// It is important to not use React to render this banner // It is important to not use React to render this banner
// otherwise Google would index it, even if it's hidden with some critical CSS! // otherwise Google would index it, even if it's hidden with some critical CSS!
// See https://github.com/facebook/docusaurus/issues/4028 // See https://github.com/facebook/docusaurus/issues/4028
@ -45,24 +43,19 @@ function createInlineHtmlBanner(baseUrl: string) {
function createInlineScript(baseUrl: string) { function createInlineScript(baseUrl: string) {
/* language=js */ /* language=js */
return ` return `
window['${InsertBannerWindowAttribute}'] = true; document.addEventListener('DOMContentLoaded', function maybeInsertBanner() {
var shouldInsert = typeof window['docusaurus'] === 'undefined';
document.addEventListener('DOMContentLoaded', maybeInsertBanner);
function maybeInsertBanner() {
var shouldInsert = window['${InsertBannerWindowAttribute}'];
shouldInsert && insertBanner(); shouldInsert && insertBanner();
} });
function insertBanner() { function insertBanner() {
var bannerContainer = document.getElementById('${BannerContainerId}'); var bannerContainer = document.createElement('div');
if (!bannerContainer) { bannerContainer.id = '${BannerContainerId}';
return;
}
var bannerHtml = ${JSON.stringify(createInlineHtmlBanner(baseUrl)) var bannerHtml = ${JSON.stringify(createInlineHtmlBanner(baseUrl))
// See https://redux.js.org/recipes/server-rendering/#security-considerations // See https://redux.js.org/recipes/server-rendering/#security-considerations
.replace(/</g, '\\\u003c')}; .replace(/</g, '\\\u003c')};
bannerContainer.innerHTML = bannerHtml; bannerContainer.innerHTML = bannerHtml;
document.body.prepend(bannerContainer);
var suggestionContainer = document.getElementById('${SuggestionContainerId}'); var suggestionContainer = document.getElementById('${SuggestionContainerId}');
var actualHomePagePath = window.location.pathname; var actualHomePagePath = window.location.pathname;
var suggestedBaseUrl = actualHomePagePath.substr(-1) === '/' var suggestedBaseUrl = actualHomePagePath.substr(-1) === '/'
@ -73,23 +66,11 @@ function insertBanner() {
`; `;
} }
declare global {
interface Window {
[InsertBannerWindowAttribute]: boolean;
}
}
function BaseUrlIssueBanner() { function BaseUrlIssueBanner() {
const { const {
siteConfig: {baseUrl}, siteConfig: {baseUrl},
} = useDocusaurusContext(); } = useDocusaurusContext();
// useLayoutEffect fires before DOMContentLoaded.
// It gives the opportunity to avoid inserting the banner in the first place
useLayoutEffect(() => {
window[InsertBannerWindowAttribute] = false;
}, []);
return ( return (
<> <>
{!ExecutionEnvironment.canUseDOM && ( {!ExecutionEnvironment.canUseDOM && (
@ -99,7 +80,6 @@ function BaseUrlIssueBanner() {
<script>{createInlineScript(baseUrl)}</script> <script>{createInlineScript(baseUrl)}</script>
</Head> </Head>
)} )}
<div id={BannerContainerId} />
</> </>
); );
} }

View file

@ -5,8 +5,9 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {useLayoutEffect, type ReactElement} from 'react'; import {type ReactElement} from 'react';
import clientModules from '@generated/client-modules'; import clientModules from '@generated/client-modules';
import useIsomorphicLayoutEffect from './exports/useIsomorphicLayoutEffect';
import type {ClientModule} from '@docusaurus/types'; import type {ClientModule} from '@docusaurus/types';
import type {Location} from 'history'; import type {Location} from 'history';
@ -66,7 +67,7 @@ function ClientLifecyclesDispatcher({
location: Location; location: Location;
previousLocation: Location | null; previousLocation: Location | null;
}): JSX.Element { }): JSX.Element {
useLayoutEffect(() => { useIsomorphicLayoutEffect(() => {
if (previousLocation !== location) { if (previousLocation !== location) {
scrollAfterNavigation({location, previousLocation}); scrollAfterNavigation({location, previousLocation});
dispatchLifecycleAction('onRouteDidUpdate', {previousLocation, location}); dispatchLifecycleAction('onRouteDidUpdate', {previousLocation, location});

View file

@ -10,6 +10,8 @@
// Jest doesn't allow pragma below other comments. https://github.com/facebook/jest/issues/12573 // Jest doesn't allow pragma below other comments. https://github.com/facebook/jest/issues/12573
// eslint-disable-next-line header/header // eslint-disable-next-line header/header
import React from 'react'; import React from 'react';
// TODO migrate to @testing-library/react when SSR rendering possible
// See https://github.com/testing-library/react-testing-library/issues/1120
import {renderHook} from '@testing-library/react-hooks/server'; import {renderHook} from '@testing-library/react-hooks/server';
import {BrowserContextProvider} from '../browserContext'; import {BrowserContextProvider} from '../browserContext';
import useIsBrowser from '../exports/useIsBrowser'; import useIsBrowser from '../exports/useIsBrowser';

View file

@ -10,6 +10,8 @@
// Jest doesn't allow pragma below other comments. https://github.com/facebook/jest/issues/12573 // Jest doesn't allow pragma below other comments. https://github.com/facebook/jest/issues/12573
// eslint-disable-next-line header/header // eslint-disable-next-line header/header
import React from 'react'; import React from 'react';
// TODO migrate to @testing-library/react when SSR rendering possible
// See https://github.com/testing-library/react-testing-library/issues/1120
import {renderHook} from '@testing-library/react-hooks/server'; import {renderHook} from '@testing-library/react-hooks/server';
import {DocusaurusContextProvider} from '../docusaurusContext'; import {DocusaurusContextProvider} from '../docusaurusContext';
import useDocusaurusContext from '../exports/useDocusaurusContext'; import useDocusaurusContext from '../exports/useDocusaurusContext';

View file

@ -6,6 +6,8 @@
*/ */
import React from 'react'; import React from 'react';
// TODO migrate to @testing-library/react when SSR rendering possible
// See https://github.com/testing-library/react-testing-library/issues/1120
import {renderHook} from '@testing-library/react-hooks/server'; import {renderHook} from '@testing-library/react-hooks/server';
import {RouteContextProvider} from '../routeContext'; import {RouteContextProvider} from '../routeContext';
import useRouteContext from '../exports/useRouteContext'; import useRouteContext from '../exports/useRouteContext';

View file

@ -6,7 +6,7 @@
*/ */
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom/client';
import {BrowserRouter} from 'react-router-dom'; import {BrowserRouter} from 'react-router-dom';
import {HelmetProvider} from 'react-helmet-async'; import {HelmetProvider} from 'react-helmet-async';
@ -21,26 +21,38 @@ declare global {
} }
} }
const hydrate = Boolean(process.env.HYDRATE_CLIENT_ENTRY);
// Client-side render (e.g: running in browser) to become single-page // Client-side render (e.g: running in browser) to become single-page
// application (SPA). // application (SPA).
if (ExecutionEnvironment.canUseDOM) { if (ExecutionEnvironment.canUseDOM) {
window.docusaurus = docusaurus; window.docusaurus = docusaurus;
// For production, attempt to hydrate existing markup for performant const container = document.getElementById('__docusaurus')!;
// first-load experience.
// For development, there is no existing markup so we had to render it. const app = (
// We also preload async component to avoid first-load loading screen. <HelmetProvider>
const renderMethod = <BrowserRouter>
process.env.NODE_ENV === 'production' ? ReactDOM.hydrate : ReactDOM.render; <App />
preload(window.location.pathname).then(() => { </BrowserRouter>
renderMethod( </HelmetProvider>
<HelmetProvider> );
<BrowserRouter>
<App /> const onRecoverableError = (error: unknown): void => {
</BrowserRouter> console.error('Docusaurus React Root onRecoverableError:', error);
</HelmetProvider>, };
document.getElementById('__docusaurus'),
); const renderApp = () => {
}); if (hydrate) {
ReactDOM.hydrateRoot(container, app, {
onRecoverableError,
});
} else {
const root = ReactDOM.createRoot(container, {onRecoverableError});
root.render(app);
}
};
preload(window.location.pathname).then(renderApp);
// Webpack Hot Module Replacement API // Webpack Hot Module Replacement API
if (module.hot) { if (module.hot) {

View file

@ -6,6 +6,8 @@
*/ */
import React from 'react'; import React from 'react';
// TODO migrate to @testing-library/react when SSR rendering possible
// See https://github.com/testing-library/react-testing-library/issues/1120
import {renderHook} from '@testing-library/react-hooks/server'; import {renderHook} from '@testing-library/react-hooks/server';
import {RouteContextProvider} from '../../routeContext'; import {RouteContextProvider} from '../../routeContext';
import useRouteContext from '../useRouteContext'; import useRouteContext from '../useRouteContext';

View file

@ -0,0 +1,27 @@
/**
* 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 {useEffect, useLayoutEffect} from 'react';
import ExecutionEnvironment from './ExecutionEnvironment';
/**
* This hook is like `useLayoutEffect`, but without the SSR warning.
* It seems hacky but it's used in many React libs (Redux, Formik...).
* Also mentioned here: https://github.com/facebook/react/issues/16956
*
* It is useful when you need to update a ref as soon as possible after a React
* render (before `useEffect`).
*
* TODO should become unnecessary in React v19?
* https://github.com/facebook/react/pull/26395
* This was added in core with Docusaurus v3 but kept undocumented on purpose
*/
const useIsomorphicLayoutEffect = ExecutionEnvironment.canUseDOM
? useLayoutEffect
: useEffect;
export default useIsomorphicLayoutEffect;

View file

@ -12,11 +12,11 @@ import fs from 'fs-extra';
import _ from 'lodash'; import _ from 'lodash';
import * as eta from 'eta'; import * as eta from 'eta';
import {StaticRouter} from 'react-router-dom'; import {StaticRouter} from 'react-router-dom';
import ReactDOMServer from 'react-dom/server';
import {HelmetProvider, type FilledContext} from 'react-helmet-async'; import {HelmetProvider, type FilledContext} from 'react-helmet-async';
import {getBundles, type Manifest} from 'react-loadable-ssr-addon-v5-slorber'; import {getBundles, type Manifest} from 'react-loadable-ssr-addon-v5-slorber';
import Loadable from 'react-loadable'; import Loadable from 'react-loadable';
import {minify} from 'html-minifier-terser'; import {minify} from 'html-minifier-terser';
import {renderStaticApp} from './serverRenderer';
import preload from './preload'; import preload from './preload';
import App from './App'; import App from './App';
import { import {
@ -97,7 +97,8 @@ async function doRender(locals: Locals & {path: string}) {
const helmetContext = {}; const helmetContext = {};
const linksCollector = createStatefulLinksCollector(); const linksCollector = createStatefulLinksCollector();
const appHtml = ReactDOMServer.renderToString(
const app = (
// @ts-expect-error: we are migrating away from react-loadable anyways // @ts-expect-error: we are migrating away from react-loadable anyways
<Loadable.Capture report={(moduleName) => modules.add(moduleName)}> <Loadable.Capture report={(moduleName) => modules.add(moduleName)}>
<HelmetProvider context={helmetContext}> <HelmetProvider context={helmetContext}>
@ -107,8 +108,10 @@ async function doRender(locals: Locals & {path: string}) {
</LinksCollectorProvider> </LinksCollectorProvider>
</StaticRouter> </StaticRouter>
</HelmetProvider> </HelmetProvider>
</Loadable.Capture>, </Loadable.Capture>
); );
const appHtml = await renderStaticApp(app);
onLinksCollected(location, linksCollector.getCollectedLinks()); onLinksCollected(location, linksCollector.getCollectedLinks());
const {helmet} = helmetContext as FilledContext; const {helmet} = helmetContext as FilledContext;

View file

@ -0,0 +1,80 @@
/**
* 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 {ReactNode} from 'react';
import {renderToPipeableStream} from 'react-dom/server';
import {Writable} from 'stream';
export async function renderStaticApp(app: ReactNode): Promise<string> {
// Inspired from
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
// https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/cache-dir/static-entry.js
const writableStream = new WritableAsPromise();
const {pipe} = renderToPipeableStream(app, {
onError(error) {
writableStream.destroy(error as Error);
},
onAllReady() {
pipe(writableStream);
},
});
return writableStream.getPromise();
}
// WritableAsPromise inspired by https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/cache-dir/server-utils/writable-as-promise.js
/* eslint-disable no-underscore-dangle */
class WritableAsPromise extends Writable {
private _output: string;
private _deferred: {
promise: Promise<string> | null;
resolve: (value: string) => void;
reject: (reason: Error) => void;
};
constructor() {
super();
this._output = ``;
this._deferred = {
promise: null,
resolve: () => null,
reject: () => null,
};
this._deferred.promise = new Promise((resolve, reject) => {
this._deferred.resolve = resolve;
this._deferred.reject = reject;
});
}
override _write(
chunk: {toString: () => string},
_enc: unknown,
next: () => void,
) {
this._output += chunk.toString();
next();
}
override _destroy(error: Error | null, next: (error?: Error | null) => void) {
if (error instanceof Error) {
this._deferred.reject(error);
} else {
next();
}
}
override end() {
this._deferred.resolve(this._output);
return this.destroy();
}
getPromise(): Promise<string> {
return this._deferred.promise!;
}
}

View file

@ -35,6 +35,7 @@ export type BuildCLIOptions = Pick<
> & { > & {
bundleAnalyzer?: boolean; bundleAnalyzer?: boolean;
minify?: boolean; minify?: boolean;
dev?: boolean;
}; };
export async function build( export async function build(
@ -49,6 +50,11 @@ export async function build(
process.env.BABEL_ENV = 'production'; process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
process.env.DOCUSAURUS_CURRENT_LOCALE = cliOptions.locale; process.env.DOCUSAURUS_CURRENT_LOCALE = cliOptions.locale;
if (cliOptions.dev) {
logger.info`Building in dev mode`;
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
}
const siteDir = await fs.realpath(siteDirParam); const siteDir = await fs.realpath(siteDirParam);
@ -158,7 +164,7 @@ async function buildLocale({
'client-manifest.json', 'client-manifest.json',
); );
let clientConfig: Configuration = merge( let clientConfig: Configuration = merge(
await createClientConfig(props, cliOptions.minify), await createClientConfig(props, cliOptions.minify, true),
{ {
plugins: [ plugins: [
// Remove/clean build folders before building bundles. // Remove/clean build folders before building bundles.

View file

@ -123,7 +123,7 @@ export async function start(
); );
let config: webpack.Configuration = merge( let config: webpack.Configuration = merge(
await createClientConfig(props, cliOptions.minify), await createClientConfig(props, cliOptions.minify, false),
{ {
watchOptions: { watchOptions: {
ignored: /node_modules\/(?!@docusaurus)/, ignored: /node_modules\/(?!@docusaurus)/,

View file

@ -19,6 +19,7 @@ exports[`base webpack config creates webpack aliases 1`] = `
"@docusaurus/useDocusaurusContext": "../../../../client/exports/useDocusaurusContext.ts", "@docusaurus/useDocusaurusContext": "../../../../client/exports/useDocusaurusContext.ts",
"@docusaurus/useGlobalData": "../../../../client/exports/useGlobalData.ts", "@docusaurus/useGlobalData": "../../../../client/exports/useGlobalData.ts",
"@docusaurus/useIsBrowser": "../../../../client/exports/useIsBrowser.ts", "@docusaurus/useIsBrowser": "../../../../client/exports/useIsBrowser.ts",
"@docusaurus/useIsomorphicLayoutEffect": "../../../../client/exports/useIsomorphicLayoutEffect.tsx",
"@docusaurus/useRouteContext": "../../../../client/exports/useRouteContext.tsx", "@docusaurus/useRouteContext": "../../../../client/exports/useRouteContext.tsx",
"@generated": "../../../../../../..", "@generated": "../../../../../../..",
"@site": "", "@site": "",

View file

@ -19,6 +19,7 @@ exports[`getDocusaurusAliases returns appropriate webpack aliases 1`] = `
"@docusaurus/useDocusaurusContext": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useDocusaurusContext.ts", "@docusaurus/useDocusaurusContext": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useDocusaurusContext.ts",
"@docusaurus/useGlobalData": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useGlobalData.ts", "@docusaurus/useGlobalData": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useGlobalData.ts",
"@docusaurus/useIsBrowser": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useIsBrowser.ts", "@docusaurus/useIsBrowser": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useIsBrowser.ts",
"@docusaurus/useIsomorphicLayoutEffect": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useIsomorphicLayoutEffect.tsx",
"@docusaurus/useRouteContext": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useRouteContext.tsx", "@docusaurus/useRouteContext": "<PROJECT_ROOT>/packages/docusaurus/src/client/exports/useRouteContext.tsx",
} }
`; `;

View file

@ -9,6 +9,7 @@ import path from 'path';
import logger from '@docusaurus/logger'; import logger from '@docusaurus/logger';
import merge from 'webpack-merge'; import merge from 'webpack-merge';
import WebpackBar from 'webpackbar'; import WebpackBar from 'webpackbar';
import {DefinePlugin} from 'webpack';
import {createBaseConfig} from './base'; import {createBaseConfig} from './base';
import ChunkAssetPlugin from './plugins/ChunkAssetPlugin'; import ChunkAssetPlugin from './plugins/ChunkAssetPlugin';
import {formatStatsErrorMessage} from './utils'; import {formatStatsErrorMessage} from './utils';
@ -18,6 +19,7 @@ import type {Configuration} from 'webpack';
export default async function createClientConfig( export default async function createClientConfig(
props: Props, props: Props,
minify: boolean = true, minify: boolean = true,
hydrate: boolean = true,
): Promise<Configuration> { ): Promise<Configuration> {
const isBuilding = process.argv[2] === 'build'; const isBuilding = process.argv[2] === 'build';
const config = await createBaseConfig(props, false, minify); const config = await createBaseConfig(props, false, minify);
@ -33,6 +35,9 @@ export default async function createClientConfig(
runtimeChunk: true, runtimeChunk: true,
}, },
plugins: [ plugins: [
new DefinePlugin({
'process.env.HYDRATE_CLIENT_ENTRY': JSON.stringify(hydrate),
}),
new ChunkAssetPlugin(), new ChunkAssetPlugin(),
// Show compilation progress bar and build time. // Show compilation progress bar and build time.
new WebpackBar({ new WebpackBar({

View file

@ -24,9 +24,7 @@ export default `
</head> </head>
<body <%~ it.bodyAttributes %>> <body <%~ it.bodyAttributes %>>
<%~ it.preBodyTags %> <%~ it.preBodyTags %>
<div id="__docusaurus"> <div id="__docusaurus"><%~ it.appHtml %></div>
<%~ it.appHtml %>
</div>
<%~ it.postBodyTags %> <%~ it.postBodyTags %>
</body> </body>
</html> </html>

View file

@ -245,6 +245,7 @@ philpl
photoshop photoshop
picocolors picocolors
picomatch picomatch
Pipeable
playbtn playbtn
pluggable pluggable
plushie plushie

View file

@ -10,7 +10,7 @@ export const DocPropsList = ({items}) => (
<th>Custom Props</th> <th>Custom Props</th>
</tr> </tr>
{items.map((item, index) => ( {items.map((item, index) => (
<tr> <tr key={index}>
<td>{item.label}</td> <td>{item.label}</td>
<td>{JSON.stringify(item.customProps)}</td> <td>{JSON.stringify(item.customProps)}</td>
</tr> </tr>

View file

@ -25,6 +25,7 @@ import Readme from "../README.mdx"
### Other tests ### Other tests
- [React 18](/tests/pages/react-18)
- [Crash test](/tests/pages/crashTest) - [Crash test](/tests/pages/crashTest)
- [Code block tests](/tests/pages/code-block-tests) - [Code block tests](/tests/pages/code-block-tests)
- [Link tests](/tests/pages/link-tests) - [Link tests](/tests/pages/link-tests)

View file

@ -0,0 +1,18 @@
/**
* 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 React from 'react';
export default function HeavyComponent(): JSX.Element {
return (
<div style={{border: 'solid', margin: 10, padding: 10}}>
<button type="button" onClick={() => alert('click')}>
HeavyComponent
</button>
</div>
);
}

View file

@ -0,0 +1,51 @@
/**
* 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 React, {Suspense} from 'react';
import BrowserOnly from '@docusaurus/BrowserOnly';
import Layout from '@theme/Layout';
import Heading from '@theme/Heading';
const HeavyComponentLazy = React.lazy(
// @ts-expect-error: not sure why TS is unhappy about this...
() => import('./_components/heavyComponent'),
);
export default function React18Tests(): JSX.Element {
return (
<Layout>
<main style={{padding: 30}}>
<Heading as="h1">React 18 tests</Heading>
<hr />
<Heading as="h2">{'Suspense > HeavyComponent'}</Heading>
<Suspense fallback="[Suspense fallback] - Suspense > HeavyComponent">
<HeavyComponentLazy />
</Suspense>
<hr />
<Heading as="h2">{'BrowserOnly > Suspense > HeavyComponent'}</Heading>
<BrowserOnly>
{() => (
<Suspense fallback="[Suspense fallback] - BrowserOnly > Suspense > HeavyComponent">
<HeavyComponentLazy />
</Suspense>
)}
</BrowserOnly>
<hr />
<Heading as="h2">{'Suspense > BrowserOnly > HeavyComponent'}</Heading>
<Suspense fallback="[Suspense fallback] - Suspense > BrowserOnly > HeavyComponent">
<BrowserOnly>{() => <HeavyComponentLazy />}</BrowserOnly>
</Suspense>
</main>
</Layout>
);
}

View file

@ -37,6 +37,7 @@ Builds and serves a preview of your site locally with [Webpack Dev Server](https
| Name | Default | Description | | Name | Default | Description |
| --- | --- | --- | | --- | --- | --- |
| `--dev` | | Builds in dev mode, including full React error messages. |
| `--port` | `3000` | Specifies the port of the dev server. | | `--port` | `3000` | Specifies the port of the dev server. |
| `--host` | `localhost` | Specify a host to use. For example, if you want your server to be accessible externally, you can use `--host 0.0.0.0`. | | `--host` | `localhost` | Specify a host to use. For example, if you want your server to be accessible externally, you can use `--host 0.0.0.0`. |
| `--hot-only` | `false` | Enables Hot Module Replacement without page refresh as a fallback in case of build failures. More information [here](https://webpack.js.org/configuration/dev-server/#devserverhotonly). | | `--hot-only` | `false` | Enables Hot Module Replacement without page refresh as a fallback in case of build failures. More information [here](https://webpack.js.org/configuration/dev-server/#devserverhotonly). |

View file

@ -129,6 +129,11 @@ module.exports = async function createConfigAsync() {
syntax: 'typescript', syntax: 'typescript',
tsx: true, tsx: true,
}, },
transform: {
react: {
runtime: 'automatic',
},
},
target: 'es2017', target: 'es2017',
}, },
module: { module: {
@ -258,7 +263,7 @@ module.exports = async function createConfigAsync() {
[ [
'pwa', 'pwa',
{ {
debug: isDeployPreview, // debug: isDeployPreview,
offlineModeActivationStrategies: [ offlineModeActivationStrategies: [
'appInstalled', 'appInstalled',
'standalone', 'standalone',

View file

@ -57,8 +57,8 @@
"netlify-plugin-cache": "^1.0.3", "netlify-plugin-cache": "^1.0.3",
"pure-react-carousel": "^1.30.1", "pure-react-carousel": "^1.30.1",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"react": "^17.0.2", "react": "^18.0.0",
"react-dom": "^17.0.2", "react-dom": "^18.0.0",
"react-lite-youtube-embed": "^2.3.52", "react-lite-youtube-embed": "^2.3.52",
"react-medium-image-zoom": "^5.1.3", "react-medium-image-zoom": "^5.1.3",
"react-popper": "^2.3.0", "react-popper": "^2.3.0",
@ -84,7 +84,7 @@
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/eslint-plugin": "^3.0.0-alpha.0", "@docusaurus/eslint-plugin": "^3.0.0-alpha.0",
"@tsconfig/docusaurus": "^1.0.5", "@tsconfig/docusaurus": "^1.0.7",
"@types/jest": "^29.4.0", "@types/jest": "^29.4.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"rimraf": "^3.0.2" "rimraf": "^3.0.2"

View file

@ -51,17 +51,3 @@ export default function BrowserWindow({
</div> </div>
); );
} }
// Quick and dirty component, to improve later if needed
export function IframeWindow({url}: {url: string}): JSX.Element {
return (
<div style={{padding: 10}}>
<BrowserWindow
url={url}
style={{minWidth: '40vw', maxWidth: 400}}
bodyStyle={{padding: 0}}>
<iframe src={url} title={url} style={{width: '100%', height: 300}} />
</BrowserWindow>
</div>
);
}

View file

@ -47,9 +47,8 @@ export default function ConfigTabs({
} }
</Translate> </Translate>
</p> </p>
<p> <CodeBlock language="js" title="docusaurus.config.js">
<CodeBlock language="js" title="docusaurus.config.js"> {`module.exports = {
{`module.exports = {
presets: [ presets: [
[ [
'@docusaurus/preset-classic', '@docusaurus/preset-classic',
@ -61,8 +60,7 @@ export default function ConfigTabs({
], ],
], ],
};`} };`}
</CodeBlock> </CodeBlock>
</p>
</TabItem> </TabItem>
<TabItem value="plugin" label={translate({message: 'Plugin options'})}> <TabItem value="plugin" label={translate({message: 'Plugin options'})}>
<p> <p>
@ -71,9 +69,8 @@ export default function ConfigTabs({
the plugin: the plugin:
</Translate> </Translate>
</p> </p>
<p> <CodeBlock language="js" title="docusaurus.config.js">
<CodeBlock language="js" title="docusaurus.config.js"> {`module.exports = {
{`module.exports = {
plugins: [ plugins: [
[ [
'${pluginName}', '${pluginName}',
@ -83,8 +80,7 @@ export default function ConfigTabs({
], ],
], ],
};`} };`}
</CodeBlock> </CodeBlock>
</p>
</TabItem> </TabItem>
</Tabs> </Tabs>
); );

View file

@ -15,7 +15,7 @@ export default function CustomDogfoodNavbarItem(props: {
mobile?: boolean; mobile?: boolean;
}): JSX.Element | null { }): JSX.Element | null {
const {pathname} = useLocation(); const {pathname} = useLocation();
const shouldRender = pathname.includes('/tests/'); const shouldRender = pathname === '/tests' || pathname.startsWith('/tests/');
if (!shouldRender) { if (!shouldRender) {
return null; return null;
} }

View file

@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import React from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import LiteYouTubeEmbed from 'react-lite-youtube-embed'; import LiteYouTubeEmbed from 'react-lite-youtube-embed';
import Link from '@docusaurus/Link'; import Link from '@docusaurus/Link';

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import React, {useState, useMemo, useEffect} from 'react'; import {useState, useMemo, useEffect} from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import Translate, {translate} from '@docusaurus/Translate'; import Translate, {translate} from '@docusaurus/Translate';

View file

@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import React from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Link from '@docusaurus/Link'; import Link from '@docusaurus/Link';
import Translate from '@docusaurus/Translate'; import Translate from '@docusaurus/Translate';

View file

@ -2,6 +2,8 @@
// This file is not used in compilation. It is here just for a nice editor experience. // This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@tsconfig/docusaurus/tsconfig.json", "extends": "@tsconfig/docusaurus/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"jsx": "preserve", // TODO add to preset config
"lib": ["DOM", "ESNext"], "lib": ["DOM", "ESNext"],
"baseUrl": ".", "baseUrl": ".",
"resolveJsonModule": true, "resolveJsonModule": true,

View file

@ -1610,11 +1610,6 @@
dependencies: dependencies:
loader-utils "^2.0.0" loader-utils "^2.0.0"
"@endiliey/react-ideal-image@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@endiliey/react-ideal-image/-/react-ideal-image-0.0.11.tgz#dc3803d04e1409cf88efa4bba0f67667807bdf27"
integrity sha512-QxMjt/Gvur/gLxSoCy7VIyGGGrGmDN+VHcXkN3R2ApoWX0EYUE+hMgPHSW/PV6VVebZ1Nd4t2UnGRBDihu16JQ==
"@eslint/eslintrc@^2.0.0": "@eslint/eslintrc@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.0.tgz#943309d8697c52fc82c076e90c1c74fbbe69dbff" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.0.tgz#943309d8697c52fc82c076e90c1c74fbbe69dbff"
@ -2024,7 +2019,7 @@
unist-util-visit "^4.0.0" unist-util-visit "^4.0.0"
vfile "^5.0.0" vfile "^5.0.0"
"@mdx-js/react@^2.1.5": "@mdx-js/react@^2.1.5", "@mdx-js/react@^2.3.0":
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.3.0.tgz#4208bd6d70f0d0831def28ef28c26149b03180b3" resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.3.0.tgz#4208bd6d70f0d0831def28ef28c26149b03180b3"
integrity sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g== integrity sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==
@ -2571,6 +2566,11 @@
dependencies: dependencies:
"@sinonjs/commons" "^2.0.0" "@sinonjs/commons" "^2.0.0"
"@slorber/react-ideal-image@^0.0.12":
version "0.0.12"
resolved "https://registry.yarnpkg.com/@slorber/react-ideal-image/-/react-ideal-image-0.0.12.tgz#5f867f9e10f2d82456568e8fd5bfb7673089c29c"
integrity sha512-u8KiDTEkMA7/KAeA5ywg/P7YG4zuKhWtswfVZDH8R8HXgQsFcHIYU2WaQnGuK/Du7Wdj90I+SdFmajSGFRvoKA==
"@slorber/static-site-generator-webpack-plugin@^4.0.7": "@slorber/static-site-generator-webpack-plugin@^4.0.7":
version "4.0.7" version "4.0.7"
resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3" resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3"
@ -2818,10 +2818,10 @@
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
"@tsconfig/docusaurus@^1.0.5": "@tsconfig/docusaurus@^1.0.5", "@tsconfig/docusaurus@^1.0.7":
version "1.0.6" version "1.0.7"
resolved "https://registry.yarnpkg.com/@tsconfig/docusaurus/-/docusaurus-1.0.6.tgz#7305a7fa590decc0d5968500234e95fd68788978" resolved "https://registry.yarnpkg.com/@tsconfig/docusaurus/-/docusaurus-1.0.7.tgz#a3ee3c8109b3fec091e3d61a61834e563aeee3c3"
integrity sha512-1QxDaP54hpzM6bq9E+yFEo4F9WbWHhsDe4vktZXF/iDlc9FqGr9qlg+3X/nuKQXx8QxHV7ue8NXFazzajsxFBA== integrity sha512-ffTXxGIP/IRMCjuzHd6M4/HdIrw1bMfC7Bv8hMkTadnePkpe0lG0oDSdbRpSDZb2rQMAgpbWiR10BvxvNYwYrg==
"@types/acorn@^4.0.0": "@types/acorn@^4.0.0":
version "4.0.6" version "4.0.6"
@ -13342,14 +13342,13 @@ react-dev-utils@^12.0.1:
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
text-table "^0.2.0" text-table "^0.2.0"
react-dom@^17.0.2: react-dom@^18.0.0:
version "17.0.2" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" scheduler "^0.23.0"
scheduler "^0.20.2"
react-error-boundary@^3.1.0: react-error-boundary@^3.1.0:
version "3.1.4" version "3.1.4"
@ -13379,7 +13378,7 @@ react-helmet-async@*, react-helmet-async@^1.3.0:
react-fast-compare "^3.2.0" react-fast-compare "^3.2.0"
shallowequal "^1.1.0" shallowequal "^1.1.0"
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", "react-is@^17.0.1 || ^18.0.0", react-is@^18.0.0: "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", "react-is@^17.0.1 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0:
version "18.2.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
@ -13389,11 +13388,6 @@ react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-json-view@^1.21.3: react-json-view@^1.21.3:
version "1.21.3" version "1.21.3"
resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475"
@ -13482,7 +13476,7 @@ react-router@5.3.4, react-router@^5.3.4:
tiny-invariant "^1.0.2" tiny-invariant "^1.0.2"
tiny-warning "^1.0.0" tiny-warning "^1.0.0"
react-shallow-renderer@^16.13.1: react-shallow-renderer@^16.15.0:
version "16.15.0" version "16.15.0"
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
@ -13495,15 +13489,14 @@ react-simple-code-editor@^0.10.0:
resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.10.0.tgz#73e7ac550a928069715482aeb33ccba36efe2373" resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.10.0.tgz#73e7ac550a928069715482aeb33ccba36efe2373"
integrity sha512-bL5W5mAxSW6+cLwqqVWY47Silqgy2DKDTR4hDBrLrUqC5BXc29YVx17l2IZk5v36VcDEq1Bszu2oHm1qBwKqBA== integrity sha512-bL5W5mAxSW6+cLwqqVWY47Silqgy2DKDTR4hDBrLrUqC5BXc29YVx17l2IZk5v36VcDEq1Bszu2oHm1qBwKqBA==
react-test-renderer@^17.0.2: react-test-renderer@^18.0.0:
version "17.0.2" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
dependencies: dependencies:
object-assign "^4.1.1" react-is "^18.2.0"
react-is "^17.0.2" react-shallow-renderer "^16.15.0"
react-shallow-renderer "^16.13.1" scheduler "^0.23.0"
scheduler "^0.20.2"
react-textarea-autosize@^8.3.2: react-textarea-autosize@^8.3.2:
version "8.4.0" version "8.4.0"
@ -13524,13 +13517,12 @@ react-waypoint@^10.3.0:
prop-types "^15.0.0" prop-types "^15.0.0"
react-is "^17.0.1 || ^18.0.0" react-is "^17.0.1 || ^18.0.0"
react@^17.0.2: react@^18.0.0:
version "17.0.2" version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1"
read-cmd-shim@3.0.0: read-cmd-shim@3.0.0:
version "3.0.0" version "3.0.0"
@ -14281,13 +14273,12 @@ saxes@^6.0.0:
dependencies: dependencies:
xmlchars "^2.2.0" xmlchars "^2.2.0"
scheduler@^0.20.2: scheduler@^0.23.0:
version "0.20.2" version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@2.7.0: schema-utils@2.7.0:
version "2.7.0" version "2.7.0"