-
Notifications
You must be signed in to change notification settings - Fork 541
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support dynamic app config and runtime config (#1154)
- Loading branch information
Showing
9 changed files
with
127 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
import { eventHandler } from "h3"; | ||
|
||
export default eventHandler(() => "<h1>Hello Nitro!</h1>"); | ||
export default eventHandler(() => { | ||
return "<h1>Hello Nitro!</h1>"; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,103 @@ | ||
import destr from "destr"; | ||
import { snakeCase } from "scule"; | ||
import { appConfig as _appConfig } from "#internal/nitro/virtual/app-config"; | ||
import { klona } from "klona"; | ||
import { H3Event } from "h3"; | ||
import { appConfig as _inlineAppConfig } from "#internal/nitro/virtual/app-config"; | ||
|
||
// Runtime config | ||
const _runtimeConfig = process.env.RUNTIME_CONFIG as any; | ||
// Static runtime config inlined by nitro build | ||
const _inlineRuntimeConfig = process.env.RUNTIME_CONFIG as any; | ||
const ENV_PREFIX = "NITRO_"; | ||
const ENV_PREFIX_ALT = | ||
_runtimeConfig.nitro.envPrefix ?? process.env.NITRO_ENV_PREFIX ?? "_"; | ||
overrideConfig(_runtimeConfig); | ||
_inlineRuntimeConfig.nitro.envPrefix ?? process.env.NITRO_ENV_PREFIX ?? "_"; | ||
|
||
const runtimeConfig = deepFreeze(_runtimeConfig); | ||
export default runtimeConfig; // TODO: Remove in next major version | ||
export const useRuntimeConfig = () => runtimeConfig; | ||
// Runtime config | ||
const _sharedRuntimeConfig = _deepFreeze( | ||
_applyEnv(klona(_inlineRuntimeConfig)) | ||
); | ||
export function useRuntimeConfig(event?: H3Event) { | ||
// Backwards compatibility with ambient context | ||
if (!event) { | ||
return _sharedRuntimeConfig; | ||
} | ||
// Reuse cached runtime config from event context | ||
if (event.context.nitro.runtimeConfig) { | ||
return event.context.nitro.runtimeConfig; | ||
} | ||
// Prepare runtime config for event context | ||
const runtimeConfig = klona(_inlineRuntimeConfig); | ||
_applyEnv(runtimeConfig); | ||
event.context.nitro.runtimeConfig = runtimeConfig; | ||
return runtimeConfig; | ||
} | ||
|
||
// App config | ||
const appConfig = deepFreeze(_appConfig); | ||
export const useAppConfig = () => appConfig; | ||
const _sharedAppConfig = _deepFreeze(klona(_inlineAppConfig)); | ||
export function useAppConfig(event?: H3Event) { | ||
// Backwards compatibility with ambient context | ||
if (!event) { | ||
return _sharedAppConfig; | ||
} | ||
// Reuse cached app config from event context | ||
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; | ||
return appConfig; | ||
} | ||
|
||
// --- Utils --- | ||
|
||
function getEnv(key: string) { | ||
function _getEnv(key: string) { | ||
const envKey = snakeCase(key).toUpperCase(); | ||
return destr( | ||
process.env[ENV_PREFIX + envKey] ?? process.env[ENV_PREFIX_ALT + envKey] | ||
); | ||
} | ||
|
||
function isObject(input: unknown) { | ||
function _isObject(input: unknown) { | ||
return typeof input === "object" && !Array.isArray(input); | ||
} | ||
|
||
function overrideConfig(obj: object, parentKey = "") { | ||
function _applyEnv(obj: object, parentKey = "") { | ||
for (const key in obj) { | ||
const subKey = parentKey ? `${parentKey}_${key}` : key; | ||
const envValue = getEnv(subKey); | ||
if (isObject(obj[key])) { | ||
if (isObject(envValue)) { | ||
const envValue = _getEnv(subKey); | ||
if (_isObject(obj[key])) { | ||
if (_isObject(envValue)) { | ||
obj[key] = { ...obj[key], ...envValue }; | ||
} | ||
overrideConfig(obj[key], subKey); | ||
_applyEnv(obj[key], subKey); | ||
} else { | ||
obj[key] = envValue ?? obj[key]; | ||
} | ||
} | ||
return obj; | ||
} | ||
|
||
function deepFreeze(object: Record<string, any>) { | ||
function _deepFreeze(object: Record<string, any>) { | ||
const propNames = Object.getOwnPropertyNames(object); | ||
for (const name of propNames) { | ||
const value = object[name]; | ||
if (value && typeof value === "object") { | ||
deepFreeze(value); | ||
_deepFreeze(value); | ||
} | ||
} | ||
return Object.freeze(object); | ||
} | ||
|
||
// --- Deprecated default export --- | ||
// TODO: Remove in next major version | ||
export default new Proxy(Object.create(null), { | ||
get: (_, prop) => { | ||
console.warn( | ||
"Please use `useRuntimeConfig()` instead of accessing config directly." | ||
); | ||
const runtimeConfig = useRuntimeConfig(); | ||
if (prop in runtimeConfig) { | ||
return runtimeConfig[prop]; | ||
} | ||
return undefined; | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
process.env.NITRO_DYNAMIC = "from-env"; | ||
|
||
export default eventHandler((event) => { | ||
const appConfig = useAppConfig(event); | ||
appConfig.dynamic = "from-middleware"; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
}; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters