feat(core): use react-helmet-async (#6306)

* Use React Strict Mode

Even though Strict Mode is not required a WARNING icon now displays
on all components that do not use React.StrictMode on React DevTools extension.

Signed-off-by: Shinwon Elizabeth Yoon <24852454+seyoon20087@users.noreply.github.com>

* Utilize react-helmet-async instead of react-helmet

react-helmet is NOT thread safe, as explained in https://open.nytimes.com/the-future-of-meta-tag-management-for-modern-react-development-ec26a7dc9183#fdc2

Therefore, it's better if react-helmet-async is utilized instead of react-helmet.

Even though react-helmet-async is being utilized, most users will not require any code changes to @docusaurus/Head since it uses the same API as react-helmet.

Signed-off-by: Shinwon Elizabeth Yoon <24852454+seyoon20087@users.noreply.github.com>

* Include HelmetProvider inside client entry

I forgot to do this before.

Signed-off-by: Shinwon Elizabeth Yoon <24852454+seyoon20087@users.noreply.github.com>

* format

* fix TS

* address reviews

* Remove forked react-loadable package in favor of @react-loadable/revised

Both unforked react-loadable and @docusaurus/react-loadable uses legacy React APIs.

However, @react-loadable/revised (https://github.com/react-loadable/revised) is actively maintained and widely used in production, thus replaced with this package.

Signed-off-by: Shinwon Elizabeth Yoon <24852454+seyoon20087@users.noreply.github.com>

* remove unused comma

* Address reviews from https://github.com/facebook/docusaurus/pull/6306#pullrequestreview-864745191

Signed-off-by: Shinwon Elizabeth Yoon <24852454+seyoon20087@users.noreply.github.com>

Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
This commit is contained in:
seyoon20087 2022-02-02 15:52:44 +09:00 committed by GitHub
parent 94135ac71a
commit a615ab3999
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 27 deletions

View file

@ -81,7 +81,7 @@
"postcss-loader": "^6.1.1", "postcss-loader": "^6.1.1",
"prompts": "^2.4.1", "prompts": "^2.4.1",
"react-dev-utils": "^12.0.0", "react-dev-utils": "^12.0.0",
"react-helmet": "^6.1.0", "react-helmet-async": "^1.2.2",
"react-loadable": "npm:@docusaurus/react-loadable@5.5.2", "react-loadable": "npm:@docusaurus/react-loadable@5.5.2",
"react-loadable-ssr-addon-v5-slorber": "^1.0.1", "react-loadable-ssr-addon-v5-slorber": "^1.0.1",
"react-router": "^5.2.0", "react-router": "^5.2.0",
@ -111,7 +111,6 @@
"@types/mini-css-extract-plugin": "^2.5.1", "@types/mini-css-extract-plugin": "^2.5.1",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"@types/react-helmet": "^6.0.0",
"@types/react-router-config": "^5.0.1", "@types/react-router-config": "^5.0.1",
"@types/rtl-detect": "^1.0.0", "@types/rtl-detect": "^1.0.0",
"@types/serve-handler": "^6.1.1", "@types/serve-handler": "^6.1.1",

View file

@ -8,6 +8,7 @@
import React from 'react'; import React from 'react';
import {hydrate, render} from 'react-dom'; import {hydrate, render} from 'react-dom';
import {BrowserRouter} from 'react-router-dom'; import {BrowserRouter} from 'react-router-dom';
import {HelmetProvider} from 'react-helmet-async';
import routes from '@generated/routes'; import routes from '@generated/routes';
import ExecutionEnvironment from './exports/ExecutionEnvironment'; import ExecutionEnvironment from './exports/ExecutionEnvironment';
@ -32,9 +33,11 @@ if (ExecutionEnvironment.canUseDOM) {
const renderMethod = process.env.NODE_ENV === 'production' ? hydrate : render; const renderMethod = process.env.NODE_ENV === 'production' ? hydrate : render;
preload(routes, window.location.pathname).then(() => { preload(routes, window.location.pathname).then(() => {
renderMethod( renderMethod(
<BrowserRouter> <HelmetProvider>
<App /> <BrowserRouter>
</BrowserRouter>, <App />
</BrowserRouter>
</HelmetProvider>,
document.getElementById('__docusaurus'), document.getElementById('__docusaurus'),
); );
}); });

View file

@ -6,7 +6,7 @@
*/ */
import React from 'react'; import React from 'react';
import {Helmet} from 'react-helmet'; import {Helmet} from 'react-helmet-async';
import type {HeadProps} from '@docusaurus/Head'; import type {HeadProps} from '@docusaurus/Head';
function Head(props: HeadProps): JSX.Element { function Head(props: HeadProps): JSX.Element {

View file

@ -9,7 +9,7 @@ import * as eta from 'eta';
import React from 'react'; import React from 'react';
import {StaticRouter} from 'react-router-dom'; import {StaticRouter} from 'react-router-dom';
import ReactDOMServer from 'react-dom/server'; import ReactDOMServer from 'react-dom/server';
import {Helmet} from 'react-helmet'; 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';
@ -82,20 +82,23 @@ async function doRender(locals: Locals & {path: string}) {
await preload(routes, location); await preload(routes, location);
const modules = new Set<string>(); const modules = new Set<string>();
const context = {}; const context = {};
const helmetContext = {};
const linksCollector = createStatefulLinksCollector(); const linksCollector = createStatefulLinksCollector();
const appHtml = ReactDOMServer.renderToString( const appHtml = ReactDOMServer.renderToString(
<Loadable.Capture report={(moduleName) => modules.add(moduleName)}> <Loadable.Capture report={(moduleName) => modules.add(moduleName)}>
<StaticRouter location={location} context={context}> <HelmetProvider context={helmetContext}>
<ProvideLinksCollector linksCollector={linksCollector}> <StaticRouter location={location} context={context}>
<App /> <ProvideLinksCollector linksCollector={linksCollector}>
</ProvideLinksCollector> <App />
</StaticRouter> </ProvideLinksCollector>
</StaticRouter>
</HelmetProvider>
</Loadable.Capture>, </Loadable.Capture>,
); );
onLinksCollected(location, linksCollector.getCollectedLinks()); onLinksCollected(location, linksCollector.getCollectedLinks());
const helmet = Helmet.renderStatic(); const {helmet} = helmetContext as FilledContext;
const htmlAttributes = helmet.htmlAttributes.toString(); const htmlAttributes = helmet.htmlAttributes.toString();
const bodyAttributes = helmet.bodyAttributes.toString(); const bodyAttributes = helmet.bodyAttributes.toString();
const metaStrings = [ const metaStrings = [

View file

@ -4112,7 +4112,7 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-helmet@*", "@types/react-helmet@^6.0.0": "@types/react-helmet@*":
version "6.1.5" version "6.1.5"
resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.5.tgz#35f89a6b1646ee2bc342a33a9a6c8777933f9083" resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.5.tgz#35f89a6b1646ee2bc342a33a9a6c8777933f9083"
integrity sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA== integrity sha512-/ICuy7OHZxR0YCAZLNg9r7I9aijWUWvxaPR6uTuyxe8tAj5RL4Sw1+R6NhXUtOsarkGYPmaHdBDvuXh2DIN/uA==
@ -10497,6 +10497,13 @@ into-stream@^3.1.0:
from2 "^2.1.1" from2 "^2.1.1"
p-is-promise "^1.1.0" p-is-promise "^1.1.0"
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
dependencies:
loose-envify "^1.0.0"
ip@^1.1.0, ip@^1.1.5: ip@^1.1.0, ip@^1.1.5:
version "1.1.5" version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
@ -15358,20 +15365,21 @@ react-error-overlay@^6.0.10:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.10.tgz#0fe26db4fa85d9dbb8624729580e90e7159a59a6"
integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA== integrity sha512-mKR90fX7Pm5seCOfz8q9F+66VCc1PGsWSBxKbITjfKVQHMNF2zudxHnMdJiB1fRCb+XsbQV9sO9DCkgsMQgBIA==
react-fast-compare@^3.0.1, react-fast-compare@^3.1.1: react-fast-compare@^3.0.1, react-fast-compare@^3.2.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
react-helmet@^6.1.0: react-helmet-async@^1.2.2:
version "6.1.0" version "1.2.2"
resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.2.2.tgz#38d58d32ebffbc01ba42b5ad9142f85722492389"
integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== integrity sha512-XgSQezeCbLfCxdZhDA3T/g27XZKnOYyOkruopTLSJj8RvFZwdXnM4djnfYaiBSDzOidDgTo1jcEozoRu/+P9UQ==
dependencies: dependencies:
object-assign "^4.1.1" "@babel/runtime" "^7.12.5"
invariant "^2.2.4"
prop-types "^15.7.2" prop-types "^15.7.2"
react-fast-compare "^3.1.1" react-fast-compare "^3.2.0"
react-side-effect "^2.1.0" shallowequal "^1.1.0"
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
version "16.13.1" version "16.13.1"
@ -15479,11 +15487,6 @@ react-shallow-renderer@^16.13.1:
object-assign "^4.1.1" object-assign "^4.1.1"
react-is "^16.12.0 || ^17.0.0" react-is "^16.12.0 || ^17.0.0"
react-side-effect@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3"
integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ==
react-simple-code-editor@^0.10.0: react-simple-code-editor@^0.10.0:
version "0.10.0" version "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"
@ -16558,6 +16561,11 @@ shallow-clone@^3.0.0:
dependencies: dependencies:
kind-of "^6.0.2" kind-of "^6.0.2"
shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
sharp@^0.29.1: sharp@^0.29.1:
version "0.29.3" version "0.29.3"
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2"