import { useNuxt, createResolver, addTemplate, defineNuxtModule, useLogger, addImportsDir, addComponent, addServerImportsDir, addPlugin, hasNuxtModule, addServerHandler, addPrerenderRoutes, addServerPlugin } from '@nuxt/kit'; import { initSiteConfig, updateSiteConfig, getSiteConfigStack } from 'nuxt-site-config-kit'; import { readPackageJSON } from 'pkg-types'; import { validateSiteConfigStack } from 'site-config-stack'; import { existsSync } from 'node:fs'; import { relative } from 'pathe'; const DEVTOOLS_UI_ROUTE = "/__nuxt-site-config"; const DEVTOOLS_UI_LOCAL_PORT = 3030; function setupDevToolsUI(resolve, nuxt = useNuxt()) { const clientPath = resolve("./client"); const isProductionBuild = existsSync(clientPath); if (isProductionBuild) { nuxt.hook("vite:serverCreated", async (server) => { const sirv = await import('sirv').then((r) => r.default || r); server.middlewares.use( DEVTOOLS_UI_ROUTE, sirv(clientPath, { dev: true, single: true }) ); }); } else { nuxt.hook("vite:extendConfig", (config) => { config.server = config.server || {}; config.server.proxy = config.server.proxy || {}; config.server.proxy[DEVTOOLS_UI_ROUTE] = { target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`, changeOrigin: true, followRedirects: true, rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "") }; }); } nuxt.hook("devtools:customTabs", (tabs) => { tabs.push({ // unique identifier name: "nuxt-site-config", // title to display in the tab title: "Site Config", // any icon from Iconify, or a URL to an image icon: "carbon:settings-check", // iframe view view: { type: "iframe", src: DEVTOOLS_UI_ROUTE } }); }); } function extendTypes(module, template) { const nuxt = useNuxt(); const { resolve } = createResolver(import.meta.url); addTemplate({ filename: `module/${module}.d.ts`, getContents: async () => { const typesPath = relative(resolve(nuxt.options.rootDir, nuxt.options.buildDir, "module"), resolve("runtime/types")); const s = await template({ typesPath }); return `// Generated by ${module} ${s} export {} `; } }); nuxt.hooks.hook("prepare:types", ({ references }) => { references.push({ path: resolve(nuxt.options.buildDir, `module/${module}.d.ts`) }); }); nuxt.hooks.hook("nitro:config", (config) => { config.typescript = config.typescript || {}; config.typescript.tsConfig = config.typescript.tsConfig || {}; config.typescript.tsConfig.include = config.typescript.tsConfig.include || []; config.typescript.tsConfig.include.push(`./module/${module}.d.ts`); }); } const module = defineNuxtModule({ meta: { name: "nuxt-site-config", compatibility: { nuxt: ">=3.9.0", bridge: false }, configKey: "site" }, defaults(nuxt) { return { enabled: true, debug: nuxt.options.debug || false }; }, async setup(config, nuxt) { const { resolve } = createResolver(import.meta.url); const { name, version } = await readPackageJSON(resolve("../package.json")); const logger = useLogger(name); logger.level = config.debug ? 4 : 3; if (config.enabled === false) { logger.debug("The module is disabled, skipping setup."); return; } await initSiteConfig(); const siteConfigInput = { ...config }; delete siteConfigInput.debug; delete siteConfigInput.enabled; updateSiteConfig({ // we should allow environment variables to override the site config _priority: -3, _context: "nuxt-site-config:config", ...siteConfigInput }); nuxt.hook("modules:done", async () => { await nuxt.callHook("site-config:resolve"); const errors = validateSiteConfigStack(getSiteConfigStack()); if (errors.length > 0) { logger.warn("[Nuxt Site Config] Invalid config provided, please correct:"); for (const error of errors) logger.log(` - ${error}`); logger.log(""); } nuxt.options.runtimeConfig["nuxt-site-config"] = { stack: getSiteConfigStack().stack, version, debug: config.debug }; }); extendTypes("nuxt-site-config", async ({ typesPath }) => { return ` declare module 'nitropack' { interface NitroRouteRules { site?: import('${typesPath}').SiteConfigInput } interface NitroRouteConfig { site?: import('${typesPath}').SiteConfig } interface NitroRuntimeHooks { 'site-config:init': (ctx: import('${typesPath}').HookSiteConfigInitContext) => void | Promise } } declare module 'h3' { interface H3EventContext { siteConfig: import('${typesPath}').SiteConfigStack siteConfigNitroOrigin: string } } declare module '@nuxt/schema' { interface AppConfigInput { /** Theme configuration */ site?: import('${typesPath}').SiteConfigInput } interface Nuxt { _siteConfig?: import('${typesPath}').SiteConfigStack } } declare module 'nuxt/app' { interface NuxtApp { $nuxtSiteConfig: import('${typesPath}').SiteConfigResolved } } declare module '#app' { interface NuxtApp { $nuxtSiteConfig: import('${typesPath}').SiteConfigResolved } } declare global { interface Window { __NUXT_SITE_CONFIG__: import('${typesPath}').SiteConfigResolved } } `; }); addImportsDir(resolve("./runtime/nuxt/composables")); await addComponent({ filePath: resolve("./runtime/nuxt/component/SiteLink.vue"), name: `${config.componentOptions?.prefix || ""}SiteLink`, global: config.componentOptions?.global }); if (process.env.playground) { nuxt.options.alias["site-config-stack/urls"] = resolve("../../site-config/src/urls"); nuxt.options.alias["site-config-stack"] = resolve("../../site-config/src/index"); } addServerImportsDir(resolve("./runtime/nitro/composables")); nuxt.options.nitro.alias = nuxt.options.nitro.alias || {}; nuxt.options.nitro.alias["#site-config"] = resolve("./runtime"); nuxt.options.nitro.alias["#internal/nuxt-site-config"] = resolve("./runtime/nitro/composable-barrel-deprecated"); nuxt.options.build.transpile.push("site-config-stack"); addPlugin({ src: resolve("./runtime/nuxt/plugins/0.siteConfig") }); if (hasNuxtModule("@nuxtjs/i18n")) { addPlugin({ mode: "server", src: resolve("./runtime/nuxt/plugins/i18n.server") }); updateSiteConfig({ _context: "@nuxtjs/i18n", // @ts-expect-error untyped url: nuxt.options.i18n?.baseUrl, // @ts-expect-error untyped defaultLocale: nuxt.options.i18n?.defaultLocale }); } addServerHandler({ middleware: true, handler: resolve("./runtime/nitro/middleware/init") }); if (config.debug || nuxt.options.dev) { addServerHandler({ route: "/__site-config__/debug.json", handler: resolve("./runtime/nitro/routes/__site-config__/debug") }); if (nuxt.options._generate) addPrerenderRoutes("/__site-config__/debug.json"); } if (nuxt.options.dev) setupDevToolsUI(resolve); addServerPlugin(resolve("./runtime/nitro/plugins/injectState")); } }); export { module as default };