Dominik Milacher 73083ded58
Some checks failed
Build and deploy updated apps / Build & deploy (push) Failing after 1m7s
Overhaul content management
2025-10-22 19:31:38 +02:00

95 lines
3.3 KiB
TypeScript

import {addVitePlugin, defineNuxtModule, getLayerDirectories} from '@nuxt/kit'
import {getLayerFiles, getLayerContent} from '../utils/content-files'
import {getRoutes, getExpandedRoutes, getRouteShortcuts, getConfig} from '../utils/content-routes'
import {join, relative, dirname, basename, extname} from 'path'
import {existsSync} from 'fs'
/* Rationale:
1) Every project file containing useInjectedContent() is transformed and virtual modules are imported
2) Upon request the virtual modules are compiled from ts/yaml files
3) If any of the files that back a virtual module change the module is invalidated
*/
export default defineNuxtModule({
setup() {
addVitePlugin(() => ({
name: 'content-import-injector',
resolveId(id) {
if (id.startsWith('virtual:content/')) {
return '\0' + id // convention, hide from further processing
}
},
async load(id) {
if (id.startsWith('\0' + 'virtual:content/')) {
const slug = id.slice(17)
if (slug === 'shortcuts') {
const routes = await getRoutes()
const shortcuts = getRouteShortcuts(routes)
return `export default ${JSON.stringify(shortcuts)}`
} else if (slug === 'content.config') {
const file = getLayerDirectories().map(d => join(d.root, 'content.config.ts')).find(f => existsSync(f))
if (file) {
this.addWatchFile(file)
return `export {default} from '${file}'`
}
} else {
const files = getLayerFiles(slug)
const content = await getLayerContent(files)
files.filter(f => existsSync(f)).forEach(f => this.addWatchFile(f))
return `export const content = ${JSON.stringify(content)}`
}
}
},
hotUpdate({file, server}) {
const root = getLayerDirectories().map(d => d.root).find(r => file.startsWith(r))
if (root !== undefined) {
const path = relative(root, file)
const slug = join(dirname(path), basename(path, extname(path)))
const module = server.moduleGraph.getModuleById('\0' + `virtual:content/${slug}`)
if (module) {
server.moduleGraph.invalidateModule(module)
this.environment.hot.send({ type: 'full-reload' })
}
}
return []
},
transform(code, id) {
let slug: string | undefined = undefined
for (const root of getLayerDirectories().map(d => d.root)) {
if (id.startsWith(root) && !id.includes('/node_modules/')) {
const path = relative(root, id).split('?')[0]!
slug = join(dirname(path), basename(path, extname(path)))
break
}
}
if (slug === undefined) {
return null
}
const expression = /\buseContentInjected\s*\(\s*\)/g
if (!expression.test(code)) return null
const transformed = code.replace(expression,
`useContentInjected('${slug}', a23631da455de90c5a945da16897d0bd, d61397115e513f2bb1d8e872b7e7788f)`)
return `
import {content as a23631da455de90c5a945da16897d0bd} from 'virtual:content/content.global';
import {content as d61397115e513f2bb1d8e872b7e7788f} from 'virtual:content/${slug}';
${transformed}
`
}
}))
}
})