diff --git a/gatsby-config.js b/gatsby-config.js index d2031822..5cfd4ad9 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -74,7 +74,9 @@ module.exports = { ], }, }, - `gatsby-plugin-react-helmet-async`, + { + resolve: `./gatsby/plugin/helmet-async`, + }, { resolve: `gatsby-source-filesystem`, options: { diff --git a/gatsby/plugin/helmet-async/gatsby-browser.tsx b/gatsby/plugin/helmet-async/gatsby-browser.tsx new file mode 100644 index 00000000..6f9c2e5b --- /dev/null +++ b/gatsby/plugin/helmet-async/gatsby-browser.tsx @@ -0,0 +1,9 @@ +import { GatsbyBrowser, WrapRootElementBrowserArgs } from "gatsby"; +import React from "react"; +import { HelmetProvider } from "react-helmet-async"; + +export const wrapRootElement: GatsbyBrowser["wrapRootElement"] = ({ + element, +}: WrapRootElementBrowserArgs): React.ReactElement => ( + {element} +); diff --git a/gatsby/plugin/helmet-async/gatsby-ssr.tsx b/gatsby/plugin/helmet-async/gatsby-ssr.tsx new file mode 100644 index 00000000..eaf71852 --- /dev/null +++ b/gatsby/plugin/helmet-async/gatsby-ssr.tsx @@ -0,0 +1,55 @@ +import { + GatsbySSR, + PreRenderHTMLArgs, + RenderBodyArgs, + WrapRootElementNodeArgs, +} from "gatsby"; +import React from "react"; +import { HelmetProvider, HelmetServerState } from "react-helmet-async"; + +const context: { + [pathname: string]: { helmet?: HelmetServerState } | undefined, +} = {}; + +export const onRenderBody: GatsbySSR["onRenderBody"] = ({ + pathname, + setHeadComponents, + setHtmlAttributes, + setBodyAttributes, +}: RenderBodyArgs): void => { + const { helmet } = context[pathname] ?? {}; + + if (helmet) { + const baseComponent = helmet.base.toComponent(); + const titleComponent = helmet.title.toComponent(); + const components = [ + helmet.priority.toComponent(), + helmet.meta.toComponent(), + helmet.link.toComponent(), + helmet.style.toComponent(), + helmet.script.toComponent(), + helmet.noscript.toComponent(), + ]; + + setHeadComponents( + titleComponent[0]?.props.children + ? [baseComponent, titleComponent, ...components] + : [baseComponent, ...components] + ); + + setHtmlAttributes(helmet.htmlAttributes.toComponent()); + setBodyAttributes(helmet.bodyAttributes.toComponent()); + } +}; + +export const onPreRenderHTML = ({ pathname }: PreRenderHTMLArgs) => { + context[pathname] = undefined; +}; + +export const wrapRootElement: GatsbySSR["wrapRootElement"] = ({ + pathname, + element, +}: WrapRootElementNodeArgs): React.ReactElement => { + context[pathname] = {}; + return {element}; +}; diff --git a/gatsby/plugin/helmet-async/package.json b/gatsby/plugin/helmet-async/package.json new file mode 100644 index 00000000..ff66a6a8 --- /dev/null +++ b/gatsby/plugin/helmet-async/package.json @@ -0,0 +1,54 @@ +{ + "name": "gatsby-plugin-react-helmet-async", + "version": "0.0.1", + "description": "Use react-helmet-async with Gatsby", + "keywords": [ + "gatsby", + "gatsby-plugin", + "react", + "react-helmet", + "react-helmet-async" + ], + "license": "Apache-2.0", + "main": "index.js", + "scripts": { + "prepublish": "yarn build", + "build": "tsc --build", + "clean": "rm gatsby-browser.d.ts gatsby-browser.js gatsby-ssr.d.ts gatsby-ssr.js", + "lint": "eslint src --ext ts,tsx --fix", + "test": "echo \"Error: no tests specified\" && exit 1" + }, + "devDependencies": { + "@types/react": "^17", + "@types/react-dom": "^17", + "@typescript-eslint/eslint-plugin": "^5.42.1", + "@typescript-eslint/parser": "^5.42.1", + "eslint": "^8.27.0", + "eslint-config-prettier": "^8.5.0", + "eslint-config-standard-react": "^12.0.0", + "eslint-config-standard-with-typescript": "^23.0.0", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.31.10", + "gatsby": "^3.8.0", + "prettier": "^2.7.1", + "prettier-eslint": "^15.0.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-helmet-async": "^1.3.0", + "typescript": "^4.8.4" + }, + "peerDependencies": { + "gatsby": ">=2", + "react": "^16.6.0 || ^17 || ^18", + "react-dom": "^16.6.0 || ^17 || ^18", + "react-helmet-async": "1.x" + }, + "resolutions": { + "@types/react": "^17", + "@types/react-dom": "^17" + } +} diff --git a/package.json b/package.json index eee7241d..3a15ac59 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "gatsby-plugin-meta-redirect": "^1.1.1", "gatsby-plugin-postcss": "^5.21.0", "gatsby-plugin-purgecss": "^6.1.0", - "gatsby-plugin-react-helmet-async": "^1.2.1", "gatsby-plugin-react-i18next": "^1.2.2", "gatsby-plugin-react-svg": "^3.3.0", "gatsby-plugin-remove-serviceworker": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index ad674ff2..7dc8c967 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7352,11 +7352,6 @@ gatsby-plugin-purgecss@^6.1.0: merge-anything "^5.0.2" purgecss "^4.1.3" -gatsby-plugin-react-helmet-async@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/gatsby-plugin-react-helmet-async/-/gatsby-plugin-react-helmet-async-1.2.3.tgz#28d38b1cfc80edb140abbdb43e93258b0604ce57" - integrity sha512-fA/FGZbJlZuqeZvaGc3intLWfsPclhY2EAtdHqarwde8D8RlanVn2nVUxrvLEhzwq8n4OpTfj/DChAVmr0D+yA== - gatsby-plugin-react-i18next@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/gatsby-plugin-react-i18next/-/gatsby-plugin-react-i18next-1.2.3.tgz#ae13bc1c6189e0179913fce83ef537a193cfd1f4" @@ -11770,15 +11765,20 @@ react-error-overlay@^6.0.11: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== -react-fast-compare@^3.1.1, react-fast-compare@^3.2.0: +react-fast-compare@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== +react-fast-compare@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== + react-helmet-async@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.2.3.tgz#57326a69304ea3293036eafb49475e9ba454cb37" - integrity sha512-mCk2silF53Tq/YaYdkl2sB+/tDoPnaxN7dFS/6ZLJb/rhUY2EWGI5Xj2b4jHppScMqY45MbgPSwTxDchKpZ5Kw== + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" + integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== dependencies: "@babel/runtime" "^7.12.5" invariant "^2.2.4"