Skip to content

Instantly share code, notes, and snippets.

@mattmess1221
Created July 29, 2025 15:05
Show Gist options
  • Save mattmess1221/3c75c9330129d813434d57893605af2f to your computer and use it in GitHub Desktop.
Save mattmess1221/3c75c9330129d813434d57893605af2f to your computer and use it in GitHub Desktop.
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);
});
}
}
}
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