Skip to content

Commit 437873e

Browse files
committed
feat: extend content config
1 parent a36f9b7 commit 437873e

4 files changed

Lines changed: 41 additions & 5 deletions

File tree

modules/config.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { existsSync, readdirSync } from 'node:fs'
2-
import { defineNuxtModule, useLogger } from '@nuxt/kit'
2+
import { addServerImports, createResolver, defineNuxtModule, useLogger } from '@nuxt/kit'
33
import { defu } from 'defu'
44
import { resolveContentDir } from '../utils/content-dir'
55
import { getGitBranch, getGitEnv, getGitRoot, getLocalGitInfo } from '../utils/git'
@@ -37,7 +37,16 @@ export default defineNuxtModule<ComarkDocsOptions>({
3737
isr: 300,
3838
},
3939
async setup(options, nuxt) {
40+
const resolver = createResolver(import.meta.url)
4041
const rootDir = nuxt.options.rootDir
42+
43+
addServerImports([
44+
{
45+
name: 'extendContent',
46+
from: resolver.resolve('../server/internal/extend-content'),
47+
priority: -1,
48+
},
49+
])
4150
// Untyped view: `site` (nuxt-site-config) and `appConfig` aren't typed until app.config is generated.
4251
const nuxtOptions = nuxt.options as typeof nuxt.options & {
4352
site?: { url?: string; name?: string }

playground/server/utils/content.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { ContentOptions } from 'comark-content'
2+
3+
export function extendContent(options: ContentOptions): ContentOptions {
4+
console.log('[playground] extendContent override applied, sources:', Object.keys(options.sources || {}))
5+
return options
6+
}

server/internal/extend-content.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { ContentOptions } from 'comark-content'
2+
3+
/**
4+
* Default (no-op) hook over the options `createSourceContent` builds for every content instance.
5+
*
6+
* Consumers override it by exporting a function of the same name from their own `server/utils/`:
7+
* the layer registers this one as an auto-import with a negative priority, so a scanned export
8+
* (unimport's default priority of 1) shadows it. A plain `server/utils/` file in the layer could
9+
* not be shadowed — Nitro scans layer dirs *after* the consumer's, and equal-priority collisions
10+
* resolve to the last one scanned.
11+
*/
12+
export function extendContent<T extends ContentOptions>(options: T): T {
13+
return options
14+
}

server/utils/content.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { comarkContent, defineContentPlugin, type ComarkContent, type CacheOptions } from 'comark-content'
1+
import { type ContentOptions, defineContentPlugin, type ComarkContent, type CacheOptions, comarkContent } from 'comark-content';
22
import fs from 'comark-content/sources/fs'
33
import github from 'comark-content/sources/github'
44
import rangi from 'comark/plugins/rangi'
@@ -23,6 +23,7 @@ const comarkPlugins = [
2323
}),
2424
]
2525

26+
// Bound to THIS instance so a preview content instance serves its own version's sections, not production's.
2627
const searchSectionsPlugin = defineContentPlugin(() => ({
2728
name: 'search-sections',
2829
setup(ctx) {
@@ -38,7 +39,8 @@ export async function createSourceContent(
3839
ref: string,
3940
opts: { remote?: boolean; cache?: CacheOptions; basePath?: string; watch?: boolean } = {}
4041
) {
41-
const instance = comarkContent({
42+
43+
const options: any = {
4244
markdown: {
4345
plugins: comarkPlugins,
4446
html: false,
@@ -49,13 +51,18 @@ export async function createSourceContent(
4951
plugins: [searchSectionsPlugin],
5052
cache: opts.cache,
5153
basePath: opts.basePath,
52-
})
54+
}
55+
56+
// A no-op unless the consumer shadows it from their own `server/utils/`. Re-typed as the layer's own
57+
// options: a consumer's hook is declared against the wide `ContentOptions`, and letting that widen the
58+
// argument would erase the source and plugin types `comarkContent` infers from the literal.
59+
const instance = comarkContent(extendContent(options) as ContentOptions)
5360

5461
// Only the default instance watches: others read a fixed ref that can't change, and retaining
5562
// `watch()`'s stop function to release the watcher would leak once the preview entry is evicted.
5663
if (import.meta.dev && opts.watch) {
5764
await instance.watch()
58-
instance.hooks.hook('watch:file:update', (_source, key) => {
65+
instance.hooks.hook('watch:file:update', (_source:string, key: string) => {
5966
invalidateSearchSections(instance)
6067
console.log(`${key} updated`)
6168
})

0 commit comments

Comments
 (0)