From dc43f7fea5fb9d819b3dca93503190026292950a Mon Sep 17 00:00:00 2001 From: wraeth-eth <104132113+wraeth-eth@users.noreply.github.com> Date: Fri, 1 Sep 2023 09:06:26 +1000 Subject: [PATCH] unblock lingui and allow for SSR on almost all pages (#3999) ## What does this PR do and why? The big kahuna - this breaks lingui out into SSR and will not block for client side compilation/rendering ## Screenshots or screen recordings _If applicable, provide screenshots or screen recordings to demonstrate the changes._ ## Acceptance checklist - [ ] I have evaluated the [Approval Guidelines](https://github.com/jbx-protocol/juice-interface/blob/main/CONTRIBUTING.md#approval-guidelines) for this PR. - [ ] (if relevant) I have tested this PR in [all supported browsers](https://github.com/jbx-protocol/juice-interface/blob/main/CONTRIBUTING.md#supported-browsers). - [ ] (if relevant) I have tested this PR in dark mode and light mode (if applicable). --- .linguirc.json | 19 ---- lingui.config.js | 25 +++++ next.config.js | 19 +++- package.json | 2 + src/components/ErrorNotificationButtons.tsx | 2 +- .../Navbar/components/DropdownMenu.tsx | 2 + .../Navbar/components/NavLanguageSelector.tsx | 29 +++--- .../components/SummaryCollapsedView.test.tsx | 2 + .../ProjectDashboard/utils/modals.tsx | 2 +- .../common/CoreAppWrapper/CoreAppWrapper.tsx | 32 +++--- src/contexts/Language/LanguageProvider.tsx | 73 +++++--------- .../Language/__mocks__/LanguageProvider.tsx | 7 ++ src/hooks/useLinguiInit.ts | 26 +++++ src/locales/utils.ts | 5 + src/pages/_app.tsx | 11 ++- src/pages/about/index.tsx | 3 + src/pages/account/[addressOrEnsName]/edit.tsx | 10 ++ .../account/[addressOrEnsName]/index.tsx | 40 ++++++-- src/pages/activity/index.tsx | 3 + src/pages/contact/index.tsx | 3 + src/pages/create/index.tsx | 3 + src/pages/experimental/flags/index.tsx | 3 + src/pages/index.tsx | 3 + src/pages/legal/index.tsx | 3 + src/pages/p/[handle]/index.tsx | 16 ++- src/pages/p/[handle]/safe/index.tsx | 9 +- src/pages/privacy.tsx | 3 + src/pages/projects/index.tsx | 3 + src/pages/success-stories/constitutiondao.tsx | 3 + src/pages/success-stories/moondao.tsx | 3 + src/pages/success-stories/sharkdao.tsx | 3 + src/pages/success-stories/studiodao.tsx | 3 + .../v2/p/[projectId]/contracts/index.tsx | 3 + src/pages/v2/p/[projectId]/index.tsx | 54 ++++++++--- src/pages/v2/p/[projectId]/safe/index.tsx | 9 +- .../p/[projectId]/settings/[settingsPage].tsx | 3 + src/pages/v2/p/[projectId]/settings/index.tsx | 3 + .../next-server/globalGetServerSideProps.ts | 23 +++++ src/utils/projectPageLoaders.ts | 4 +- yarn.lock | 97 +++++++++++++++++++ 40 files changed, 424 insertions(+), 142 deletions(-) delete mode 100644 .linguirc.json create mode 100644 lingui.config.js create mode 100644 src/contexts/Language/__mocks__/LanguageProvider.tsx create mode 100644 src/hooks/useLinguiInit.ts create mode 100644 src/locales/utils.ts create mode 100644 src/utils/next-server/globalGetServerSideProps.ts diff --git a/.linguirc.json b/.linguirc.json deleted file mode 100644 index ee7f81e809..0000000000 --- a/.linguirc.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "catalogs": [ - { - "path": "src/locales/{locale}/messages", - "include": ["src"] - } - ], - "format": "po", - "formatOptions": { - "lineNumbers": false, - "origins": false - }, - "orderBy": "messageId", - "fallbackLocales": { - "default": "en" - }, - "locales": ["en", "zh"], - "sourceLocale": "en" -} diff --git a/lingui.config.js b/lingui.config.js new file mode 100644 index 0000000000..1fc467eeb6 --- /dev/null +++ b/lingui.config.js @@ -0,0 +1,25 @@ +const { formatter } = require('@lingui/format-po') + +const locales = ['en', 'zh'] + +if (process.env.NODE_ENV !== 'production') { + locales.push('pseudo') +} + +/** @type {import('@lingui/conf').LinguiConfig} */ +module.exports = { + locales: locales, + sourceLocale: 'en', + pseudoLocale: 'pseudo', + catalogs: [ + { + path: 'src/locales/{locale}/messages', + include: ['src'], + }, + ], + format: formatter({ origins: false, lineNumbers: false }), + orderBy: 'messageId', + fallbackLocales: { + default: 'en', + }, +} diff --git a/next.config.js b/next.config.js index 7e027d9b55..689511d3e5 100644 --- a/next.config.js +++ b/next.config.js @@ -4,6 +4,7 @@ // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ const { withSentryConfig } = require('@sentry/nextjs') const withBundleAnalyzer = require('@next/bundle-analyzer') +const linguiConfig = require('./lingui.config') const webpack = require('webpack') @@ -112,8 +113,19 @@ const SECURITY_HEADERS = [ }, // NOTE: gnosis safe is still allowed due to frame-ancestors definition ] +/** @type {import('next').NextConfig} */ const nextConfig = removeImports({ - experimental: { esmExternals: true }, + experimental: { + esmExternals: true, + swcPlugins: [ + '@lingui/swc-plugin', + { + runtimeModules: { + i18n: ['@lingui/core', 'i18n'], + }, + }, + ], + }, staticPageGenerationTimeout: 90, webpack: config => { config.resolve.fallback = { fs: false, module: false } @@ -126,6 +138,11 @@ const nextConfig = removeImports({ return config }, + i18n: { + localeDetection: false, + locales: linguiConfig.locales, + defaultLocale: linguiConfig.sourceLocale, + }, async redirects() { return [ { diff --git a/package.json b/package.json index aa493e9944..8f9015ff1f 100644 --- a/package.json +++ b/package.json @@ -162,6 +162,8 @@ "@graphql-codegen/typescript-operations": "^3.0.3", "@graphql-codegen/typescript-react-apollo": "^3.3.2", "@graphql-codegen/typescript-resolvers": "^3.2.0", + "@lingui/loader": "^4.4.0", + "@lingui/swc-plugin": "4.0.4", "@next/bundle-analyzer": "^13.2.4", "@testing-library/cypress": "^8.0.2", "@testing-library/jest-dom": "^5.16.5", diff --git a/src/components/ErrorNotificationButtons.tsx b/src/components/ErrorNotificationButtons.tsx index 32d4c2881c..7142d046c3 100644 --- a/src/components/ErrorNotificationButtons.tsx +++ b/src/components/ErrorNotificationButtons.tsx @@ -1,7 +1,7 @@ import { WarningOutlined } from '@ant-design/icons' import { Trans } from '@lingui/macro' import { Button } from 'antd' -import LanguageProvider from 'contexts/Language/LanguageProvider' +import { LanguageProvider } from 'contexts/Language/LanguageProvider' import { helpPagePath } from 'utils/routes' import ExternalLink from './ExternalLink' diff --git a/src/components/Navbar/components/DropdownMenu.tsx b/src/components/Navbar/components/DropdownMenu.tsx index 63cc234e35..04862eae4d 100644 --- a/src/components/Navbar/components/DropdownMenu.tsx +++ b/src/components/Navbar/components/DropdownMenu.tsx @@ -15,6 +15,7 @@ type LinkItem = { id: string label: ReactNode href: string + locale?: string isExternal?: boolean } @@ -130,6 +131,7 @@ export const DropdownMenu = ({ {item.label} diff --git a/src/components/Navbar/components/NavLanguageSelector.tsx b/src/components/Navbar/components/NavLanguageSelector.tsx index 0e08d88b36..4af481b0a6 100644 --- a/src/components/Navbar/components/NavLanguageSelector.tsx +++ b/src/components/Navbar/components/NavLanguageSelector.tsx @@ -1,9 +1,8 @@ import { LanguageIcon } from '@heroicons/react/24/solid' -import { Trans } from '@lingui/macro' +import { useLingui } from '@lingui/react' import { SUPPORTED_LANGUAGES } from 'constants/locale' -import { useCallback, useMemo } from 'react' +import { useRouter } from 'next/router' import { twMerge } from 'tailwind-merge' -import { reloadWindow } from 'utils/windowUtils' import { DropdownMenu } from './DropdownMenu' // Language select tool seen in top nav @@ -12,16 +11,10 @@ export default function NavLanguageSelector({ }: { className?: string }) { - // Sets the new language with localStorage and reloads the page - const setLanguage = useCallback((newLanguage: string) => { - localStorage.setItem('lang', newLanguage) - reloadWindow() - }, []) - - const currentLanguage = useMemo( - () => localStorage.getItem('lang') ?? 'en', - [], - ) + const router = useRouter() + const { + i18n: { locale }, + } = useLingui() return ( - {SUPPORTED_LANGUAGES[currentLanguage].short} + {SUPPORTED_LANGUAGES[locale].short} } items={Object.values(SUPPORTED_LANGUAGES).map(lang => ({ id: lang.code, label: lang.long, - onClick: () => { - setLanguage(lang.code) + // TODO: We want to use the bottom but due to a bug in t macros we cant + // locale: lang.code, + // href: pathname, + onClick: async () => { + await router.push(router.asPath, router.asPath, { locale: lang.code }) + router.reload() }, }))} /> diff --git a/src/components/ProjectDashboard/components/Cart/components/SummaryCollapsedView.test.tsx b/src/components/ProjectDashboard/components/Cart/components/SummaryCollapsedView.test.tsx index 02a6261611..8012cd6c03 100644 --- a/src/components/ProjectDashboard/components/Cart/components/SummaryCollapsedView.test.tsx +++ b/src/components/ProjectDashboard/components/Cart/components/SummaryCollapsedView.test.tsx @@ -7,6 +7,8 @@ import { V2V3_CURRENCY_ETH } from 'utils/v2v3/currency' import { useCartSummary } from '../hooks/useCartSummary' import { SummaryCollapsedView } from './SummaryCollapsedView' +jest.mock('contexts/Language/LanguageProvider') + jest.mock('use-resize-observer', () => ({ __esModule: true, default: jest.fn(() => ({ diff --git a/src/components/ProjectDashboard/utils/modals.tsx b/src/components/ProjectDashboard/utils/modals.tsx index 3e82146cbd..0dbd071bc8 100644 --- a/src/components/ProjectDashboard/utils/modals.tsx +++ b/src/components/ProjectDashboard/utils/modals.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro' import { ModalOnCancelFn, ModalOnOkFn } from 'components/modals/JuiceModal' -import LanguageProvider from 'contexts/Language/LanguageProvider' +import { LanguageProvider } from 'contexts/Language/LanguageProvider' import { ReactNode } from 'react' import { createRoot } from 'react-dom/client' import { ConfirmationDeletionModal } from '../components/ui/ConfirmationDeletionModal' diff --git a/src/components/common/CoreAppWrapper/CoreAppWrapper.tsx b/src/components/common/CoreAppWrapper/CoreAppWrapper.tsx index 4c9aca9f51..1e37757cea 100644 --- a/src/components/common/CoreAppWrapper/CoreAppWrapper.tsx +++ b/src/components/common/CoreAppWrapper/CoreAppWrapper.tsx @@ -8,19 +8,11 @@ import ReactQueryProvider from 'contexts/ReactQueryProvider' import { ThemeProvider } from 'contexts/Theme/ThemeProvider' import TxHistoryProvider from 'contexts/Transaction/TxHistoryProvider' import { installJuiceboxWindowObject } from 'lib/juicebox' -import dynamic from 'next/dynamic' import { useRouter } from 'next/router' import React, { useEffect } from 'react' import { twJoin } from 'tailwind-merge' import { redirectTo } from 'utils/windowUtils' -const LanguageProvider = dynamic( - () => import('contexts/Language/LanguageProvider'), - { - ssr: false, - }, -) - /** * Contains all the core app providers used by each page. * @@ -35,19 +27,17 @@ export const AppWrapper: React.FC< return ( - - - - - - - <_Wrapper hideNav={hideNav}>{children} - - - - - - + + + + + + <_Wrapper hideNav={hideNav}>{children} + + + + + ) diff --git a/src/contexts/Language/LanguageProvider.tsx b/src/contexts/Language/LanguageProvider.tsx index 98f14fea92..15699b9efb 100644 --- a/src/contexts/Language/LanguageProvider.tsx +++ b/src/contexts/Language/LanguageProvider.tsx @@ -1,60 +1,33 @@ -import { i18n } from '@lingui/core' -import { - detect, - fromNavigator, - fromStorage, - fromUrl, -} from '@lingui/detect-locale' +import { Messages } from '@lingui/core' import { I18nProvider } from '@lingui/react' -import defaultLocale from 'locales/en/messages' -import { ReactNode } from 'react' +import { useLingUiInit } from 'hooks/useLinguiInit' +import React from 'react' -import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from 'constants/locale' - -const getLocale = (): string => { - if (typeof window === 'undefined') return DEFAULT_LOCALE - - let locale = - detect(fromUrl('lang'), fromStorage('lang'), fromNavigator()) ?? - DEFAULT_LOCALE - - if (!SUPPORTED_LOCALES.includes(locale)) { - locale = DEFAULT_LOCALE - } - - return locale -} - -const activateDefaultLocale = () => { - const { messages } = defaultLocale - i18n.load(DEFAULT_LOCALE, messages) - i18n.activate(DEFAULT_LOCALE) +export type I18nProviderProps = { + children: React.ReactNode + i18n?: { messages: Messages; locale: string } } -const dynamicActivate = async (locale: string) => { - try { - const { messages } = await import(`../../locales/${locale}/messages`) +let i18nSingleton: { messages: Messages; locale: string } | undefined - i18n.load(locale, messages) - i18n.activate(locale) - } catch (e) { - console.error(`Error loading locale "${locale}:"`, e) - // fall back to default locale - activateDefaultLocale() +export const LanguageProvider: React.FC = ({ + children, + i18n: _i18n, +}) => { + if (_i18n) { + i18nSingleton = _i18n + } else { + _i18n = i18nSingleton } -} -const locale = getLocale() -if (locale === DEFAULT_LOCALE) { - activateDefaultLocale() -} else { - dynamicActivate(locale) -} + if (!_i18n) + throw new Error( + 'i18n must be provided at least once. This is usually done in _app.tsx', + ) + + const messages = _i18n?.messages ?? [] + const locale = _i18n?.locale ?? 'en' + const i18n = useLingUiInit(messages, locale) -export default function LanguageProvider({ - children, -}: { - children: ReactNode -}) { return {children} } diff --git a/src/contexts/Language/__mocks__/LanguageProvider.tsx b/src/contexts/Language/__mocks__/LanguageProvider.tsx new file mode 100644 index 0000000000..9788b09780 --- /dev/null +++ b/src/contexts/Language/__mocks__/LanguageProvider.tsx @@ -0,0 +1,7 @@ +// generate mock for LanguageProvider + +import { I18nProviderProps } from '../LanguageProvider' + +export const LanguageProvider: React.FC = ({ children }) => { + return
{children}
+} diff --git a/src/hooks/useLinguiInit.ts b/src/hooks/useLinguiInit.ts new file mode 100644 index 0000000000..507f31f753 --- /dev/null +++ b/src/hooks/useLinguiInit.ts @@ -0,0 +1,26 @@ +// https://github.com/lingui/js-lingui/pull/1541 + +import { i18n, Messages } from '@lingui/core' +import { useEffect } from 'react' + +export const useLingUiInit = (messages: Messages, locale: string) => { + const isClient = typeof window !== 'undefined' + + if (!isClient && locale !== i18n.locale) { + // there is single instance of i18n on the server + i18n.loadAndActivate({ locale, messages }) + } + if (isClient && i18n.locale === undefined) { + // first client render + i18n.loadAndActivate({ locale, messages }) + } + + useEffect(() => { + const localeDidChange = locale !== i18n.locale + if (localeDidChange) { + i18n.loadAndActivate({ locale, messages }) + } + }, [locale, messages]) + + return i18n +} diff --git a/src/locales/utils.ts b/src/locales/utils.ts new file mode 100644 index 0000000000..8c7e998b14 --- /dev/null +++ b/src/locales/utils.ts @@ -0,0 +1,5 @@ +export async function loadCatalog(locale: string) { + const { messages } = await import(`@lingui/loader!./${locale}/messages.po`) + + return messages +} diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 67de047a0a..aa442530eb 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,4 +1,5 @@ import { Head } from 'components/common' +import { LanguageProvider } from 'contexts/Language/LanguageProvider' import SupabaseSessionProvider from 'contexts/SupabaseSession/SupabaseSessionProvider' import { initWeb3Onboard, useInitWallet } from 'hooks/Wallet' import type { AppProps } from 'next/app' @@ -15,13 +16,19 @@ export default function MyApp({ Component, pageProps }: AppProps) { // Currently, init() must be called *here* (as opposed to AppWrapper), or else it breaks when navigating between pages. useInitWallet() + if (!pageProps.i18n) { + console.error( + 'Missing i18n prop - please ensure that page has globalGetServerSideProps', + ) + } + return ( - <> + {/* Default HEAD - overwritten by specific page SEO */} - + ) } diff --git a/src/pages/about/index.tsx b/src/pages/about/index.tsx index 647ec3fade..1bac5b0ffb 100644 --- a/src/pages/about/index.tsx +++ b/src/pages/about/index.tsx @@ -1,5 +1,6 @@ import { AboutDashboard } from 'components/AboutDashboard' import { AppWrapper, Head } from 'components/common' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function AboutPage() { return ( @@ -17,3 +18,5 @@ export default function AboutPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/account/[addressOrEnsName]/edit.tsx b/src/pages/account/[addressOrEnsName]/edit.tsx index f8103df9ae..861e0c0fac 100644 --- a/src/pages/account/[addressOrEnsName]/edit.tsx +++ b/src/pages/account/[addressOrEnsName]/edit.tsx @@ -9,6 +9,7 @@ import { User } from 'models/database' import { GetServerSideProps, InferGetServerSidePropsType } from 'next' import { Database } from 'types/database.types' import { isEqualAddress } from 'utils/address' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' type AccountSettingsType = { initialSession: Session @@ -18,6 +19,8 @@ type AccountSettingsType = { export const getServerSideProps: GetServerSideProps< AccountSettingsType > = async context => { + const global = await globalGetServerSideProps(context) + const supabase = createServerSupabaseClient(context) const { data: { session }, @@ -28,6 +31,7 @@ export const getServerSideProps: GetServerSideProps< typeof context.params.addressOrEnsName !== 'string' ) { return { + ...global, redirect: { destination: '/', permanent: false, @@ -39,6 +43,7 @@ export const getServerSideProps: GetServerSideProps< ) if (!pair) { return { + ...global, redirect: { destination: '/', permanent: false, @@ -56,6 +61,7 @@ export const getServerSideProps: GetServerSideProps< if (!user.data) { return { + ...global, redirect: { destination: '/', permanent: false, @@ -70,6 +76,7 @@ export const getServerSideProps: GetServerSideProps< console.info('Error occurred on signout', e) } return { + ...global, redirect: { destination: '/', permanent: false, @@ -78,7 +85,10 @@ export const getServerSideProps: GetServerSideProps< } return { + ...global, props: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...((global as any).props || {}), initialSession: session, user: user.data, }, diff --git a/src/pages/account/[addressOrEnsName]/index.tsx b/src/pages/account/[addressOrEnsName]/index.tsx index df9e42db3b..704c065ce5 100644 --- a/src/pages/account/[addressOrEnsName]/index.tsx +++ b/src/pages/account/[addressOrEnsName]/index.tsx @@ -4,8 +4,10 @@ import Loading from 'components/Loading' import { AppWrapper, SEO } from 'components/common' import { isAddress } from 'ethers/lib/utils' import { resolveAddress } from 'lib/api/ens' +import { loadCatalog } from 'locales/utils' import { Profile } from 'models/database' -import { useRouter } from 'next/router' +import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next' +import { useMemo } from 'react' import { useQuery } from 'react-query' import { truncateEthAddress } from 'utils/format/formatAddress' @@ -47,14 +49,17 @@ function _AccountPage({ addressOrEnsName }: { addressOrEnsName: string }) { ) } -export default function AccountPage() { - const router = useRouter() - const { addressOrEnsName } = router.query as { addressOrEnsName: string } +export default function AccountPage({ + addressOrEnsName, +}: InferGetStaticPropsType) { + const addressFound = useMemo( + () => isAddress(addressOrEnsName) || addressOrEnsName.endsWith('eth'), + [addressOrEnsName], + ) return ( - {addressOrEnsName && - (isAddress(addressOrEnsName) || addressOrEnsName.endsWith('eth')) ? ( + {addressOrEnsName && addressFound ? ( <_AccountPage addressOrEnsName={addressOrEnsName as string} /> ) : (
Not found
@@ -62,3 +67,26 @@ export default function AccountPage() {
) } + +export const getStaticPaths: GetStaticPaths = async () => { + return { + paths: [], + fallback: 'blocking', + } +} + +export const getStaticProps: GetStaticProps<{ + addressOrEnsName: string +}> = async context => { + const locale = context.locale as string + const messages = await loadCatalog(locale) + const i18n = { locale, messages } + + const { addressOrEnsName } = context.params as { addressOrEnsName: string } + return { + props: { + addressOrEnsName, + i18n, + }, + } +} diff --git a/src/pages/activity/index.tsx b/src/pages/activity/index.tsx index 69e9341364..7fcf572e90 100644 --- a/src/pages/activity/index.tsx +++ b/src/pages/activity/index.tsx @@ -1,6 +1,7 @@ import { Trans } from '@lingui/macro' import { PaymentsFeed } from 'components/Activity' import { AppWrapper } from 'components/common' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function ActivityPage() { return ( @@ -18,3 +19,5 @@ export default function ActivityPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/contact/index.tsx b/src/pages/contact/index.tsx index ea273d80ec..3c3abdc96f 100644 --- a/src/pages/contact/index.tsx +++ b/src/pages/contact/index.tsx @@ -1,6 +1,7 @@ import { Contact } from 'components/Contact' import { Footer } from 'components/Footer' import { AppWrapper, Head } from 'components/common' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function ContactPage() { return ( @@ -18,3 +19,5 @@ export default function ContactPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/create/index.tsx b/src/pages/create/index.tsx index 8909587c19..bcec01e98a 100644 --- a/src/pages/create/index.tsx +++ b/src/pages/create/index.tsx @@ -6,6 +6,7 @@ import { V2V3ContractsProvider } from 'contexts/v2v3/Contracts/V2V3ContractsProv import { V2V3CurrencyProvider } from 'contexts/v2v3/V2V3CurrencyProvider' import { Provider } from 'react-redux' import store from 'redux/store' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function V2CreatePage() { return ( @@ -31,3 +32,5 @@ export default function V2CreatePage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/experimental/flags/index.tsx b/src/pages/experimental/flags/index.tsx index 9c3ef53cc0..e705e5f42f 100644 --- a/src/pages/experimental/flags/index.tsx +++ b/src/pages/experimental/flags/index.tsx @@ -5,6 +5,7 @@ import { readNetwork } from 'constants/networks' import Head from 'next/head' import { useCallback, useState } from 'react' import { featureFlagEnabled, setFeatureFlag } from 'utils/featureFlags' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function FlagsPage() { const [, updateState] = useState({}) @@ -49,3 +50,5 @@ export default function FlagsPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/index.tsx b/src/pages/index.tsx index ab7e4bc2ab..95c09e3c90 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,6 +1,7 @@ import { AppWrapper } from 'components/common' import { HomePage } from 'components/Home' import { AnnouncementsProvider } from 'contexts/Announcements/AnnouncementsProvider' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function LandingPage() { return ( @@ -11,3 +12,5 @@ export default function LandingPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/legal/index.tsx b/src/pages/legal/index.tsx index 6dad968fc5..c5ba3dbc8c 100644 --- a/src/pages/legal/index.tsx +++ b/src/pages/legal/index.tsx @@ -1,6 +1,7 @@ import { Footer } from 'components/Footer/Footer' import { Legal } from 'components/Legal' import { AppWrapper, Head } from 'components/common' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function LegalPage() { return ( @@ -17,3 +18,5 @@ export default function LegalPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/p/[handle]/index.tsx b/src/pages/p/[handle]/index.tsx index 070883b16c..c3c50dcf56 100644 --- a/src/pages/p/[handle]/index.tsx +++ b/src/pages/p/[handle]/index.tsx @@ -8,6 +8,7 @@ import { V1ProjectProvider } from 'contexts/v1/Project/V1ProjectProvider' import { V1UserProvider } from 'contexts/v1/User/V1UserProvider' import { V1CurrencyProvider } from 'contexts/v1/V1CurrencyProvider' import { V1ProjectMetadataProvider } from 'contexts/v1/V1ProjectMetadataProvider' +import { loadCatalog } from 'locales/utils' import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next' import { useRouter } from 'next/router' import { useContext } from 'react' @@ -25,7 +26,20 @@ export const getStaticPaths: GetStaticPaths = async context => { } export const getStaticProps: GetStaticProps = async context => { - return getV1StaticProps(context) + const locale = context.locale as string + const messages = await loadCatalog(locale) + const i18n = { locale, messages } + + const v1Props = await getV1StaticProps(context) + + return { + ...v1Props, + props: { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(v1Props as any).props, + i18n, + }, + } } export default function V1HandlePage({ diff --git a/src/pages/p/[handle]/safe/index.tsx b/src/pages/p/[handle]/safe/index.tsx index a82e4a734a..330ba3ab1d 100644 --- a/src/pages/p/[handle]/safe/index.tsx +++ b/src/pages/p/[handle]/safe/index.tsx @@ -1,11 +1,12 @@ -import { AppWrapper } from 'components/common' import { ProjectSafeDashboard } from 'components/ProjectSafeDashboard' +import { AppWrapper } from 'components/common' import { V1ProjectContext } from 'contexts/v1/Project/V1ProjectContext' -import { useRouter } from 'next/router' +import { V1ProjectProvider } from 'contexts/v1/Project/V1ProjectProvider' import { V1UserProvider } from 'contexts/v1/User/V1UserProvider' import { V1ProjectMetadataProvider } from 'contexts/v1/V1ProjectMetadataProvider' -import { V1ProjectProvider } from 'contexts/v1/Project/V1ProjectProvider' +import { useRouter } from 'next/router' import { useContext } from 'react' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' function V1ProjectSafeDashboard({ handle }: { handle: string }) { const { owner } = useContext(V1ProjectContext) @@ -36,3 +37,5 @@ export default function V1ProjectSafeDashboardPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/privacy.tsx b/src/pages/privacy.tsx index 0f1a1d10f2..78b440d479 100644 --- a/src/pages/privacy.tsx +++ b/src/pages/privacy.tsx @@ -1,6 +1,7 @@ import { AppWrapper } from 'components/common' import ExternalLink from 'components/ExternalLink' import Link from 'next/link' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function PrivacyPolicyPage() { return ( @@ -73,3 +74,5 @@ function PrivacyPolicy() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/projects/index.tsx b/src/pages/projects/index.tsx index d6045da579..8b92e4ef9b 100644 --- a/src/pages/projects/index.tsx +++ b/src/pages/projects/index.tsx @@ -1,5 +1,6 @@ import { ProjectsView } from 'components/Projects/ProjectsView' import { AppWrapper } from 'components/common' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function ProjectsPage() { return ( @@ -8,3 +9,5 @@ export default function ProjectsPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/success-stories/constitutiondao.tsx b/src/pages/success-stories/constitutiondao.tsx index 577c457f24..f532a4965f 100644 --- a/src/pages/success-stories/constitutiondao.tsx +++ b/src/pages/success-stories/constitutiondao.tsx @@ -10,6 +10,7 @@ import { CONSTITUTION_FUNDING_CONFIG, } from 'constants/successStoryProjects' import Image from 'next/image' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function ConstitutionDAOPage() { const constitutionSuccessStoryProject = CASE_STUDY_PROJECTS[0] @@ -106,3 +107,5 @@ export default function ConstitutionDAOPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/success-stories/moondao.tsx b/src/pages/success-stories/moondao.tsx index d73ec42021..28da591597 100644 --- a/src/pages/success-stories/moondao.tsx +++ b/src/pages/success-stories/moondao.tsx @@ -10,6 +10,7 @@ import { MOONDAO_FUNDING_CONFIG, } from 'constants/successStoryProjects' import Image from 'next/image' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function MoonDAOPage() { const moonDAOSuccessStoryProject = CASE_STUDY_PROJECTS[1] @@ -114,3 +115,5 @@ export default function MoonDAOPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/success-stories/sharkdao.tsx b/src/pages/success-stories/sharkdao.tsx index eb56035b5b..dce2d635cd 100644 --- a/src/pages/success-stories/sharkdao.tsx +++ b/src/pages/success-stories/sharkdao.tsx @@ -10,6 +10,7 @@ import { SHARKDAO_FUNDING_CONFIG, } from 'constants/successStoryProjects' import Image from 'next/image' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function SharkDAOPage() { const sharkDAOSuccessStoryProject = CASE_STUDY_PROJECTS[2] @@ -133,3 +134,5 @@ export default function SharkDAOPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/success-stories/studiodao.tsx b/src/pages/success-stories/studiodao.tsx index 367826567a..396ae006d8 100644 --- a/src/pages/success-stories/studiodao.tsx +++ b/src/pages/success-stories/studiodao.tsx @@ -10,6 +10,7 @@ import { STUDIODAO_FUNDING_CONFIG, } from 'constants/successStoryProjects' import Image from 'next/image' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function StudioDAOPage() { const studioDAOSuccessStoryProject = CASE_STUDY_PROJECTS[3] @@ -117,3 +118,5 @@ export default function StudioDAOPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/v2/p/[projectId]/contracts/index.tsx b/src/pages/v2/p/[projectId]/contracts/index.tsx index 05ce812863..bc98e38a4a 100644 --- a/src/pages/v2/p/[projectId]/contracts/index.tsx +++ b/src/pages/v2/p/[projectId]/contracts/index.tsx @@ -3,6 +3,7 @@ import { V2V3ProjectContractsDashboard } from 'components/v2v3/V2V3Project/V2V3P import { TransactionProvider } from 'contexts/Transaction/TransactionProvider' import { V2V3ProjectPageProvider } from 'contexts/v2v3/V2V3ProjectPageProvider' import { useRouter } from 'next/router' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function V2V3ProjectContractsPage() { const router = useRouter() @@ -22,3 +23,5 @@ export default function V2V3ProjectContractsPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/v2/p/[projectId]/index.tsx b/src/pages/v2/p/[projectId]/index.tsx index 1ab2a1baba..aef545be06 100644 --- a/src/pages/v2/p/[projectId]/index.tsx +++ b/src/pages/v2/p/[projectId]/index.tsx @@ -6,9 +6,10 @@ import { PV_V2 } from 'constants/pv' import { AnnouncementsProvider } from 'contexts/Announcements/AnnouncementsProvider' import { V2V3ProjectPageProvider } from 'contexts/v2v3/V2V3ProjectPageProvider' import { paginateDepleteProjectsQueryCall } from 'lib/apollo/paginateDepleteProjectsQuery' +import { loadCatalog } from 'locales/utils' import { ProjectMetadata } from 'models/projectMetadata' import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next' -import { Suspense, lazy } from 'react' +import React, { PropsWithChildren, Suspense, lazy } from 'react' import { featureFlagEnabled } from 'utils/featureFlags' import { cidFromUrl, ipfsPublicGatewayUrl } from 'utils/ipfs' import { @@ -37,12 +38,18 @@ export const getStaticPaths: GetStaticPaths = async () => { } export const getStaticProps: GetStaticProps< - ProjectPageProps + ProjectPageProps & { i18n: unknown } > = async context => { + const locale = context.locale as string + const messages = await loadCatalog(locale) + const i18n = { locale, messages } + if (!context.params) throw new Error('params not supplied') const projectId = parseInt(context.params.projectId as string) - const props = await getProjectStaticProps(projectId) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const props = (await getProjectStaticProps(projectId)) as any + props.props.i18n = i18n return { ...props, @@ -86,19 +93,34 @@ export default function V2ProjectPage({ <> - - - - {newProjectPageEnabled ? ( - }> - - - ) : ( - - )} - - - + <_Wrapper> + + + + {newProjectPageEnabled ? ( + }> + + + ) : ( + + )} + + + + ) } + +// This is a hack to avoid SSR for now. At the moment when this is not applied to this page, you will see a rehydration error. +const _Wrapper: React.FC = ({ children }) => { + const [hasMounted, setHasMounted] = React.useState(false) + React.useEffect(() => { + setHasMounted(true) + }, []) + if (!hasMounted) { + return null + } + + return <>{children} +} diff --git a/src/pages/v2/p/[projectId]/safe/index.tsx b/src/pages/v2/p/[projectId]/safe/index.tsx index 58a2d02890..7475419b8e 100644 --- a/src/pages/v2/p/[projectId]/safe/index.tsx +++ b/src/pages/v2/p/[projectId]/safe/index.tsx @@ -1,11 +1,12 @@ -import { AppWrapper } from 'components/common' import { ProjectSafeDashboard } from 'components/ProjectSafeDashboard' +import { AppWrapper } from 'components/common' +import { TransactionProvider } from 'contexts/Transaction/TransactionProvider' import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext' import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext' -import { useRouter } from 'next/router' -import { TransactionProvider } from 'contexts/Transaction/TransactionProvider' import { V2V3ProjectPageProvider } from 'contexts/v2v3/V2V3ProjectPageProvider' +import { useRouter } from 'next/router' import { useContext } from 'react' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' import { v2v3ProjectRoute } from 'utils/routes' function V2V3ProjectSafeDashboard() { @@ -38,3 +39,5 @@ export default function V2V3ProjectSafeDashboardPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/v2/p/[projectId]/settings/[settingsPage].tsx b/src/pages/v2/p/[projectId]/settings/[settingsPage].tsx index af82d5f542..d2e4f4af51 100644 --- a/src/pages/v2/p/[projectId]/settings/[settingsPage].tsx +++ b/src/pages/v2/p/[projectId]/settings/[settingsPage].tsx @@ -2,6 +2,7 @@ import { ProjectSettingsContent } from 'components/v2v3/V2V3Project/V2V3ProjectS import { V2V3SettingsPageKey } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/ProjectSettingsDashboard' import { V2V3SettingsProvider } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/V2V3SettingsProvider' import { useRouter } from 'next/router' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function V2V3CycleSettingsPage() { const router = useRouter() @@ -17,3 +18,5 @@ export default function V2V3CycleSettingsPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/pages/v2/p/[projectId]/settings/index.tsx b/src/pages/v2/p/[projectId]/settings/index.tsx index d03dd0a5e2..89d29798ef 100644 --- a/src/pages/v2/p/[projectId]/settings/index.tsx +++ b/src/pages/v2/p/[projectId]/settings/index.tsx @@ -1,5 +1,6 @@ import { ProjectSettingsDashboard } from 'components/v2v3/V2V3Project/V2V3ProjectSettings' import { V2V3SettingsProvider } from 'components/v2v3/V2V3Project/V2V3ProjectSettings/V2V3SettingsProvider' +import globalGetServerSideProps from 'utils/next-server/globalGetServerSideProps' export default function V2V3ProjectSettingsPage() { return ( @@ -8,3 +9,5 @@ export default function V2V3ProjectSettingsPage() { ) } + +export const getServerSideProps = globalGetServerSideProps diff --git a/src/utils/next-server/globalGetServerSideProps.ts b/src/utils/next-server/globalGetServerSideProps.ts new file mode 100644 index 0000000000..4824abe8c4 --- /dev/null +++ b/src/utils/next-server/globalGetServerSideProps.ts @@ -0,0 +1,23 @@ +import { loadCatalog } from 'locales/utils' +import { GetServerSidePropsContext, GetServerSidePropsResult } from 'next' + +/** + * `getServerSideProps` for all pages. + * + * This is a global getServerSideProps that is used for all pages. It is used + * to load the i18n catalog for the page. + * + * @param ctx + * @returns + */ +export default async function globalGetServerSideProps( + ctx: GetServerSidePropsContext, +): Promise> { + const locale = ctx.locale as string + const messages = await loadCatalog(locale) + return { + props: { + i18n: { messages, locale }, + }, + } +} diff --git a/src/utils/projectPageLoaders.ts b/src/utils/projectPageLoaders.ts index 795b5f43bb..ed692770dd 100644 --- a/src/utils/projectPageLoaders.ts +++ b/src/utils/projectPageLoaders.ts @@ -48,7 +48,7 @@ export const getV1StaticProps: GetStaticProps< console.error( `Failed to load metadata uri for ${JSON.stringify(context.params)}`, ) - return { notFound: true } + return { notFound: true, props: undefined } } try { @@ -63,7 +63,7 @@ export const getV1StaticProps: GetStaticProps< // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { if (e?.response?.status === 404) { - return { notFound: true } + return { notFound: true, props: undefined } } throw e } diff --git a/yarn.lock b/yarn.lock index 1f168c2412..83870d05b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4232,6 +4232,46 @@ resolved "https://registry.yarnpkg.com/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.1.2.tgz#68f52ff847e596e16acf089ff1d758928db28a6a" integrity sha512-FhdfV9XS3MUkQkmYK6SC4q6i2qQhk3HfVG5bhThukB8dHn6iK0sytBK9uL7BLsV4TJR6YKi3mDTO4MMWreYHHw== +"@lingui/babel-plugin-extract-messages@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@lingui/babel-plugin-extract-messages/-/babel-plugin-extract-messages-4.4.0.tgz#3f4e003fddc89d5a8071bfe2d15d18d1628e346a" + integrity sha512-0pu4bgQCGEa1e7a6qwB9pEEMpq8GzI4LW6V9YaraXUexusahJpnfKP81PIBsm8zj06OlYK5oI2AflkOmGMCiBA== + +"@lingui/cli@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@lingui/cli/-/cli-4.4.0.tgz#ef143e2de21916a8f4c1b67b2a86e3ddcd8c90d4" + integrity sha512-9mamvFfzXoaa7kSyshfCrHdK54oAhGB3P7S5M8QjHxUINaQn8naRhZ1AEFoW6rw8Jy2I27XplJsuQrdgxkd+Zw== + dependencies: + "@babel/core" "^7.21.0" + "@babel/generator" "^7.21.1" + "@babel/parser" "^7.21.2" + "@babel/runtime" "^7.21.0" + "@babel/types" "^7.21.2" + "@lingui/babel-plugin-extract-messages" "4.4.0" + "@lingui/conf" "4.4.0" + "@lingui/core" "4.4.0" + "@lingui/format-po" "4.4.0" + "@lingui/message-utils" "4.4.0" + babel-plugin-macros "^3.0.1" + chalk "^4.1.0" + chokidar "3.5.1" + cli-table "0.3.6" + commander "^10.0.0" + convert-source-map "^2.0.0" + date-fns "^2.16.1" + esbuild "^0.17.10" + glob "^7.1.4" + inquirer "^7.3.3" + micromatch "4.0.2" + normalize-path "^3.0.0" + ora "^5.1.0" + pathe "^1.1.0" + pkg-up "^3.1.0" + pofile "^1.1.4" + pseudolocale "^2.0.0" + ramda "^0.27.1" + source-map "^0.8.0-beta.0" + "@lingui/cli@^4.0.0": version "4.1.2" resolved "https://registry.yarnpkg.com/@lingui/cli/-/cli-4.1.2.tgz#39811b2dcb61b0a4c8df79cdd74a8eed2ed71e7f" @@ -4279,6 +4319,18 @@ jiti "^1.17.1" lodash.get "^4.4.2" +"@lingui/conf@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@lingui/conf/-/conf-4.4.0.tgz#0cf0506a26e393aefb36f0565c3e9dc4a7fd800b" + integrity sha512-mIAGh5eeWJm5jYM/ag4lbktxlS/73iANOahMM3q5OlEfIVvBzoinwSONRQEVsIgLT1SI6ULbqvVaI42VF5WrAw== + dependencies: + "@babel/runtime" "^7.20.13" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + jest-validate "^29.4.3" + jiti "^1.17.1" + lodash.get "^4.4.2" + "@lingui/core@4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@lingui/core/-/core-4.1.2.tgz#94bf8d539d8a67b0216aa705919d16b4b29b8cd6" @@ -4287,6 +4339,15 @@ "@babel/runtime" "^7.20.13" "@lingui/message-utils" "4.1.2" +"@lingui/core@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@lingui/core/-/core-4.4.0.tgz#d043c2770673d70bafa6b850cfeec3c859844085" + integrity sha512-0ngEP+g4bt6f3cNqEzkU5796VkEEamxNXF/JD/QV9Ftxp8QBw91WqAoHjNYs3aYZOctCsRBR7FlvRQ6o2fDWDg== + dependencies: + "@babel/runtime" "^7.20.13" + "@lingui/message-utils" "4.4.0" + unraw "^2.0.1" + "@lingui/detect-locale@^4.0.0": version "4.1.2" resolved "https://registry.yarnpkg.com/@lingui/detect-locale/-/detect-locale-4.1.2.tgz#dae1fb73ce88d09ab6ca0f728b36e8774e595cc7" @@ -4302,6 +4363,25 @@ date-fns "^2.29.3" pofile "^1.1.4" +"@lingui/format-po@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@lingui/format-po/-/format-po-4.4.0.tgz#37f119545d39a625698877a2e0ca5bb27b9ea967" + integrity sha512-FRk9HD199Klknv+zccytddHfnSJwns6Fv3ZHvm8hoB9VLKnO0RBMR2f3mqw+MYPvSYGtTIxVQXLGISva7nFG4A== + dependencies: + "@lingui/conf" "4.4.0" + "@lingui/message-utils" "4.4.0" + date-fns "^2.29.3" + pofile "^1.1.4" + +"@lingui/loader@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@lingui/loader/-/loader-4.4.0.tgz#ec1d316ba9337b5274c83f9cd94b7b002664e6e6" + integrity sha512-MDsfuZjG2/bju13/b3PEzxcD45JJBR1Q+i2fKkVtgyiVjh2UUvYeg4PgCQwsbWCblRQ4L9bpTcgbI+yFW8Zb7w== + dependencies: + "@babel/runtime" "^7.20.13" + "@lingui/cli" "4.4.0" + "@lingui/conf" "4.4.0" + "@lingui/macro@^4.0.0": version "4.1.2" resolved "https://registry.yarnpkg.com/@lingui/macro/-/macro-4.1.2.tgz#8f4d27355d72c1d5dc8dc74ef247746877aec73c" @@ -4320,6 +4400,13 @@ dependencies: "@messageformat/parser" "^5.0.0" +"@lingui/message-utils@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@lingui/message-utils/-/message-utils-4.4.0.tgz#88a7ff9c0ca10fdce25374a92fc27b932a96778a" + integrity sha512-SScnNuemsyHx2vyLvLsHgmAaCBHwnaAxUg3LkKoulqXe2Po8CmLBh1/28oNQ20ZhjwadUmy0unGalp9qqEBOkw== + dependencies: + "@messageformat/parser" "^5.0.0" + "@lingui/react@^4.0.0": version "4.1.2" resolved "https://registry.yarnpkg.com/@lingui/react/-/react-4.1.2.tgz#763bd93c75e8790f7534fdb0d98bf37f7607f1ab" @@ -4328,6 +4415,11 @@ "@babel/runtime" "^7.20.13" "@lingui/core" "4.1.2" +"@lingui/swc-plugin@4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@lingui/swc-plugin/-/swc-plugin-4.0.4.tgz#1e6e753b8aae50edd929eb4905db24a186513dca" + integrity sha512-xRnR96Mqi6zwGlVfGJMfoM8QykBbUz/sSnwmcFL9BZ8Y9YBZxzLAVf4t1BbiIQsAs+pMYu/HfujTBD4y/r1ucA== + "@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.1.tgz#64df34e2f12e68e78ac57e571d25ec07fa460ca9" @@ -20364,6 +20456,11 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +unraw@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unraw/-/unraw-2.0.1.tgz#7b51dcdfb1e43d59d5e52cdb44d349d029edbaba" + integrity sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ== + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"