// The layer's local Nuxt module (configKey `journal`). It registers the // Journal page components at the paths in journal.routes, so an app moves or // disables a view in its own nuxt.config without copying page files. It lives // in lib/ rather than modules/ so Nuxt's local-module scan cannot register it // a second time. Layer source never uses the ~ or @ aliases: they resolve to // the consuming project. import { fileURLToPath } from 'node:url' import { defineNuxtModule, extendPages } from 'nuxt/kit' import { DEFAULT_JOURNAL_ROUTES, JOURNAL_ROUTE_NAMES, normalizeJournalRoutes } from '../app/utils/routes.ts' import type { JournalModuleOptions, JournalRoutes } from '../app/utils/types.ts' const PAGE_FILES: Record = { index: 'PostIndexPage', post: 'PostPage', category: 'CategoryPage', tag: 'TagPage', author: 'AuthorPage', } const pagesDir = fileURLToPath(new URL('../app/journal-pages/', import.meta.url)) const langDir = fileURLToPath(new URL('../i18n/locales/', import.meta.url)) /** UI string locales the layer ships, each in i18n/locales/.json. */ const LAYER_LOCALES = ['pl', 'en'] as const /** Locale codes the project enables in its i18n config (strings or objects). */ function enabledLocaleCodes(locales: unknown): string[] { if (!Array.isArray(locales)) return [] return locales .map((l) => (typeof l === 'string' ? l : l && typeof l === 'object' ? (l as { code?: unknown }).code : undefined)) .filter((c): c is string => typeof c === 'string') } export default defineNuxtModule({ meta: { name: 'vue-journal-layer', configKey: 'journal', }, defaults: { routes: { ...DEFAULT_JOURNAL_ROUTES }, perPage: { index: 9, category: 9, tag: 9, author: 9 }, sort: 'published_at desc', relatedCount: 3, rss: true, }, setup(options, nuxt) { const routes = normalizeJournalRoutes(options.routes as unknown as Record) extendPages((pages) => { for (const key of Object.keys(PAGE_FILES) as Array) { const path = routes[key] if (path === false) continue pages.push({ name: JOURNAL_ROUTE_NAMES[key], path, file: `${pagesDir}${PAGE_FILES[key]}.vue` }) } }) // Register the layer's strings only for locales the app enables. The // layer config declares no locales: @nuxtjs/i18n merges every locale a // layer declares into the app, which would add /en routes to an app that // enables only pl. const hook = nuxt.hook as unknown as ( name: 'i18n:registerModule', fn: (register: (config: { langDir: string; locales: Array<{ code: string; file: string }> }) => void) => void, ) => void hook('i18n:registerModule', (register) => { const enabled = enabledLocaleCodes((nuxt.options as { i18n?: { locales?: unknown } }).i18n?.locales) const locales = LAYER_LOCALES.filter((code) => enabled.includes(code)).map((code) => ({ code, file: `${code}.json` })) if (locales.length > 0) register({ langDir, locales }) }) // The public runtime config carries the resolved options next to apiBase, // so pages, links and the sitemap source read one source of truth. const pub = nuxt.options.runtimeConfig.public as Record pub.journal = { ...(pub.journal ?? {}), routes, perPage: { ...options.perPage }, sort: options.sort, relatedCount: options.relatedCount, rss: options.rss, } }, })