Created
July 29, 2025 15:05
-
-
Save mattmess1221/3c75c9330129d813434d57893605af2f to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import type { RuntimeConfig } from "nuxt/schema"; | |
import * as fs from "node:fs/promises"; | |
import { useNuxt } from "@nuxt/kit"; | |
import { join } from "pathe"; | |
import { snakeCase } from "scule"; | |
type RecursiveExtract<T, E> = T extends object | |
? { | |
[K in keyof T as T[K] extends object | string ? K : never]: T[K] extends object ? RecursiveExtract<T[K], E> : T[K]; | |
} | |
: Extract<T, E>; | |
type Promisable<T> = T | PromiseLike<T>; | |
type AutoKey<T = any> = { | |
[K in keyof T]?: T[K] extends object ? AutoKey<T[K]> : (() => Promisable<T[K] & string>) | |
}; | |
type ExcludeEmptyObject<T> = T extends object | |
? { | |
[K in keyof T as keyof T[K] extends never ? never : K]: ExcludeEmptyObject<T[K]>; | |
} | |
: T; | |
type StringRuntimeConfig = ExcludeEmptyObject<RecursiveExtract<RuntimeConfig, string>>; | |
function* autokey(object: any, generated: AutoKey, segments: string[] = []): Generator<[string[], () => Promise<string>]> { | |
for (const key in generated) { | |
const seg = [...segments, key]; | |
const value = generated[key]; | |
if (typeof value === "object" && value !== null) { | |
yield* autokey(object[key], value, seg); | |
} else if (typeof value === "function") { | |
yield [seg, value]; | |
} | |
} | |
} | |
async function updateEnvFile(envName: string, value: string, nuxt = useNuxt()) { | |
const envPath = join(nuxt.options.rootDir, ".env"); | |
const envContent = await fs.readFile(envPath, "utf-8").catch(() => ""); | |
const lines = envContent.split("\n"); | |
if (!lines.find((line) => line.startsWith(`${envName}=`))) { | |
lines.push(`${envName}=${JSON.stringify(value)}`); | |
} | |
await fs.writeFile(envPath, lines.join("\n"), "utf-8"); | |
} | |
export function addSecureConfig(generated: AutoKey<StringRuntimeConfig>, nuxt = useNuxt()) { | |
const { runtimeConfig } = nuxt.options; | |
for (const [segments, generate] of autokey(runtimeConfig, generated)) { | |
const envName = `${runtimeConfig.nitro?.envPrefix || "NUXT_"}${segments.map((s) => snakeCase(s)).join("_").toUpperCase()}`; | |
let config: any = runtimeConfig; | |
for (const segment of segments.slice(0, -1)) { | |
config = config[segment]; | |
} | |
config[segments[segments.length - 1]] ??= ""; | |
if (nuxt.options.dev && !import.meta.env[envName]) { | |
nuxt.hooks.hook("ready", async () => { | |
const newValue = await generate(); | |
config[segments[segments.length - 1]] = newValue; | |
await updateEnvFile(envName, newValue); | |
}); | |
} | |
} | |
} |
This file contains hidden or 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
import { addSecureConfig } from "./config" | |
export default defineNuxtModule({ | |
meta: { | |
name: 'myModule', | |
key: 'myModule', | |
}, | |
setup(options, nuxt) { | |
nuxt.options.runtimeConfig.myMoudule = options | |
addSecureConfig({ | |
myModule: { | |
secretKey: () => crypto.randomUUID(), | |
} | |
}); | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment