- index and category pages paginate with ?page=N and 404 out of range - category content renders above the list through the html gate - related posts fall back to the latest posts - --journal-* tokens and neutral base styles - pl/en strings registered only for locales the app enables
85 lines
3.4 KiB
TypeScript
85 lines
3.4 KiB
TypeScript
// 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<keyof JournalRoutes, string> = {
|
|
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/<code>.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<JournalModuleOptions>({
|
|
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<string, unknown>)
|
|
|
|
extendPages((pages) => {
|
|
for (const key of Object.keys(PAGE_FILES) as Array<keyof JournalRoutes>) {
|
|
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<string, any>
|
|
pub.journal = {
|
|
...(pub.journal ?? {}),
|
|
routes,
|
|
perPage: { ...options.perPage },
|
|
sort: options.sort,
|
|
relatedCount: options.relatedCount,
|
|
rss: options.rss,
|
|
}
|
|
},
|
|
})
|