Skip to content

Commit 0bab47c

Browse files
committed
New setup to support SSR streaming
1 parent 521bd39 commit 0bab47c

13 files changed

+227
-196
lines changed

src/next-appdir/DsfrHead.tsx renamed to src/next-app-router/DsfrHead.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
12
import React, { useMemo } from "react";
23
import { objectKeys } from "tsafe/objectKeys";
34
import { getAssetUrl } from "../tools/getAssetUrl";
4-
import AppleTouchIcon from "../dsfr/favicon/apple-touch-icon.png";
5-
import FaviconSvg from "../dsfr/favicon/favicon.svg";
6-
import FaviconIco from "../dsfr/favicon/favicon.ico";
5+
import AppleTouchIcon from "@codegouvfr/react-dsfr/dsfr/favicon/apple-touch-icon.png";
6+
import FaviconSvg from "@codegouvfr/react-dsfr/dsfr/favicon/favicon.svg";
7+
import FaviconIco from "@codegouvfr/react-dsfr/dsfr/favicon/favicon.ico";
78
import { getScriptToRunAsap } from "../useIsDark/scriptToRunAsap";
89
import { fontUrlByFileBasename } from "./zz_internal/fontUrlByFileBasename";
910
import { getDefaultColorSchemeServerSide } from "./zz_internal/defaultColorScheme";
@@ -12,16 +13,14 @@ import { assert } from "tsafe/assert";
1213
//NOTE: As of now there is no way to enforce ordering in Next Appdir
1314
//See: https://github.com/vercel/next.js/issues/16630
1415
// @import url(...) doesn't work. Using Sass and @use is our last resort.
15-
import "../assets/dsfr_plus_icons.scss";
16+
import "@codegouvfr/react-dsfr/assets/dsfr_plus_icons.scss";
1617
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in doc
17-
import type { startReactDsfr } from "./zz_internal/start";
1818

1919
export type DsfrHeadProps = {
2020
/** If not provided no fonts are preloaded.
2121
* Preloading of fonts is only enabled in production.
2222
*/
2323
preloadFonts?: (keyof typeof fontUrlByFileBasename)[];
24-
Link: Function;
2524
/**
2625
* When set, the value will be used as the nonce attribute of subsequent script tags.
2726
*
@@ -52,7 +51,11 @@ export type DsfrHeadProps = {
5251

5352
const isProduction = process.env.NODE_ENV !== "development";
5453

55-
export function DsfrHead(props: DsfrHeadProps) {
54+
export function DsfrHeadBase(
55+
props: DsfrHeadProps & {
56+
Link: Function;
57+
}
58+
) {
5659
const {
5760
preloadFonts = [],
5861
Link,
@@ -66,7 +69,7 @@ export function DsfrHead(props: DsfrHeadProps) {
6669
const defaultColorScheme = getDefaultColorSchemeServerSide();
6770

6871
useMemo(() => {
69-
setLink({ "Link": Link as any });
72+
setLink({ Link: Link as any });
7073
}, [Link]);
7174

7275
return (
@@ -97,7 +100,7 @@ export function DsfrHead(props: DsfrHeadProps) {
97100
suppressHydrationWarning
98101
nonce={nonce}
99102
dangerouslySetInnerHTML={{
100-
"__html": getScriptToRunAsap({
103+
__html: getScriptToRunAsap({
101104
defaultColorScheme,
102105
nonce,
103106
trustedTypesPolicyName

src/next-app-router/DsfrProvider.tsx

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"use client";
2+
/* eslint-disable @typescript-eslint/no-explicit-any */
3+
4+
import React, { useMemo, useEffect } from "react";
5+
import type { ReactNode } from "react";
6+
import { isBrowser } from "../tools/isBrowser";
7+
import { SsrIsDarkProvider } from "../useIsDark/server";
8+
import type { DefaultColorScheme } from "./zz_internal/defaultColorScheme";
9+
import { setUseLang } from "../i18n";
10+
import { setLink } from "../link";
11+
import { start } from "../start";
12+
13+
export type DsfrProviderProps = {
14+
children: ReactNode;
15+
lang: string | undefined;
16+
/** Default: false */
17+
verbose?: boolean;
18+
/**
19+
* When true, the nonce of the script tag will be checked, fetched from {@link DsfrHead} component and injected in react-dsfr scripts.
20+
*
21+
* @see https://developer.mozilla.org/fr/docs/Web/HTML/Global_attributes/nonce
22+
* @default false
23+
*/
24+
doCheckNonce?: boolean;
25+
/**
26+
* Enable Trusted Types with a custom policy name.
27+
*
28+
* Don't forget to also add the policy name in {@link DsfrHead} component.
29+
*
30+
* `<trustedTypesPolicyName>` and `<trustedTypesPolicyName>-asap` should be set in your Content-Security-Policy header.
31+
*
32+
* For example:
33+
* ```txt
34+
* With a policy name of "react-dsfr":
35+
* Content-Security-Policy:
36+
* require-trusted-types-for 'script';
37+
* trusted-types react-dsfr react-dsfr-asap nextjs nextjs#bundler;
38+
* ```
39+
*
40+
* @see https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types
41+
* @see {@link DEFAULT_TRUSTED_TYPES_POLICY_NAME}
42+
* @default "react-dsfr"
43+
*/
44+
trustedTypesPolicyName?: string;
45+
};
46+
47+
export function DsfrProviderBase(
48+
props: DsfrProviderProps & {
49+
Link: Function;
50+
defaultColorScheme: DefaultColorScheme;
51+
}
52+
) {
53+
const {
54+
children,
55+
lang,
56+
Link,
57+
defaultColorScheme,
58+
verbose = false,
59+
doCheckNonce = false,
60+
trustedTypesPolicyName = "react-dsfr"
61+
} = props;
62+
63+
/*
64+
useEffect(() => {
65+
dsfrEffect();
66+
}, []);
67+
*/
68+
69+
useMemo(() => {
70+
if (!isBrowser) {
71+
return;
72+
}
73+
74+
start({
75+
defaultColorScheme,
76+
verbose,
77+
doCheckNonce,
78+
trustedTypesPolicyName,
79+
"nextParams": {
80+
"doPersistDarkModePreferenceWithCookie": false,
81+
"registerEffectAction": action => {
82+
console.log("registerEffectAction", action);
83+
84+
if (isAfterFirstEffect) {
85+
console.log("run now");
86+
action();
87+
} else {
88+
console.log("push");
89+
actions.push(action);
90+
}
91+
}
92+
}
93+
});
94+
}, []);
95+
96+
useMemo(() => {
97+
if (lang === undefined) {
98+
return;
99+
}
100+
setUseLang({ useLang: () => lang });
101+
}, [lang]);
102+
103+
useMemo(() => {
104+
setLink({ Link: Link as any });
105+
}, [Link]);
106+
107+
if (isBrowser) {
108+
return <>{children}</>;
109+
}
110+
111+
const isDark = defaultColorScheme === "dark" ? true : false;
112+
113+
return <SsrIsDarkProvider value={isDark}>{children}</SsrIsDarkProvider>;
114+
}
115+
116+
let isAfterFirstEffect = false;
117+
const actions: (() => void)[] = [];
118+
119+
function dsfrEffect(): void {
120+
if (isAfterFirstEffect) {
121+
return;
122+
}
123+
isAfterFirstEffect = true;
124+
actions.forEach(action => {
125+
console.log("running action", action);
126+
action();
127+
});
128+
}
129+
130+
export function StartDsfrOnHydration() {
131+
useEffect(() => {
132+
console.log("wesh hydratation!");
133+
134+
dsfrEffect();
135+
}, []);
136+
137+
return null;
138+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { data_fr_scheme, data_fr_theme } from "../useIsDark/constants";
2+
import type { ColorScheme } from "../useIsDark";
3+
import {
4+
type DefaultColorScheme,
5+
setDefaultColorSchemeServerSide
6+
} from "./zz_internal/defaultColorScheme";
7+
import { setUseLang } from "../i18n";
8+
9+
const suppressHydrationWarning = true;
10+
11+
export function createGetHtmlAttributes(params: { defaultColorScheme: DefaultColorScheme }) {
12+
const { defaultColorScheme } = params;
13+
14+
function getHtmlAttributes(params: {
15+
lang: string | undefined;
16+
}): { suppressHydrationWarning: true; lang?: string } & (
17+
| Record<typeof data_fr_scheme | typeof data_fr_theme, ColorScheme>
18+
| {}
19+
) {
20+
const { lang } = params;
21+
22+
setDefaultColorSchemeServerSide({ defaultColorScheme });
23+
24+
if (lang !== undefined) {
25+
setUseLang({ useLang: () => lang });
26+
}
27+
28+
if (defaultColorScheme === "system") {
29+
return {
30+
lang,
31+
suppressHydrationWarning
32+
};
33+
}
34+
35+
return {
36+
lang,
37+
suppressHydrationWarning,
38+
[data_fr_scheme]: defaultColorScheme,
39+
[data_fr_theme]: defaultColorScheme
40+
};
41+
}
42+
return { getHtmlAttributes };
43+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export type { RegisterLink } from "../link";
22
export type { DefaultColorScheme } from "./zz_internal/defaultColorScheme";
3-
export { startReactDsfr } from "./zz_internal/start";
3+
export { DsfrProviderBase, type DsfrProviderProps, StartDsfrOnHydration } from "./DsfrProvider";
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { createGetHtmlAttributes } from "./getHtmlAttributes";
2+
export { DsfrHeadBase, type DsfrHeadProps } from "./DsfrHead";

src/next-appdir/zz_internal/defaultColorScheme.ts renamed to src/next-app-router/zz_internal/defaultColorScheme.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { assert } from "tsafe/assert";
2-
32
import type { ColorScheme } from "../../useIsDark";
43

54
export type DefaultColorScheme = ColorScheme | "system";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import marianneLightWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Light.woff2";
2+
import marianneItalicWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Light_Italic.woff2";
3+
import marianneRegularWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Regular.woff2";
4+
import marianneRegularItalicWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Regular_Italic.woff2";
5+
import marianneMediumWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Medium.woff2";
6+
import marianneMediumItalicWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Medium_Italic.woff2";
7+
import marianneBoldWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Bold.woff2";
8+
import marianneBoldItalicWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Marianne-Bold_Italic.woff2";
9+
import spectralRegularWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Spectral-Regular.woff2";
10+
import spectralExtraBoldWoff2Url from "@codegouvfr/react-dsfr/dsfr/fonts/Spectral-ExtraBold.woff2";
11+
12+
export const fontUrlByFileBasename = {
13+
"Marianne-Light": marianneLightWoff2Url,
14+
"Marianne-Light_Italic": marianneItalicWoff2Url,
15+
"Marianne-Regular": marianneRegularWoff2Url,
16+
"Marianne-Regular_Italic": marianneRegularItalicWoff2Url,
17+
"Marianne-Medium": marianneMediumWoff2Url,
18+
"Marianne-Medium_Italic": marianneMediumItalicWoff2Url,
19+
"Marianne-Bold": marianneBoldWoff2Url,
20+
"Marianne-Bold_Italic": marianneBoldItalicWoff2Url,
21+
"Spectral-Regular": spectralRegularWoff2Url,
22+
"Spectral-ExtraBold": spectralExtraBoldWoff2Url
23+
} as const;

src/next-appdir/DsfrProvider.tsx

-45
This file was deleted.

src/next-appdir/getHtmlAttributes.tsx

-39
This file was deleted.

src/next-appdir/zz_internal/fontUrlByFileBasename.ts

-23
This file was deleted.

0 commit comments

Comments
 (0)