UNPKG

vike

Version:

The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.

166 lines (165 loc) 7.09 kB
export { getPageContextClientSerialized }; export { getPageContextClientSerializedAbort }; export { getGlobalContextClientSerialized }; import { stringify, isJsonSerializerError } from '@brillout/json-serializer/stringify'; import { assert, assertUsage, assertWarning, getPropAccessNotation, hasProp, unique } from '../utils.js'; import { isErrorPage } from '../../../shared/error-page.js'; import { addIs404ToPageProps } from '../../../shared/addIs404ToPageProps.js'; import pc from '@brillout/picocolors'; import { NOT_SERIALIZABLE } from '../../../shared/NOT_SERIALIZABLE.js'; import { pageContextInitIsPassedToClient } from '../../../shared/misc/pageContextInitIsPassedToClient.js'; import { isServerSideError } from '../../../shared/misc/isServerSideError.js'; import { getPropKeys, getPropVal, setPropVal } from './propKeys.js'; const passToClientBuiltInPageContext = [ 'abortReason', '_urlRewrite', '_urlRedirect', 'abortStatusCode', '_abortCall', /* Not needed on the client-side '_abortCaller', */ pageContextInitIsPassedToClient, 'pageId', 'routeParams', 'data', // for data() hook ]; const pageToClientBuiltInPageContextError = ['pageProps', 'is404', isServerSideError]; function getPageContextClientSerialized(pageContext) { const passToClientPageContext = getPassToClientPageContext(pageContext); const pageContextClient = applyPassToClient(passToClientPageContext, pageContext); if (passToClientPageContext.some((prop) => getPropVal(pageContext._pageContextInit, prop))) { pageContextClient[pageContextInitIsPassedToClient] = true; } const pageContextClientSerialized = serializeObject(pageContextClient, 'pageContext', passToClientPageContext); return pageContextClientSerialized; } function getGlobalContextClientSerialized(pageContext) { const passToClient = pageContext._passToClient; const globalContextClient = applyPassToClient(passToClient, pageContext._globalContext); const globalContextClientSerialized = serializeObject(globalContextClient, 'globalContext', passToClient); return globalContextClientSerialized; } function serializeObject(obj, objName, passToClient) { let serialized; try { serialized = serializeValue(obj); } catch (err) { const h = (s) => pc.cyan(s); let hasWarned = false; const propsNonSerializable = []; passToClient.forEach((prop) => { const res = getPropVal(obj, prop); if (!res) return; const { value } = res; const varName = `${objName}${getPropKeys(prop).map(getPropAccessNotation).join('')}`; try { serializeValue(value, varName); } catch (err) { propsNonSerializable.push(prop); // useConfig() wrong usage if (prop === '_configFromHook') { let pathString = ''; if (isJsonSerializerError(err)) { pathString = err.pathString; } // There used to be a `## Serialization Error` section in the docs but we removed it at: // https://github.com/vikejs/vike/commit/c9da2f577db01bd1c8f72265ff83e78484ddc2c0 assertUsage(false, `Cannot serialize config value ${h(pathString)} set by useConfig()`); } // Non-serializable property set by the user let msg = [ `${h(varName)} can't be serialized and, therefore, can't be passed to the client side.`, `Make sure ${h(varName)} is serializable, or remove ${h(JSON.stringify(prop))} from ${h('passToClient')}.`, ].join(' '); if (isJsonSerializerError(err)) { msg = `${msg} Serialization error: ${err.messageCore}.`; } else { // When a property getter throws an error console.warn('Serialization error:'); console.warn(err); msg = `${msg} The serialization failed because of the error printed above.`; } // We warn (instead of throwing an error) since Vike's client runtime throws an error (with `assertUsage()`) if the user's client code tries to access the property that cannot be serialized assertWarning(false, msg, { onlyOnce: false }); hasWarned = true; } }); assert(hasWarned); propsNonSerializable.forEach((prop) => { obj[getPropKeys(prop)[0]] = NOT_SERIALIZABLE; }); try { serialized = serializeValue(obj); } catch (err) { assert(false); } } return serialized; } function serializeValue(value, varName) { return stringify(value, { forbidReactElements: true, valueName: varName }); } function getPassToClientPageContext(pageContext) { let passToClient = [...pageContext._passToClient, ...passToClientBuiltInPageContext]; if (isErrorPage(pageContext.pageId, pageContext._globalContext._pageConfigs)) { assert(hasProp(pageContext, 'is404', 'boolean')); addIs404ToPageProps(pageContext); passToClient.push(...pageToClientBuiltInPageContextError); } passToClient = unique(passToClient); return passToClient; } function getPageContextClientSerializedAbort(pageContext) { assert(pageContext._urlRedirect || pageContext._urlRewrite || pageContext.abortStatusCode); assert(pageContext._abortCall); assert(pageContext._abortCaller); // Not needed on the client-side delete pageContext._abortCaller; const unknownProps = Object.keys(pageContext).filter((prop) => ![ // prettier-ignore // biome-ignore format: '_abortCall', /* Not needed on the client-side '_abortCaller', */ '_urlRedirect', '_urlRewrite', 'abortStatusCode', 'abortReason', 'is404', 'pageProps', ].includes(prop)); if (!pageContext._isLegacyRenderErrorPage) { assert(unknownProps.length === 0); } else { // TODO/v1-release: remove assertWarning(unknownProps.length === 0, [ "The following pageContext values won't be available on the client-side:", unknownProps.map((p) => ` pageContext[${JSON.stringify(p)}]`), 'Use `throw render()` instead of `throw RenderErrorPage()`', ].join('\n'), { onlyOnce: false, }); } return serializeValue(pageContext); } function applyPassToClient(passToClient, pageContext) { const pageContextClient = {}; passToClient.forEach((prop) => { // Get value from pageContext const res = getPropVal(pageContext, prop); if (!res) return; const { value } = res; // Set value to pageContextClient setPropVal(pageContextClient, prop, value); }); return pageContextClient; }