diff --git a/vike/client/client-routing-runtime/createPageContext.ts b/vike/client/client-routing-runtime/createPageContext.ts index 47e34254625..e21ad034329 100644 --- a/vike/client/client-routing-runtime/createPageContext.ts +++ b/vike/client/client-routing-runtime/createPageContext.ts @@ -4,8 +4,9 @@ import { getPageContextUrlComputed } from '../../shared/getPageContextUrlCompute import { getPageFilesAll } from '../../shared/getPageFiles.js' import { loadPageRoutes } from '../../shared/route/loadPageRoutes.js' import { getBaseServer } from './getBaseServer.js' -import { assert, isBaseServer, PromiseType, getGlobalObject, objectAssign } from './utils.js' +import { assert, isBaseServer, PromiseType, getGlobalObject, objectAssign, isObject } from './utils.js' const globalObject = getGlobalObject<{ + onBootResult?: Record, pageFilesData?: PromiseType> }>('createPageContext.ts', {}) @@ -14,7 +15,7 @@ async function createPageContext(urlOriginal: string) { globalObject.pageFilesData = await getPageFilesAll(true) } const { pageFilesAll, allPageIds, pageConfigs, pageConfigGlobal } = globalObject.pageFilesData - const { pageRoutes, onBeforeRouteHook } = await loadPageRoutes( + const { pageRoutes, onBeforeRouteHook, onBootHook } = await loadPageRoutes( pageFilesAll, pageConfigs, pageConfigGlobal, @@ -22,7 +23,8 @@ async function createPageContext(urlOriginal: string) { ) const baseServer = getBaseServer() assert(isBaseServer(baseServer)) - const pageContext = { + + let pageContext = { urlOriginal, _objectCreatedByVike: true, _urlHandler: null, @@ -35,6 +37,18 @@ async function createPageContext(urlOriginal: string) { _pageRoutes: pageRoutes, _onBeforeRouteHook: onBeforeRouteHook } + + if (!globalObject.onBootResult && onBootHook) { + globalObject.onBootResult = await onBootHook.hookFn(pageContext) as Record + } + + if (isObject(globalObject.onBootResult)) { + pageContext = { + ...pageContext, + ...globalObject.onBootResult + } + } + const pageContextUrlComputed = getPageContextUrlComputed(pageContext) objectAssign(pageContext, pageContextUrlComputed) return pageContext diff --git a/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts b/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts index 66f575052a4..baf95830172 100644 --- a/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts +++ b/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.ts @@ -564,7 +564,7 @@ async function getGlobalConfigs( ) const configValueSource = sources[0] if (!configValueSource) return - if (configName === 'onBeforeRoute' || configName === 'onPrerenderStart') { + if (['onBeforeRoute', 'onPrerenderStart', 'onBoot'].includes(configName)) { assert(!('value' in configValueSource)) pageConfigGlobal.configValueSources[configName] = [configValueSource] } else { diff --git a/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.ts b/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.ts index 832837a1d21..f06b9b99438 100644 --- a/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.ts +++ b/vike/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.ts @@ -189,6 +189,7 @@ const configDefinitionsBuiltIn: ConfigDefinitionsBuiltIn = { } type ConfigNameGlobal = + | 'onBoot' | 'onPrerenderStart' | 'onBeforeRoute' | 'prerender' @@ -206,6 +207,9 @@ const configDefinitionsBuiltInGlobal: Record + pageContextInit = { + ...pageContextInit, + ...onBootResult + }; + } + // Check Base URL { const pageContextHttpResponse = checkBaseUrl(pageContextInit, httpRequestId) diff --git a/vike/node/runtime/renderPage/renderPageAlreadyRouted.ts b/vike/node/runtime/renderPage/renderPageAlreadyRouted.ts index 01422895e6e..8365b72b067 100644 --- a/vike/node/runtime/renderPage/renderPageAlreadyRouted.ts +++ b/vike/node/runtime/renderPage/renderPageAlreadyRouted.ts @@ -252,6 +252,7 @@ type RenderContext = { allPageIds: string[] pageRoutes: PageRoutes onBeforeRouteHook: Hook | null + onBootHook: Hook | null } // TODO: remove getRenderContext() in favor of getGlobalObject() + reloadGlobalContext() // TODO: impl GlobalNodeContext + GlobalClientContext + GloablContext, and use GlobalContext instead of RenderContext @@ -261,7 +262,7 @@ async function getRenderContext(): Promise { false, globalContext.isProduction ) - const { pageRoutes, onBeforeRouteHook } = await loadPageRoutes( + const { pageRoutes, onBeforeRouteHook, onBootHook } = await loadPageRoutes( pageFilesAll, pageConfigs, pageConfigGlobal, @@ -278,7 +279,8 @@ async function getRenderContext(): Promise { pageConfigGlobal, allPageIds: allPageIds, pageRoutes, - onBeforeRouteHook + onBeforeRouteHook, + onBootHook } return renderContext } diff --git a/vike/shared/page-configs/Config.ts b/vike/shared/page-configs/Config.ts index 5ecf84a41fe..b994547fb02 100644 --- a/vike/shared/page-configs/Config.ts +++ b/vike/shared/page-configs/Config.ts @@ -15,6 +15,8 @@ export type { OnBeforePrerenderStartAsync } export type { OnBeforePrerenderStartSync } export type { OnBeforeRenderAsync } export type { OnBeforeRenderSync } +export type { OnBoot } +export type { OnBootSync } export type { OnBeforeRouteAsync } export type { OnBeforeRouteSync } export type { OnHydrationEndAsync } @@ -51,12 +53,12 @@ type HookNamePage = | 'onRenderClient' | 'guard' | 'data' -type HookNameGlobal = 'onBeforePrerender' | 'onBeforeRoute' | 'onPrerenderStart' +type HookNameGlobal = 'onBeforePrerender' | 'onBeforeRoute' | 'onPrerenderStart' | 'onBoot' // v0.4 design TODO/v1-release: remove type HookNameOldDesign = 'render' | 'prerender' type ConfigNameBuiltIn = - | Exclude + | Exclude | 'prerender' | 'clientEntryLoaded' | 'onBeforeRenderEnv' @@ -131,6 +133,18 @@ type OnBeforeRenderAsync = ( * https://vike.dev/onBeforeRender */ type OnBeforeRenderSync = (pageContext: PageContextServer) => { pageContext: Partial } | void +/** Hook called when Vike is first initialized. + * + * https://vike.dev/onBoot + */ +type OnBoot = ( + pageContext: PageContextServer +) => Promise<{ pageContext: Partial } | void> +/** Hook called when Vike is first initialized. + * + * https://vike.dev/onBoot + */ +type OnBootSync = (pageContext: PageContextServer) => { pageContext: Partial } | void /** Hook called before the URL is routed to a page. * * https://vike.dev/onBeforeRoute @@ -349,6 +363,12 @@ type ConfigBuiltIn = { */ onBeforePrerenderStart?: OnBeforePrerenderStartAsync | OnBeforePrerenderStartSync | ImportString + /** Hook called when Vike is first initialized. + * + * https://vike.dev/onBoot + */ + onBoot?: OnBoot | OnBootSync | ImportString + /** Hook called before the URL is routed to a page. * * https://vike.dev/onBeforeRoute diff --git a/vike/shared/route/loadPageRoutes.ts b/vike/shared/route/loadPageRoutes.ts index 693c46093fb..77eb91c096a 100644 --- a/vike/shared/route/loadPageRoutes.ts +++ b/vike/shared/route/loadPageRoutes.ts @@ -30,11 +30,11 @@ async function loadPageRoutes( pageConfigs: PageConfigRuntime[], pageConfigGlobal: PageConfigGlobalRuntime, allPageIds: string[] -): Promise<{ pageRoutes: PageRoutes; onBeforeRouteHook: null | Hook }> { +): Promise<{ pageRoutes: PageRoutes; onBeforeRouteHook: null | Hook; onBootHook: null | Hook }> { await Promise.all(pageFilesAll.filter((p) => p.fileType === '.page.route').map((p) => p.loadFile?.())) - const { onBeforeRouteHook, filesystemRoots } = getGlobalHooks(pageFilesAll, pageConfigs, pageConfigGlobal) + const { onBootHook, onBeforeRouteHook, filesystemRoots } = getGlobalHooks(pageFilesAll, pageConfigs, pageConfigGlobal) const pageRoutes = getPageRoutes(filesystemRoots, pageFilesAll, pageConfigs, allPageIds) - return { pageRoutes, onBeforeRouteHook } + return { pageRoutes, onBeforeRouteHook, onBootHook } } function getPageRoutes( @@ -175,12 +175,14 @@ function getGlobalHooks( pageConfigGlobal: PageConfigGlobalRuntime ): { onBeforeRouteHook: null | Hook + onBootHook: null | Hook filesystemRoots: null | FilesystemRoot[] } { // V1 Design if (pageConfigs.length > 0) { - const hook = getHookFromPageConfigGlobal(pageConfigGlobal, 'onBeforeRoute') - return { onBeforeRouteHook: hook, filesystemRoots: null } + const onBootHook = getHookFromPageConfigGlobal(pageConfigGlobal, 'onBoot') + const onBeforeRouteHook = getHookFromPageConfigGlobal(pageConfigGlobal, 'onBeforeRoute') + return { onBootHook, onBeforeRouteHook, filesystemRoots: null } } // Old design @@ -221,7 +223,7 @@ function getGlobalHooks( } }) - return { onBeforeRouteHook, filesystemRoots } + return { onBootHook: null, onBeforeRouteHook, filesystemRoots } } function dirname(filePath: string): string {