diff --git a/docs/1.guide/97.configuration.md b/docs/1.guide/97.configuration.md index 1dec7a20d9..6dc8f1a209 100644 --- a/docs/1.guide/97.configuration.md +++ b/docs/1.guide/97.configuration.md @@ -58,7 +58,7 @@ export default defineNuxtConfig({ ``` :: -You can now access the runtime config using `useRuntimeConfig(event)`. Use `useRuntimeConfig(event)` within event handlers and utilities and **avoid** calling it in ambient global contexts. This could lead to unexpected behavior such sharing the same runtime config across different requests. +You can now access the runtime config using `useRuntimeConfig(event)`. Use `useRuntimeConfig(event)` within event handlers, hooks and utilities and **avoid** calling it in ambient global contexts. This could lead to unexpected behavior such as sharing the same runtime config across different requests. ::code-group ```ts [api/example.get.ts (nitro)] diff --git a/src/runtime/config.ts b/src/runtime/config.ts index 0ba0855957..5e6a51e3cf 100644 --- a/src/runtime/config.ts +++ b/src/runtime/config.ts @@ -28,13 +28,13 @@ export function useRuntimeConfig< return _sharedRuntimeConfig as T; } // Reuse cached runtime config from event context - if (event.context.nitro.runtimeConfig) { + if (event.context.nitro?.runtimeConfig) { return event.context.nitro.runtimeConfig; } // Prepare runtime config for event context const runtimeConfig = klona(_inlineRuntimeConfig) as T; applyEnv(runtimeConfig, envOptions); - event.context.nitro.runtimeConfig = runtimeConfig; + (event.context.nitro ??= {}).runtimeConfig = runtimeConfig; return runtimeConfig; } @@ -46,12 +46,12 @@ export function useAppConfig(event?: H3Event) { return _sharedAppConfig; } // Reuse cached app config from event context - if (event.context.nitro.appConfig) { + if (event.context.nitro?.appConfig) { return event.context.nitro.appConfig; } // Prepare app config for event context const appConfig = klona(_inlineAppConfig); - event.context.nitro.appConfig = appConfig; + (event.context.nitro ??= {}).appConfig = appConfig; return appConfig; } diff --git a/src/types/h3.ts b/src/types/h3.ts index 115226aae4..c6555ae6ef 100644 --- a/src/types/h3.ts +++ b/src/types/h3.ts @@ -20,7 +20,7 @@ declare module "h3" { captureError: CaptureError; } interface H3Context { - nitro: { + nitro?: { _waitUntilPromises?: Promise[]; /** @experimental */ errors: { error?: Error; context: CapturedErrorContext }[]; diff --git a/test/fixture/middleware/config.ts b/test/fixture/middleware/config.ts index 28158e4dad..7140d8fc2b 100644 --- a/test/fixture/middleware/config.ts +++ b/test/fixture/middleware/config.ts @@ -1,6 +1,8 @@ process.env.NITRO_DYNAMIC = "from-env"; export default eventHandler((event) => { - const appConfig = useAppConfig(event); - appConfig.dynamic = "from-middleware"; + if (event.path.startsWith("/config")) { + const appConfig = useAppConfig(event); + appConfig.dynamic = "from-middleware"; + } }); diff --git a/test/fixture/plugins/hooks.ts b/test/fixture/plugins/hooks.ts new file mode 100644 index 0000000000..6375abd733 --- /dev/null +++ b/test/fixture/plugins/hooks.ts @@ -0,0 +1,17 @@ +export default defineNitroPlugin((app) => { + app.hooks.hook("request", (event) => { + if (event.path.startsWith("/hooks")) { + const qs = getQuery(event); + switch (qs.change) { + case "useAppConfig": { + useAppConfig(event).dynamic = "from-hook"; + break; + } + case "useRuntimeConfig": { + useRuntimeConfig(event).dynamic = "from-hook"; + break; + } + } + } + }); +}); diff --git a/test/fixture/routes/hooks.ts b/test/fixture/routes/hooks.ts new file mode 100644 index 0000000000..9097e19f5c --- /dev/null +++ b/test/fixture/routes/hooks.ts @@ -0,0 +1,14 @@ +const sharedAppConfig = useAppConfig(); +const sharedRuntimeConfig = useRuntimeConfig(); + +export default eventHandler((event) => { + const appConfig = useAppConfig(event); + const runtimeConfig = useRuntimeConfig(event); + + return { + sharedAppConfig, + appConfig, + runtimeConfig, + sharedRuntimeConfig, + }; +}); diff --git a/test/tests.ts b/test/tests.ts index 0348d9ab7f..b9d5713315 100644 --- a/test/tests.ts +++ b/test/tests.ts @@ -505,6 +505,128 @@ export function testNitro( }); }); + describe("hooks", () => { + it("no modifications", async () => { + const { data } = await callHandler({ + url: "/hooks", + }); + expect(data).toMatchObject({ + appConfig: { + dynamic: "initial", + "app-config": true, + "nitro-config": true, + "server-config": true, + }, + runtimeConfig: { + dynamic: "from-env", + url: "https://test.com", + app: { + baseURL: "/", + }, + }, + sharedAppConfig: { + dynamic: "initial", + "app-config": true, + "nitro-config": true, + "server-config": true, + }, + sharedRuntimeConfig: { + dynamic: + // TODO + ctx.preset.includes("cloudflare") || + ctx.preset === "vercel-edge" || + ctx.preset === "nitro-dev" + ? "initial" + : "from-env", + // url: "https://test.com", + app: { + baseURL: "/", + }, + }, + }); + }); + + it("modify appConfig", async () => { + const { data } = await callHandler({ + url: "/hooks?change=useAppConfig", + }); + expect(data).toMatchObject({ + appConfig: { + dynamic: "from-hook", + "app-config": true, + "nitro-config": true, + "server-config": true, + }, + runtimeConfig: { + dynamic: "from-env", + url: "https://test.com", + app: { + baseURL: "/", + }, + }, + sharedAppConfig: { + dynamic: "initial", + "app-config": true, + "nitro-config": true, + "server-config": true, + }, + sharedRuntimeConfig: { + dynamic: + // TODO + ctx.preset.includes("cloudflare") || + ctx.preset === "vercel-edge" || + ctx.preset === "nitro-dev" + ? "initial" + : "from-env", + // url: "https://test.com", + app: { + baseURL: "/", + }, + }, + }); + }); + + it("modify runtimeConfig", async () => { + const { data } = await callHandler({ + url: "/hooks?change=useRuntimeConfig", + }); + expect(data).toMatchObject({ + appConfig: { + dynamic: "initial", + "app-config": true, + "nitro-config": true, + "server-config": true, + }, + runtimeConfig: { + dynamic: "from-hook", + url: "https://test.com", + app: { + baseURL: "/", + }, + }, + sharedAppConfig: { + dynamic: "initial", + "app-config": true, + "nitro-config": true, + "server-config": true, + }, + sharedRuntimeConfig: { + dynamic: + // TODO + ctx.preset.includes("cloudflare") || + ctx.preset === "vercel-edge" || + ctx.preset === "nitro-dev" + ? "initial" + : "from-env", + // url: "https://test.com", + app: { + baseURL: "/", + }, + }, + }); + }); + }); + if (ctx.nitro!.options.timing) { it("set server timing header", async () => { const { status, headers } = await callHandler({