UNPKG

nuxt-site-config

Version:

Shared site configuration for Nuxt 3 modules.

241 lines (234 loc) 8.47 kB
import { useNuxt, createResolver, addTemplate, defineNuxtModule, useLogger, addImportsDir, addServerImportsDir, addPlugin, hasNuxtModule, hasNuxtModuleCompatibility, 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 { parseURL } from 'ufo'; 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, multiTenancy: (config.multiTenancy || [])?.map((t) => { t.hosts = (t.hosts || []).map((h) => parseURL(h, "https://").host).filter(Boolean); if (!t.hosts.length) { return false; } return t; }).filter(Boolean) }; }); 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<void> } } declare module 'h3' { interface H3EventContext { siteConfig: import('${typesPath}').SiteConfigStack siteConfigNitroOrigin: string } } declare module '@nuxt/schema' { interface Nuxt { _siteConfig?: import('${typesPath}').SiteConfigStack } } declare module 'nuxt/app' { interface NuxtApp { $nuxtSiteConfig: import('${typesPath}').SiteConfigStack } } declare module '#app' { interface NuxtApp { $nuxtSiteConfig: import('${typesPath}').SiteConfigStack } } declare global { interface Window { __NUXT_SITE_CONFIG__: import('${typesPath}').SiteConfigResolved } } `; }); addImportsDir(resolve("./runtime/app/composables")); 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/server/composables")); nuxt.options.alias["#site-config"] = resolve("./runtime"); nuxt.options.nitro.alias = nuxt.options.nitro.alias || {}; nuxt.options.nitro.alias["#internal/nuxt-site-config"] = resolve("./runtime/server/composable-barrel-deprecated"); nuxt.options.build.transpile.push("site-config-stack"); addPlugin({ src: resolve("./runtime/app/plugins/0.siteConfig") }); if (hasNuxtModule("@nuxtjs/i18n")) { const isNuxtI18nV9 = await hasNuxtModuleCompatibility("@nuxtjs/i18n", ">=9"); addTemplate({ filename: "nuxt-site-config/i18n-plugin-deps.mjs", getContents() { const v9 = ["i18n:plugin", "i18n:plugin:ssg-detect", "i18n:plugin:route-locale-detect"]; const v8 = ["i18n:plugin"]; return `export const i18nPluginDeps = ${JSON.stringify(isNuxtI18nV9 ? v9 : v8)}`; } }); addPlugin({ src: resolve("./runtime/app/plugins/i18n") }); const baseUrl = nuxt.options.i18n?.baseUrl; const locale = nuxt.options.i18n?.locales?.find((l) => l.code === nuxt.options.i18n?.defaultLocale); updateSiteConfig({ _context: "@nuxtjs/i18n", url: typeof baseUrl === "string" ? baseUrl : void 0, // @ts-expect-error untyped defaultLocale: locale?.language || locale?.iso || nuxt.options.i18n?.defaultLocale }); } else if (hasNuxtModule("nuxt-i18n-micro")) { addPlugin({ src: resolve("./runtime/app/plugins/i18n-micro") }); const baseUrl = nuxt.options.i18n?.baseUrl; const locale = nuxt.options.i18n?.locales?.find((l) => l.code === nuxt.options.i18n?.defaultLocale); updateSiteConfig({ _context: "nuxt-i18n-micro", url: typeof baseUrl === "string" ? baseUrl : void 0, // @ts-expect-error untyped defaultLocale: locale?.language || locale?.iso || nuxt.options.i18n?.defaultLocale }); } addServerHandler({ middleware: true, handler: resolve("./runtime/server/middleware/init") }); if (config.debug || nuxt.options.dev) { addServerHandler({ route: "/__site-config__/debug.json", handler: resolve("./runtime/server/routes/__site-config__/debug") }); if (nuxt.options._generate) addPrerenderRoutes("/__site-config__/debug.json"); } if (nuxt.options.dev) setupDevToolsUI(resolve); addServerPlugin(resolve("./runtime/server/plugins/injectState")); } }); export { module as default };