diff --git a/packages/vike-react-zustand/src/getOrCreateStore.ts b/packages/vike-react-zustand/src/getOrCreateStore.ts index bef73bc1..50369d8a 100644 --- a/packages/vike-react-zustand/src/getOrCreateStore.ts +++ b/packages/vike-react-zustand/src/getOrCreateStore.ts @@ -41,10 +41,11 @@ function getOrCreateStore({ const serverState = store.getInitialState() const transferableState = sanitizeForSerialization(serverState) assert(stream) - // No need to escape the injected HTML — see https://github.com/vikejs/vike/blob/36201ddad5f5b527b244b24d548014ec86c204e4/packages/vike/src/server/runtime/renderPageServer/csp.ts#L45 + // No need to escape the injected nonce attribute — see https://github.com/vikejs/vike/blob/36201ddad5f5b527b244b24d548014ec86c204e4/packages/vike/src/server/runtime/renderPageServer/csp.ts#L45 const nonceAttr = pageContext.cspNonce ? ` nonce="${pageContext.cspNonce}"` : '' stream.injectToStream( - `if(!globalThis._vikeReactZustandState)globalThis._vikeReactZustandState={};globalThis._vikeReactZustandState['${key}']='${stringify(transferableState, { htmlScriptSafe: true })}'`, + `` + + `if(!globalThis._vikeReactZustandState)globalThis._vikeReactZustandState={};globalThis._vikeReactZustandState['${key}']=document.currentScript.previousElementSibling.textContent`, ) pageContext._vikeReactZustandStoresServer[key] = store return store diff --git a/packages/vike-react/src/hooks/useConfig/useConfig-server.ts b/packages/vike-react/src/hooks/useConfig/useConfig-server.ts index f91dd58a..cab4df9b 100644 --- a/packages/vike-react/src/hooks/useConfig/useConfig-server.ts +++ b/packages/vike-react/src/hooks/useConfig/useConfig-server.ts @@ -8,6 +8,7 @@ import { useStreamOptional } from 'react-streaming' import { objectKeys } from '../../utils/objectKeys.js' import { includes } from '../../utils/includes.js' import { assert } from '../../utils/assert.js' +import { escapeJavaScriptExpression } from '../../utils/escapeJavaScriptExpression.js' import { configsCumulative } from './configsCumulative.js' import { configsClientSide } from './configsClientSide.js' @@ -61,9 +62,10 @@ type Stream = NonNullable> function apply(config: ConfigViaHook, stream: Stream, pageContext: PageContextServer) { const { title } = config if (title) { - // No need to escape the injected HTML — see https://github.com/vikejs/vike/blob/36201ddad5f5b527b244b24d548014ec86c204e4/packages/vike/src/server/runtime/renderPageServer/csp.ts#L45 + // No need to escape the injected nonce attribute — see https://github.com/vikejs/vike/blob/36201ddad5f5b527b244b24d548014ec86c204e4/packages/vike/src/server/runtime/renderPageServer/csp.ts#L45 const nonceAttr = pageContext.cspNonce ? ` nonce="${pageContext.cspNonce}"` : '' - const htmlSnippet = `document.title = ${JSON.stringify(title)}` + const titleJs = escapeJavaScriptExpression(JSON.stringify(title)) + const htmlSnippet = `document.title = ${titleJs}` stream.injectToStream(htmlSnippet) } } diff --git a/packages/vike-react/src/utils/escapeJavaScriptExpression.ts b/packages/vike-react/src/utils/escapeJavaScriptExpression.ts new file mode 100644 index 00000000..5bb06ccd --- /dev/null +++ b/packages/vike-react/src/utils/escapeJavaScriptExpression.ts @@ -0,0 +1,15 @@ +/** + * Escapes a JavaScript expression (e.g. the output of `JSON.stringify()`) so that it can be safely embedded inside an inline `` nor `