From a76a6e4fb17a50d184bd9681f70307426f7bef20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Rialland?= Date: Wed, 13 Sep 2023 20:48:08 +0200 Subject: [PATCH] wip --- .eslintrc.cjs | 5 +- package.json | 4 +- site/build/prerender-worker.ts | 21 +- site/build/prerender.ts | 1 + site/package.json | 8 +- site/source/components/App.tsx | 393 ++++++++++-------- site/source/components/Provider.tsx | 27 +- .../components/SimulateurOrAssistantPage.tsx | 6 +- .../components/Simulation/SimulationGoal.tsx | 63 +-- .../components/conversation/Conversation.tsx | 18 +- .../components/conversation/RuleInput.tsx | 5 +- site/source/components/layout/Header.tsx | 3 +- .../source/components/utils/EngineContext.tsx | 1 + site/source/entries/entry-en.tsx | 27 +- site/source/entries/entry-fr.tsx | 6 +- site/source/entries/entry-server.tsx | 24 +- site/source/hooks/useClientOnly.ts | 16 + site/source/pages/Documentation.tsx | 16 +- site/source/pages/simulateurs/index.tsx | 8 +- .../worker/socialWorkerEngine.worker.ts | 6 +- site/tsconfig.json | 2 +- site/vite.config.ts | 9 +- yarn.lock | 106 ++--- 23 files changed, 414 insertions(+), 361 deletions(-) create mode 100644 site/source/hooks/useClientOnly.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 5701aaa77..3de581150 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -73,10 +73,7 @@ module.exports = { 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': [ 'warn', - { - additionalHooks: - 'usePromise|useLazyPromise|usePromiseOnSituationChange', - }, + { additionalHooks: 'usePromise|useLazyPromise' }, ], '@typescript-eslint/no-unsafe-call': 'warn', diff --git a/package.json b/package.json index b095dab56..82b2af072 100644 --- a/package.json +++ b/package.json @@ -64,8 +64,8 @@ "rollup": "^3.10.0", "@types/koa": "^2.13.8", "@types/react": "^18.2.18", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "18.3.0-canary-e5205658f-20230913", + "react-dom": "18.3.0-canary-e5205658f-20230913", "styled-components": "^6.0.7", "@publicodes/api": "betagouv/publicodes#head=publicodes-in-worker&workspace=@publicodes/api&v0", "publicodes": "betagouv/publicodes#head=publicodes-in-worker&workspace=publicodes&v0", diff --git a/site/build/prerender-worker.ts b/site/build/prerender-worker.ts index 1ee58cb10..e264d8198 100644 --- a/site/build/prerender-worker.ts +++ b/site/build/prerender-worker.ts @@ -16,18 +16,14 @@ const headTagsEnd = '' const regexHTML = new RegExp(htmlBodyStart + '[\\s\\S]+' + htmlBodyEnd, 'm') const regexHelmet = new RegExp(headTagsStart + '[\\s\\S]+' + headTagsEnd, 'm') +const script = `` + interface Params { site: string url: string lang: string } -const script = ` - -` - export default async ({ site, url, lang }: Params) => { const template = cache[site] ?? @@ -36,13 +32,20 @@ export default async ({ site, url, lang }: Params) => { cache[site] ??= template // // TODO: Add CI test to enforce meta tags on SSR pages - const { html, styleTags, helmet } = await render(url, lang) + const { html, styleTags, helmet } = (await render(url, lang)) as { + html: string + styleTags: string + helmet?: { title: string; meta: string } + } const page = template - .replace(regexHTML, html) + .replace(regexHTML, html.trim()) .replace('', script) .replace('', styleTags) - .replace(regexHelmet, helmet.title.toString() + helmet.meta.toString()) + .replace( + regexHelmet, + (helmet?.title.toString() ?? '') + (helmet?.meta.toString() ?? '') + ) const dir = path.join(dirname, '../dist/prerender', site, decodeURI(url)) diff --git a/site/build/prerender.ts b/site/build/prerender.ts index 2eebb239f..298f1e1c6 100644 --- a/site/build/prerender.ts +++ b/site/build/prerender.ts @@ -21,6 +21,7 @@ export const pagesToPrerender: { infrance: string[] } = { 'mon-entreprise': [ + '/test-worker', '/documentation/artiste‑auteur/cotisations/CSG‑CRDS/abattement', // '/iframes/pamc', // '/iframes/simulateur-embauche', diff --git a/site/package.json b/site/package.json index 04fa66a62..b1979e9b2 100644 --- a/site/package.json +++ b/site/package.json @@ -75,10 +75,10 @@ "modele-social": "workspace:^", "publicodes": "^1.0.0-beta.73", "publicodes-react": "^1.0.0-beta.73", - "react": "^18.2.0", + "react": "18.3.0-canary-e5205658f-20230913", "react-aria": "^3.24.0", "react-day-picker": "^8.7.1", - "react-dom": "^18.2.0", + "react-dom": "18.3.0-canary-e5205658f-20230913", "react-easy-emoji": "^1.8.1", "react-flip-move": "^3.0.5", "react-helmet-async": "^1.3.0", @@ -86,7 +86,7 @@ "react-instantsearch": "^6.38.1", "react-instantsearch-dom": "^6.38.1", "react-redux": "^8.0.5", - "react-router-dom": "^6.14.2", + "react-router-dom": "^6.15.0", "react-signature-pad-wrapper": "^3.3.1", "react-spring": "^9.5.5", "react-stately": "^3.22.0", @@ -118,7 +118,7 @@ "@storybook/react-vite": "^7.0.5", "@storybook/testing-library": "^0.1.0", "@types/history": "^5.0.0", - "@types/react": "^18.2.21", + "@types/react": "^18.2.18", "@types/react-dom": "^18.2.7", "@types/react-instantsearch-dom": "^6.12.3", "@types/react-redux": "^7.1.25", diff --git a/site/source/components/App.tsx b/site/source/components/App.tsx index c023d98f4..17005b6e4 100644 --- a/site/source/components/App.tsx +++ b/site/source/components/App.tsx @@ -1,13 +1,18 @@ import { - SuspensePromise, - useAsyncShallowCopy, - useLazyPromise, + PromiseSSR, usePromise, useWorkerEngine, } from '@publicodes/worker-react' import { ErrorBoundary } from '@sentry/react' import { FallbackRender } from '@sentry/react/types/errorboundary' -import { ComponentProps, StrictMode, useEffect, useState } from 'react' +import { + ComponentProps, + StrictMode, + Suspense, + use, + useEffect, + useState, +} from 'react' import { useTranslation } from 'react-i18next' import { Route, Routes } from 'react-router-dom' import { css, styled } from 'styled-components' @@ -42,15 +47,22 @@ type RootProps = { basename: ProviderProps['basename'] } +const TestWorkerEngineWrapper = () => ( + + + +) + const TestWorkerEngine = () => { const [refresh, setRefresh] = useState(0) const workerEngine = useWorkerEngine() - const [, trigger] = useLazyPromise( - async () => workerEngine.asyncSetSituation({ SMIC: '1000€/mois' }), - [workerEngine], - { defaultValue: 'loading...' } - ) + // const [, trigger] = useLazyPromise( + // async () => workerEngine.asyncSetSituation({ SMIC: '1000€/mois' }), + // [workerEngine], + // { defaultValue: 'loading...' } + // ) + const trigger = () => workerEngine.asyncSetSituation({ SMIC: '1000€/mois' }) const date = workerEngine.getRule('date') const SMIC = workerEngine.getRule('SMIC') @@ -58,38 +70,63 @@ const TestWorkerEngine = () => { // const parsedRules = useAsyncParsedRules() const parsedRules = workerEngine.getParsedRules() + // const resultSmic = usePromise( + // () => workerEngine.asyncEvaluate('SMIC'), + // [workerEngine], + // 'loading...' + // ) + const resultSmic = usePromise( - () => workerEngine.asyncEvaluate('SMIC'), - [workerEngine], - 'loading...' + () => + workerEngine.asyncEvaluate('SMIC').then((val) => { + console.log('**************************************') + + return val + }), + [workerEngine] + ) + const resultSmicx = usePromise( + () => + workerEngine.asyncEvaluate('SMIC').then((val) => { + console.log('¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤') + + return val + }), + [workerEngine] + ) + console.log('#####>', resultSmic, resultSmicx) + + // const [resultLazySmic, triggerLazySmic] = useLazyPromise( + // () => workerEngine.asyncEvaluate('SMIC'), + // [workerEngine], + // 'wait 3sec...' + // ) + + useEffect( + () => { + void (async () => { + await workerEngine.isWorkerReady + setTimeout(() => { + // void triggerLazySmic() + }, 3000) + })() + }, + [ + // triggerLazySmic, workerEngine.isWorkerReady + ] ) - const [resultLazySmic, triggerLazySmic] = useLazyPromise( - () => workerEngine.asyncEvaluate('SMIC'), - [workerEngine], - 'wait 3sec...' - ) - - useEffect(() => { - void (async () => { - await workerEngine.isWorkerReady - setTimeout(() => { - void triggerLazySmic() - }, 3000) - })() - }, [triggerLazySmic, workerEngine.isWorkerReady]) - - const workerEngineCopy = useAsyncShallowCopy(workerEngine) + // const workerEngineCopy = useAsyncShallowCopy(workerEngine) // // const workerEngineCopy = workerEngine - console.log('=========>', workerEngine, workerEngineCopy) + // console.log('=========>', workerEngine, workerEngineCopy) - const [, triggerCopy] = useLazyPromise(async () => { - console.log('+++++++++>', workerEngineCopy) + // const [, triggerCopy] = useLazyPromise(async () => { + // console.log('+++++++++>', workerEngineCopy) - await workerEngineCopy?.asyncSetSituation({ - SMIC: '2000€/mois', - }) - }, [workerEngineCopy]) + // await workerEngineCopy?.asyncSetSituation({ + // SMIC: '2000€/mois', + // }) + // }, [workerEngineCopy]) // const dateCopy = useAsyncGetRule('date', { // defaultValue: 'loading...', @@ -100,54 +137,80 @@ const TestWorkerEngine = () => { // workerEngine: workerEngineCopy, // }) - const dateCopy = workerEngineCopy?.getRule('date') - const parsedRulesCopy = workerEngineCopy?.getParsedRules() + // const dateCopy = workerEngineCopy?.getRule('date') + // const parsedRulesCopy = workerEngineCopy?.getParsedRules() - const resultSmicCopy = usePromise( - async () => - !workerEngineCopy - ? 'still loading...' - : workerEngineCopy.asyncEvaluate('SMIC'), - [workerEngineCopy], - 'loading...' - ) + // const resultSmicCopy = usePromise( + // async () => + // !workerEngineCopy + // ? 'still loading...' + // : workerEngineCopy.asyncEvaluate('SMIC'), + // [workerEngineCopy], + // 'loading...' + // ) + // const resultSmicCopy = !workerEngineCopy + // ? 'still loading...' + // : use(workerEngineCopy.asyncEvaluate('SMIC')) - const [resultLazySmicCopy, triggerLazySmicCopy] = useLazyPromise( - async () => - !workerEngineCopy - ? 'still loading...' - : workerEngineCopy.asyncEvaluate('SMIC'), - [workerEngineCopy], - 'wait 3sec...' - ) + // const [resultLazySmicCopy, triggerLazySmicCopy] = useLazyPromise( + // async () => + // !workerEngineCopy + // ? 'still loading...' + // : workerEngineCopy.asyncEvaluate('SMIC'), + // [workerEngineCopy], + // 'wait 3sec...' + // ) - useEffect(() => { - // console.log('useEffect') + // useEffect( + // () => { + // // console.log('useEffect') - void (async () => { - await workerEngine.isWorkerReady - setTimeout(() => { - void triggerLazySmicCopy() - }, 3000) - })() - }, [triggerLazySmicCopy, workerEngine.isWorkerReady]) + // void (async () => { + // await workerEngine.isWorkerReady + // setTimeout(() => { + // // void triggerLazySmicCopy() + // }, 3000) + // })() + // }, + // [ + // // triggerLazySmicCopy, workerEngine.isWorkerReady + // ] + // ) - const { asyncSetSituation } = workerEngineCopy ?? {} - usePromise(async () => { - // console.log('**************>', workerEngineCopy, resultSmic) + // const { asyncSetSituation } = workerEngineCopy ?? {} + // usePromise(async () => { + // // console.log('**************>', workerEngineCopy, resultSmic) - if ( - resultSmic && - typeof resultSmic !== 'string' && - typeof resultSmic.nodeValue === 'number' - ) { - // console.log('ooooooooooooooooooo', resultSmic) + // if ( + // resultSmic && + // typeof resultSmic !== 'string' && + // typeof resultSmic.nodeValue === 'number' + // ) { + // // console.log('ooooooooooooooooooo', resultSmic) - await asyncSetSituation?.({ - SMIC: resultSmic.nodeValue + '€/mois', - }) - } - }, [asyncSetSituation, resultSmic]) + // await asyncSetSituation?.({ + // SMIC: resultSmic.nodeValue + '€/mois', + // }) + // } + // }, [asyncSetSituation, resultSmic]) + + // use( + // (async () => { + // // console.log('**************>', workerEngineCopy, resultSmic) + + // if ( + // resultSmic && + // typeof resultSmic !== 'string' && + // typeof resultSmic.nodeValue === 'number' + // ) { + // // console.log('ooooooooooooooooooo', resultSmic) + + // await asyncSetSituation?.({ + // SMIC: resultSmic.nodeValue + '€/mois', + // }) + // } + // })() + // ) return (
@@ -178,40 +241,40 @@ const TestWorkerEngine = () => {

resultLazySmic:{' '} - {JSON.stringify( + {/* {JSON.stringify( typeof resultLazySmic === 'string' ? resultLazySmic : resultLazySmic?.nodeValue - )} + )} */}

-

workerEngineCopy: {JSON.stringify(workerEngineCopy?.engineId)}

+ {/*

workerEngineCopy: {JSON.stringify(workerEngineCopy?.engineId)}

*/}

dateCopy title:{' '} - {JSON.stringify( + {/* {JSON.stringify( typeof dateCopy === 'string' ? dateCopy : dateCopy?.title - )} + )} */}

parsedRulesCopy length:{' '} - {JSON.stringify(Object.entries(parsedRulesCopy ?? {}).length)} + {/* {JSON.stringify(Object.entries(parsedRulesCopy ?? {}).length)} */}

-

+ {/*

resultSmicCopy:{' '} {JSON.stringify( typeof resultSmicCopy === 'string' ? resultSmicCopy : resultSmicCopy?.nodeValue )} -

+

*/}

resultLazySmicCopy:{' '} - {JSON.stringify( + {/* {JSON.stringify( typeof resultLazySmicCopy === 'string' ? resultLazySmicCopy : resultLazySmicCopy?.nodeValue - )} + )} */}

) @@ -233,73 +296,39 @@ RootProps) { // [rules] // ) + const [promiseSSR, setPromiseSSR] = useState(false) + + const elems = ( + + + + + + ) + return ( - - - - - + + {promiseSSR ? {elems} : elems} ) } const Router = () => { - /* - const exampleSyncValue = usePromiseOnSituationChange( - () => asyncEvaluate('SMIC'), - [] - )?.nodeValue - - const exampleSyncValueWithDefault = usePromiseOnSituationChange( - async () => (await asyncEvaluate('SMIC')).nodeValue, - [], - 'loading...' - ) - - const [exampleAsyncValue, fireEvaluate] = useLazyPromise( - async (param: PublicodesExpression) => - (await asyncEvaluate(param)).nodeValue, - [], - 42 - ) - - usePromise(async () => { - let count = 0 - const interval = setInterval(() => { - void fireEvaluate(count++ % 2 === 0 ? 'date' : 'SMIC') - if (count === 7) clearInterval(interval) - }, 1000) - - await new Promise((resolve) => setTimeout(resolve, 3000)) - await asyncSetSituation({ date: '01/01/2022' }) - await new Promise((resolve) => setTimeout(resolve, 3000)) - await asyncSetSituation({ date: '01/01/2021' }) - await new Promise((resolve) => setTimeout(resolve, 3000)) - }, [fireEvaluate]) - - */ - return ( <> - {/* exemple sans valeur par defaut : {JSON.stringify(exampleSyncValue)} -
- exemple avec valeur par defaut :{' '} - {JSON.stringify(exampleSyncValueWithDefault)}
- exemple d'execution manuel : {JSON.stringify(exampleAsyncValue)} */} - {/* */} - - {/* */} -
- -
-
- +
+ + + + +
} /> @@ -355,52 +384,54 @@ const App = () => { - - } /> + + + } /> - {/* } /> */} - } - /> - } - /> - - } - /> - } - /> - } - /> - } /> - } /> - } - /> - } - /> - } /> + } + /> + } + /> + + } + /> + } + /> + } + /> + } /> + } /> + } + /> + } + /> + } /> - } /> - + } /> + + diff --git a/site/source/components/Provider.tsx b/site/source/components/Provider.tsx index 332b9e7da..802e6720b 100644 --- a/site/source/components/Provider.tsx +++ b/site/source/components/Provider.tsx @@ -1,28 +1,28 @@ import NodeWorker from '@eshaz/web-worker' import { createWorkerEngineClient } from '@publicodes/worker' -import { - SuspensePromise, - useWorkerEngine, - WorkerEngineProvider, -} from '@publicodes/worker-react' +import { useWorkerEngine, WorkerEngineProvider } from '@publicodes/worker-react' import { OverlayProvider } from '@react-aria/overlays' import { ErrorBoundary } from '@sentry/react' import i18next from 'i18next' -import { createContext, ReactNode } from 'react' +import { createContext, ReactNode, Suspense } from 'react' import { HelmetProvider } from 'react-helmet-async' import { I18nextProvider, Trans, useTranslation } from 'react-i18next' import { Provider as ReduxProvider } from 'react-redux' import { BrowserRouter } from 'react-router-dom' +// import NodeWorker from 'whatwg-worker' + import logo from '@/assets/images/logo-monentreprise.svg' import FeedbackForm from '@/components/Feedback/FeedbackForm' import { ThemeColorsProvider } from '@/components/utils/colors' import { DisableAnimationOnPrintProvider } from '@/components/utils/DisableAnimationContext' +import { Loader } from '@/design-system/icons/Loader' import { Container, Grid } from '@/design-system/layout' import DesignSystemThemeProvider from '@/design-system/root' import { H1, H4 } from '@/design-system/typography/heading' import { Link } from '@/design-system/typography/link' import { Body, Intro } from '@/design-system/typography/paragraphs' +import { ClientOnly } from '@/hooks/useClientOnly' // import { workerClient } from '@/entries/entry-fr' import { EmbededContextProvider } from '@/hooks/useIsEmbedded' import { Actions } from '@/worker/socialWorkerEngine.worker' @@ -43,11 +43,13 @@ console.time('start!') export const worker = import.meta.env.SSR ? // Node doesn't support web worker :( upvote issue here: https://github.com/nodejs/node/issues/43583 new NodeWorker( - new URL('../worker/socialWorkerEngine.worker.js', import.meta.url), + new URL('./worker/socialWorkerEngine.worker.js', import.meta.url), { type: 'module' } ) : new SocialeWorkerEngine() +console.log('worker', worker) + const workerClient = createWorkerEngineClient(worker, { initParams: [{ basename: 'mon-entreprise' }], }) @@ -84,7 +86,7 @@ export default function Provider({ - + }> )} > - {!import.meta.env.SSR && - import.meta.env.MODE === 'production' && - 'serviceWorker' in navigator && } + + {!import.meta.env.SSR && + 'serviceWorker' in navigator && } + @@ -109,7 +112,7 @@ export default function Provider({ - + diff --git a/site/source/components/SimulateurOrAssistantPage.tsx b/site/source/components/SimulateurOrAssistantPage.tsx index e150f3cb0..199a3beb8 100644 --- a/site/source/components/SimulateurOrAssistantPage.tsx +++ b/site/source/components/SimulateurOrAssistantPage.tsx @@ -1,4 +1,4 @@ -import { ComponentPropsWithoutRef } from 'react' +import { ComponentPropsWithoutRef, Suspense } from 'react' import { useSelector } from 'react-redux' import { useLocation } from 'react-router-dom' import { styled } from 'styled-components' @@ -102,7 +102,9 @@ export default function SimulateurOrAssistantPage() { )} - + + + {!inIframe && ( <> diff --git a/site/source/components/Simulation/SimulationGoal.tsx b/site/source/components/Simulation/SimulationGoal.tsx index bc6cf862b..5205d08ca 100644 --- a/site/source/components/Simulation/SimulationGoal.tsx +++ b/site/source/components/Simulation/SimulationGoal.tsx @@ -1,7 +1,7 @@ -import { useWorkerEngine } from '@publicodes/worker-react' +import { usePromise, useWorkerEngine } from '@publicodes/worker-react' import { DottedName } from 'modele-social' import { formatValue, PublicodesExpression } from 'publicodes' -import React, { useCallback, useState } from 'react' +import React, { Suspense, useCallback, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { styled } from 'styled-components' @@ -9,7 +9,6 @@ import { ForceThemeProvider } from '@/components/utils/DarkModeContext' import { Grid } from '@/design-system/layout' import { Strong } from '@/design-system/typography' import { Body, SmallBody } from '@/design-system/typography/paragraphs' -import { usePromise } from '@/hooks/usePromise' import { updateSituation } from '@/store/actions/actions' import { targetUnitSelector } from '@/store/selectors/simulationSelectors' @@ -59,7 +58,7 @@ export function SimulationGoal({ arrondi: round ? 'oui' : 'non', ...(!isTypeBoolean ? { unité: currentUnit } : {}), }), - [workerEngine, dottedName, round, isTypeBoolean, currentUnit] + [currentUnit, dottedName, isTypeBoolean, round, workerEngine] ) const rule = workerEngine.getRule(dottedName) @@ -143,33 +142,35 @@ export function SimulationGoal({ {!isFocused && !small && evaluation && ( )} - setFocused(true)} - onBlur={() => setFocused(false)} - onChange={onChange} - missing={ - evaluation && dottedName in evaluation.missingVariables - } - small={small} - formatOptions={{ - maximumFractionDigits: round ? 0 : 2, - }} - /> + + setFocused(true)} + onBlur={() => setFocused(false)} + onChange={onChange} + missing={ + evaluation && dottedName in evaluation.missingVariables + } + small={small} + formatOptions={{ + maximumFractionDigits: round ? 0 : 2, + }} + /> + ) : ( diff --git a/site/source/components/conversation/Conversation.tsx b/site/source/components/conversation/Conversation.tsx index dfacae56c..59c813824 100644 --- a/site/source/components/conversation/Conversation.tsx +++ b/site/source/components/conversation/Conversation.tsx @@ -1,7 +1,7 @@ import { useWorkerEngine, WorkerEngine } from '@publicodes/worker-react' import { DottedName } from 'modele-social' import { PublicodesExpression, RuleNode } from 'publicodes' -import React, { useCallback, useEffect, useState } from 'react' +import React, { Suspense, useCallback, useEffect, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' @@ -143,13 +143,15 @@ export default function Conversation({ 'Répondez à quelques questions additionnelles afin de préciser votre résultat.' )} - + + + diff --git a/site/source/components/conversation/RuleInput.tsx b/site/source/components/conversation/RuleInput.tsx index 429165921..10169b134 100644 --- a/site/source/components/conversation/RuleInput.tsx +++ b/site/source/components/conversation/RuleInput.tsx @@ -108,9 +108,6 @@ export default function RuleInput({ const value = evaluation?.nodeValue - const isMultipleChoices = - rule && isMultiplePossibilities(workerEngine, dottedName) - const choice = usePromise( () => getOnePossibilityOptions(workerEngine, dottedName), [workerEngine, dottedName] @@ -139,7 +136,7 @@ export default function RuleInput({ } const meta = getMeta<{ affichage?: string }>(rule.rawNode, {}) - if (isMultipleChoices) { + if (rule && isMultiplePossibilities(workerEngine, dottedName)) { return ( - {i18n.language === 'fr' && } + {i18n.language === 'fr' && } ) diff --git a/site/source/components/utils/EngineContext.tsx b/site/source/components/utils/EngineContext.tsx index 786daf885..b1f2f4504 100644 --- a/site/source/components/utils/EngineContext.tsx +++ b/site/source/components/utils/EngineContext.tsx @@ -170,6 +170,7 @@ export const useSetupSafeSituation = (workerEngine?: WorkerEngine) => { console.log('set rawSituation', rawSituation, workerEngine) void asyncSetSituation(rawSituation) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [asyncSetSituation, rawSituation]) // try { diff --git a/site/source/entries/entry-en.tsx b/site/source/entries/entry-en.tsx index a51b1238d..172298bf6 100644 --- a/site/source/entries/entry-en.tsx +++ b/site/source/entries/entry-en.tsx @@ -1,27 +1,28 @@ import { I18nProvider } from '@react-aria/i18n' import { withProfiler } from '@sentry/react' -import { createRoot } from 'react-dom/client' +import { createRoot, hydrateRoot } from 'react-dom/client' import App from '../components/App' import i18next from '../locales/i18n' -import ruleTranslations from '../locales/rules-en.yaml' -import translateRules from '../locales/translateRules' +// import ruleTranslations from '../locales/rules-en.yaml' +// import translateRules from '../locales/translateRules' import translations from '../locales/ui-en.yaml' import '../api/sentry' -export const AppEn = () => ( +const AppEn = () => ( - translateRules('en', ruleTranslations, rules) - } + // TODO: translate worker + // rulesPreTransform={(rules) => + // translateRules('en', ruleTranslations, rules) + // } /> ) -const AppEnWithProfiler = withProfiler(AppEn) +export const AppEnWithProfiler = withProfiler(AppEn) i18next.addResourceBundle('en', 'translation', translations) @@ -32,6 +33,12 @@ if (!import.meta.env.SSR) { ) const container = document.querySelector('#js') as Element - const root = createRoot(container) - root.render() + if (window.PRERENDER) { + container.innerHTML = container.innerHTML.trim() // Trim before hydrating to avoid mismatche error. + const root = hydrateRoot(container, ) + console.log('>>> hydrateRoot DONE', root) + } else { + const root = createRoot(container) + root.render() + } } diff --git a/site/source/entries/entry-fr.tsx b/site/source/entries/entry-fr.tsx index 54bbf2e93..eebefa874 100644 --- a/site/source/entries/entry-fr.tsx +++ b/site/source/entries/entry-fr.tsx @@ -13,7 +13,7 @@ declare global { } } -export const AppFr = () => { +const AppFr = () => { return ( @@ -21,7 +21,7 @@ export const AppFr = () => { ) } -const AppFrWithProfiler = withProfiler(AppFr) +export const AppFrWithProfiler = withProfiler(AppFr) if (!import.meta.env.SSR) { i18next.changeLanguage('fr').catch((err) => @@ -30,7 +30,9 @@ if (!import.meta.env.SSR) { ) const container = document.querySelector('#js') as Element if (window.PRERENDER) { + container.innerHTML = container.innerHTML.trim() // Trim before hydrating to avoid mismatche error. const root = hydrateRoot(container, ) + console.log('>>> hydrateRoot DONE', root) } else { const root = createRoot(container) root.render() diff --git a/site/source/entries/entry-server.tsx b/site/source/entries/entry-server.tsx index 5fce28906..610170789 100644 --- a/site/source/entries/entry-server.tsx +++ b/site/source/entries/entry-server.tsx @@ -1,5 +1,5 @@ +import { PromiseSSR } from '@publicodes/worker-react' import { SSRProvider } from '@react-aria/ssr' -import { lazy } from 'react' import ReactDomServerType from 'react-dom/server' // @ts-ignore import ReactDomServer from 'react-dom/server.browser' @@ -8,6 +8,8 @@ import { StaticRouter } from 'react-router-dom/server' import { ServerStyleSheet, StyleSheetManager } from 'styled-components' import i18next from '../locales/i18n' +import { AppEnWithProfiler as AppEn } from './entry-en' +import { AppFrWithProfiler as AppFr } from './entry-fr' const { renderToReadableStream } = ReactDomServer as typeof ReactDomServerType @@ -15,14 +17,6 @@ function streamToString(stream: ReadableStream) { return new Response(stream).text() } -const AppFrLazy = lazy(async () => ({ - default: (await import('./entry-fr')).AppFr, -})) - -const AppEnLazy = lazy(async () => ({ - default: (await import('./entry-en')).AppEn, -})) - // @ts-ignore global.window = { // @ts-ignore @@ -38,7 +32,6 @@ interface Result { export async function render(url: string, lang: 'fr' | 'en'): Promise { global.window.location.href = url global.window.location.search = '' - console.log({ url, lang }) const sheet = new ServerStyleSheet() const helmetContext = {} as FilledContext @@ -52,35 +45,30 @@ export async function render(url: string, lang: 'fr' | 'en'): Promise { - [prerender] window: {JSON.stringify(window)} - {lang === 'fr' ? : } + {lang === 'fr' ? : } ) - console.log('!!! STARTING !!!') - try { const stream = await renderToReadableStream(element, { onError(error, errorInfo) { + // eslint-disable-next-line no-console console.error({ error, errorInfo }) }, }) - console.log('!!! LOADING !!!') - await stream.allReady - console.log('!!! DONE !!!') - const html = await streamToString(stream) const styleTags = sheet.getStyleTags() return { html, styleTags, helmet: helmetContext.helmet } } catch (error) { + // eslint-disable-next-line no-console console.error(error) throw error diff --git a/site/source/hooks/useClientOnly.ts b/site/source/hooks/useClientOnly.ts new file mode 100644 index 000000000..2d58d6fd2 --- /dev/null +++ b/site/source/hooks/useClientOnly.ts @@ -0,0 +1,16 @@ +import { useEffect, useState } from 'react' + +// Refacto of https://github.com/gfmio/react-client-only/blob/master/index.ts + +/** React hook that returns true if the component has mounted client-side */ +export const useClientOnly = () => { + const [hasMounted, setHasMounted] = useState(false) + + useEffect(() => setHasMounted(true), []) + + return hasMounted +} + +/** React component that renders its children client-side only / after first mount */ +export const ClientOnly = ({ children }: { children: React.ReactNode }) => + useClientOnly() ? children : null diff --git a/site/source/pages/Documentation.tsx b/site/source/pages/Documentation.tsx index 8f4f84a20..191536f5b 100644 --- a/site/source/pages/Documentation.tsx +++ b/site/source/pages/Documentation.tsx @@ -1,12 +1,8 @@ -import { - SuspensePromise, - useWorkerEngine, - WorkerEngine, -} from '@publicodes/worker-react' +import { useWorkerEngine, WorkerEngine } from '@publicodes/worker-react' import rules, { DottedName } from 'modele-social' import Engine from 'publicodes' import { RulePage, useDocumentationSiteMap } from 'publicodes-react' -import { ComponentProps, useMemo, useRef } from 'react' +import { ComponentProps, Suspense, useMemo, useRef } from 'react' import { Helmet } from 'react-helmet-async' import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' @@ -45,16 +41,12 @@ interface DocumentationProps { export default function DocumentationWrapper(props: DocumentationProps) { return ( - DocumentationWrapper loading...} - activateInBrowser - > + DocumentationWrapper loading...}> - + ) } diff --git a/site/source/pages/simulateurs/index.tsx b/site/source/pages/simulateurs/index.tsx index ee8b124ee..3a30419ef 100644 --- a/site/source/pages/simulateurs/index.tsx +++ b/site/source/pages/simulateurs/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo } from 'react' +import { Suspense, useEffect, useMemo } from 'react' import { Trans } from 'react-i18next' import { Navigate, Route, Routes, useLocation } from 'react-router-dom' @@ -42,7 +42,11 @@ export default function Simulateurs() { path={ s.path.replace(absoluteSitePaths.simulateurs.index, '') + '/*' } - element={} + element={ + + + + } /> )), [simulatorsData, absoluteSitePaths] diff --git a/site/source/worker/socialWorkerEngine.worker.ts b/site/source/worker/socialWorkerEngine.worker.ts index 9e2a3f0d1..4529b52e7 100644 --- a/site/source/worker/socialWorkerEngine.worker.ts +++ b/site/source/worker/socialWorkerEngine.worker.ts @@ -23,7 +23,7 @@ function getUnitKey(unit: string): string { } let warnCount = 0 -let timeout: NodeJS.Timeout | null = null +let timeout: ReturnType | null = null const logger = { // eslint-disable-next-line @typescript-eslint/no-unused-vars warn: (message: string) => { @@ -57,12 +57,12 @@ const init = ({ basename }: Pick) => { const engine = new Engine(rules, { getUnitKey, logger }) - console.timeEnd('[createWorkerEngine] init') + console.timeEnd('(createWorkerEngine) init') return engine } -console.time('[createWorkerEngine] init') +console.time('(createWorkerEngine) init') createWorkerEngine(init, { ...publicodesReactActions(), diff --git a/site/tsconfig.json b/site/tsconfig.json index 643409f0a..ce6afecc2 100644 --- a/site/tsconfig.json +++ b/site/tsconfig.json @@ -14,7 +14,7 @@ "paths": { "@/*": ["*"] }, - "types": ["vite/client", "vite-plugin-pwa/client"], + "types": ["vite/client", "vite-plugin-pwa/client", "react/canary"], "typeRoots": [ "../node_modules/@types", "../node_modules", diff --git a/site/vite.config.ts b/site/vite.config.ts index 931efb629..a4a45d7f3 100644 --- a/site/vite.config.ts +++ b/site/vite.config.ts @@ -139,8 +139,13 @@ export default defineConfig(({ command, mode }) => ({ optimizeDeps: { entries: ['./source/entries/entry-fr.tsx', './source/entries/entry-en.tsx'], - include: ['publicodes-react > react/jsx-runtime'], - exclude: ['publicodes-react', 'publicodes'], + include: [], + exclude: [ + 'publicodes-react', + 'publicodes', + '@publicodes/worker', + '@publicodes/worker-react', + ], }, ssr: { diff --git a/yarn.lock b/yarn.lock index b35392b58..9c31490ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7390,7 +7390,7 @@ __metadata: "@publicodes/api@betagouv/publicodes#head=publicodes-in-worker&workspace=@publicodes/api&v0": version: 1.0.0-beta.72 - resolution: "@publicodes/api@https://github.com/betagouv/publicodes.git#workspace=%40publicodes%2Fapi&v0=&commit=6eab7e0019e133a012b216da6d60e3314aec5707" + resolution: "@publicodes/api@https://github.com/betagouv/publicodes.git#workspace=%40publicodes%2Fapi&v0=&commit=2125c306e130ea2a67e547539e47a6792e203b0f" dependencies: "@koa/cors": ^3.3.0 "@koa/router": ^10.1.1 @@ -7405,23 +7405,23 @@ __metadata: "@publicodes/worker-react@betagouv/publicodes#head=publicodes-in-worker&workspace=@publicodes/worker-react&v0": version: 1.0.0-beta.71 - resolution: "@publicodes/worker-react@https://github.com/betagouv/publicodes.git#workspace=%40publicodes%2Fworker-react&v0=&commit=6eab7e0019e133a012b216da6d60e3314aec5707" + resolution: "@publicodes/worker-react@https://github.com/betagouv/publicodes.git#workspace=%40publicodes%2Fworker-react&v0=&commit=2125c306e130ea2a67e547539e47a6792e203b0f" dependencies: "@publicodes/worker": ^1.0.0-beta.71 peerDependencies: publicodes: ^1.0.0-beta.40 - react: ^17 || ^18 - react-dom: ^17 || ^18 - checksum: 2428c2acd4a5cde2432c532562044b94fef0e9aa430406cc07c164caa71ec6484cdcf7906face107968d42bba70b1aa86e35f37bbcc63b9b2f000ba17c7e327d + react: 18.3.0-canary-e5205658f-20230913 + react-dom: 18.3.0-canary-e5205658f-20230913 + checksum: 24c7a4067e1b8055e850be2ae5c00bea501295478afb894b7ca014456a0337e92535ba20eda00514a15caca47a80c353e9db5364105fe61d7ae3577d786b340f languageName: node linkType: hard "@publicodes/worker@betagouv/publicodes#head=publicodes-in-worker&workspace=@publicodes/worker&v0": version: 1.0.0-beta.71 - resolution: "@publicodes/worker@https://github.com/betagouv/publicodes.git#workspace=%40publicodes%2Fworker&v0=&commit=6eab7e0019e133a012b216da6d60e3314aec5707" + resolution: "@publicodes/worker@https://github.com/betagouv/publicodes.git#workspace=%40publicodes%2Fworker&v0=&commit=2125c306e130ea2a67e547539e47a6792e203b0f" peerDependencies: publicodes: ^1.0.0-beta.40 - checksum: 26c9335ef5b4f28f241ed21f2409dc2509293432f070b76524dea877ffdf82f08e349183f87463e9f5c61ee6548640af741c40e5970f13973c8b1a250d39f6c1 + checksum: 75967563fa5d4c7eb8980d0f5625028bdf7d997f3d368949669ac3ebbd82800573ecfc5eede9baa2ccb184d8ed766ec45f988acd48a985e4a493276b07d0d7e4 languageName: node linkType: hard @@ -9401,10 +9401,10 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.7.2": - version: 1.7.2 - resolution: "@remix-run/router@npm:1.7.2" - checksum: ea43bb662f1f5c93965989b1667fb6e8a301cb69c44341ee92c81cb15ea685b494168e5905593b5777d59058f1455b4b58083d5b895f04382e49362e420d7af4 +"@remix-run/router@npm:1.8.0": + version: 1.8.0 + resolution: "@remix-run/router@npm:1.8.0" + checksum: f754f02d3b4fc86791b88acf16065000609e2324b9436027844a76831c7107c0994067cb83abdd6093c282bd518a5c89b5e02aead585782978586e3a04534428 languageName: node linkType: hard @@ -25150,21 +25150,21 @@ __metadata: "publicodes-react@betagouv/publicodes#head=publicodes-in-worker&workspace=publicodes-react&v0": version: 1.0.0-beta.72 - resolution: "publicodes-react@https://github.com/betagouv/publicodes.git#workspace=publicodes-react&v0=&commit=6eab7e0019e133a012b216da6d60e3314aec5707" + resolution: "publicodes-react@https://github.com/betagouv/publicodes.git#workspace=publicodes-react&v0=&commit=2125c306e130ea2a67e547539e47a6792e203b0f" dependencies: "@publicodes/worker-react": ^1.0.0-beta.71 styled-components: ^6.0.7 peerDependencies: publicodes: ^1.0.0-beta.72 - react: ^18 - react-dom: ^18 - checksum: e775ffb71a63949fbb2f3f55e97981b376464e3cc075f3c4f4c443e516d66910d7346a1f3952ffc905237680ba532253fde57a24b17a46e48c6940321383dedb + react: 18.3.0-canary-e5205658f-20230913 + react-dom: 18.3.0-canary-e5205658f-20230913 + checksum: 676b1bee77dabd644726259640cd08c5a59c316390b3036b2d3f06df39478cb5666399baae501447a455ec67ae1604c9d6b6f1dcb090fbbbfa4b7b614ea351e0 languageName: node linkType: hard "publicodes@betagouv/publicodes#head=publicodes-in-worker&workspace=publicodes&v0": version: 1.0.0-beta.72 - resolution: "publicodes@https://github.com/betagouv/publicodes.git#workspace=publicodes&v0=&commit=6eab7e0019e133a012b216da6d60e3314aec5707" + resolution: "publicodes@https://github.com/betagouv/publicodes.git#workspace=publicodes&v0=&commit=2125c306e130ea2a67e547539e47a6792e203b0f" peerDependencies: "@types/mocha": ^9.0.0 checksum: ab67797de175ae5b6991cf6de0163d927160599afb5d002b611b7e39844d06a3c855615cd4ebced250c60e1e9e86335579ba61e417e3fdcac9cf18f5c8ec003d @@ -25496,15 +25496,15 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.2.0": - version: 18.2.0 - resolution: "react-dom@npm:18.2.0" +"react-dom@npm:18.3.0-canary-e5205658f-20230913": + version: 18.3.0-canary-e5205658f-20230913 + resolution: "react-dom@npm:18.3.0-canary-e5205658f-20230913" dependencies: loose-envify: ^1.1.0 - scheduler: ^0.23.0 + scheduler: 0.24.0-canary-e5205658f-20230913 peerDependencies: - react: ^18.2.0 - checksum: 7d323310bea3a91be2965f9468d552f201b1c27891e45ddc2d6b8f717680c95a75ae0bc1e3f5cf41472446a2589a75aed4483aee8169287909fcd59ad149e8cc + react: 18.3.0-canary-e5205658f-20230913 + checksum: da09fd41323281c79403666ef57ac5ec30998dd88dd0b2875cbdd9faf413410b930ef8e08b292c965743d9c2005991580cc1ed1e754e4899175da3616f89569c languageName: node linkType: hard @@ -25746,27 +25746,27 @@ __metadata: languageName: node linkType: hard -"react-router-dom@npm:^6.14.2": - version: 6.14.2 - resolution: "react-router-dom@npm:6.14.2" +"react-router-dom@npm:^6.15.0": + version: 6.15.0 + resolution: "react-router-dom@npm:6.15.0" dependencies: - "@remix-run/router": 1.7.2 - react-router: 6.14.2 + "@remix-run/router": 1.8.0 + react-router: 6.15.0 peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: a53dbc566ecab7890b829d42d38553684f704803c1f615db1bd6aa2d71542c369a1a79e4385be31ae71a14b72ddbcd0d8b51188248c2bccd44e015050d1927df + checksum: 95301837e293654f00934de6a4bdb27bfb06f613503e4cce7a93f19384793729832e7479d50faf3b9457d149014d4df40a3ee3a5193d7e3a3caadb7aaa6ec0f9 languageName: node linkType: hard -"react-router@npm:6.14.2": - version: 6.14.2 - resolution: "react-router@npm:6.14.2" +"react-router@npm:6.15.0": + version: 6.15.0 + resolution: "react-router@npm:6.15.0" dependencies: - "@remix-run/router": 1.7.2 + "@remix-run/router": 1.8.0 peerDependencies: react: ">=16.8" - checksum: 7507bf5732b3a8ddbd901c2061216eebca73e194449bff58acc1445171e22bdda36b455b8af066e467748ebfb5875b3c0a565941c46af65c6f653a6ed0dc4fe4 + checksum: 345b29277e13997f2625f0037f537eaf1955bb9f44ebfea80dd3ff83fc06273f7b64e1be944bfc75945fd2af5af917874133a8a93ed5ecaca523be8f045ae166 languageName: node linkType: hard @@ -25885,12 +25885,12 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.2.0": - version: 18.2.0 - resolution: "react@npm:18.2.0" +"react@npm:18.3.0-canary-e5205658f-20230913": + version: 18.3.0-canary-e5205658f-20230913 + resolution: "react@npm:18.3.0-canary-e5205658f-20230913" dependencies: loose-envify: ^1.1.0 - checksum: 88e38092da8839b830cda6feef2e8505dec8ace60579e46aa5490fc3dc9bba0bd50336507dc166f43e3afc1c42939c09fe33b25fae889d6f402721dcd78fca1b + checksum: 5f76591c029feec8e664739ef9bf1bb41ee68fbca5e43e3cb4673b0793f01b59207519cd58c8ddd381768032d7972b90ae4937da1f059e57b897b3bd5d7644ff languageName: node linkType: hard @@ -26992,6 +26992,15 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:0.24.0-canary-e5205658f-20230913": + version: 0.24.0-canary-e5205658f-20230913 + resolution: "scheduler@npm:0.24.0-canary-e5205658f-20230913" + dependencies: + loose-envify: ^1.1.0 + checksum: 6c95ebfe834cc515366cb7bacf83f464e01eec2387d43665d5ce2af1eb6ff0c3ff3d92d9c4f2720d79380b1f5420c3d89a756d632bc4310ead045bf379e321c7 + languageName: node + linkType: hard + "scheduler@npm:^0.17.0": version: 0.17.0 resolution: "scheduler@npm:0.17.0" @@ -27002,15 +27011,6 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.23.0": - version: 0.23.0 - resolution: "scheduler@npm:0.23.0" - dependencies: - loose-envify: ^1.1.0 - checksum: d79192eeaa12abef860c195ea45d37cbf2bbf5f66e3c4dcd16f54a7da53b17788a70d109ee3d3dde1a0fd50e6a8fc171f4300356c5aee4fc0171de526bf35f8a - languageName: node - linkType: hard - "scripts@workspace:site/scripts/NAFAndGuichetData": version: 0.0.0-use.local resolution: "scripts@workspace:site/scripts/NAFAndGuichetData" @@ -27397,7 +27397,7 @@ __metadata: "@storybook/react-vite": ^7.0.5 "@storybook/testing-library": ^0.1.0 "@types/history": ^5.0.0 - "@types/react": ^18.2.21 + "@types/react": ^18.2.18 "@types/react-dom": ^18.2.7 "@types/react-instantsearch-dom": ^6.12.3 "@types/react-redux": ^7.1.25 @@ -27423,10 +27423,10 @@ __metadata: netlify-cli: ^15.11.0 publicodes: ^1.0.0-beta.73 publicodes-react: ^1.0.0-beta.73 - react: ^18.2.0 + react: 18.3.0-canary-e5205658f-20230913 react-aria: ^3.24.0 react-day-picker: ^8.7.1 - react-dom: ^18.2.0 + react-dom: 18.3.0-canary-e5205658f-20230913 react-easy-emoji: ^1.8.1 react-flip-move: ^3.0.5 react-helmet-async: ^1.3.0 @@ -27434,7 +27434,7 @@ __metadata: react-instantsearch: ^6.38.1 react-instantsearch-dom: ^6.38.1 react-redux: ^8.0.5 - react-router-dom: ^6.14.2 + react-router-dom: ^6.15.0 react-signature-pad-wrapper: ^3.3.1 react-spring: ^9.5.5 react-stately: ^3.22.0 @@ -30260,9 +30260,9 @@ __metadata: linkType: hard "whatwg-fetch@npm:^3.6.2": - version: 3.6.2 - resolution: "whatwg-fetch@npm:3.6.2" - checksum: ee976b7249e7791edb0d0a62cd806b29006ad7ec3a3d89145921ad8c00a3a67e4be8f3fb3ec6bc7b58498724fd568d11aeeeea1f7827e7e1e5eae6c8a275afed + version: 3.6.19 + resolution: "whatwg-fetch@npm:3.6.19" + checksum: 2896bc9ca867ea514392c73e2a272f65d5c4916248fe0837a9df5b1b92f247047bc76cf7c29c28a01ac6c5fb4314021d2718958c8a08292a96d56f72b2f56806 languageName: node linkType: hard