diff --git a/.vitepress/config.js b/.vitepress/config.js
index b9f6082e0..9cc890d87 100644
--- a/.vitepress/config.js
+++ b/.vitepress/config.js
@@ -2,16 +2,11 @@ import gitCommitInfo from 'git-commit-info'
import { defineConfig } from 'vitepress'
import { pagefindPlugin } from 'vitepress-plugin-pagefind'
import { generateSidebar } from 'vitepress-sidebar'
-import { dovecotMdExtend, initDovecotMd } from '../lib/markdown.js'
+import { dovecotMdExtend } from '../lib/markdown.js'
import { getExcludes } from '../lib/utility.js'
const base = '/2.4'
-// Need to bootstrap configuration for Dovecot markdown driver (specifically,
-// loading all data files to allow existence checking), or else the markdown
-// processing will begin before Dovecot link markup is enabled
-await initDovecotMd(base)
-
export default defineConfig({
title: "Dovecot CE",
description: "Dovecot CE Documentation",
@@ -107,7 +102,7 @@ export default defineConfig({
},
markdown: {
- config: (md) => dovecotMdExtend(md),
+ config: async (md) => await dovecotMdExtend(md),
image: {
lazyLoading: true,
},
diff --git a/.vitepress/local.js.dist b/.vitepress/local.js.dist
deleted file mode 100644
index a43e56659..000000000
--- a/.vitepress/local.js.dist
+++ /dev/null
@@ -1,58 +0,0 @@
-/* This file allows configuration overrides of various core settings. */
-
-// Allows custom mapping of data sources.
-// It is used by the VitePress data loaders to determine what data to
-// load for export.
-//
-// By default, all data files are loaded from "../data" directory
-// (paths are relative to "/lib").
-//
-// Keys are data identifiers, Values are location RELATIVE TO
-// * "/lib" DIRECTORY.
-export const data_paths = {
- // doveadm: '../data/doveadm.js',
-}
-
-// A listing of files to watch to refresh data loaders in dev mode.
-// See: https://vitepress.dev/guide/data-loading#data-from-local-files
-// Paths are relative to project base.
-//
-// Supports fast-glob: https://github.com/mrmlnc/fast-glob#pattern-syntax
-//
-// Default: [ 'docs/**/*.md', 'docs/**/*.inc', 'data/**/*' ]
-export const watch_paths = []
-
-// A listing of paths containing man files.
-// Paths are relative to project base.
-//
-// Supports fast-glob: https://github.com/mrmlnc/fast-glob#pattern-syntax
-//
-// Default: [ 'docs/core/man/*.[[:digit:]].md' ]
-export const man_paths = []
-
-// A listing of paths containing plugin files.
-// Paths are relative to project base.
-//
-// Supports fast-glob: https://github.com/mrmlnc/fast-glob#pattern-syntax
-//
-// Default: [ 'docs/core/plugins/*.md' ]
-export const plugin_paths = []
-
-// Enable additional labels to support in Dovecot-specific markdown
-// processing (i.e. [[xyz,...]]).
-export const markdown_extend = {
-
- // Init function. Return value is configuration options to add to markdown
- // object.
- // init: () => { return {} },
-
- // Opening tag function. Returns opening tag.
- // open: (mode, parts, opts, env) => { return '' },
-
- // Body function. Returns body text.
- // body: (mode, env) => { return '' },
-
- // Close tag function. Returns closing tag.
- // close: (mode, env) => { return '' },
-
-}
diff --git a/README.md b/README.md
index 18a6b4c34..28f3aba27 100644
--- a/README.md
+++ b/README.md
@@ -158,6 +158,71 @@ The data files live in the base `/data` directory. Each file attempts to be
self-documenting, but they are all essentially large JSON objects. Developers
should need to know basically no JavaScript to be able to edit the files.
+### VitePress Path Overrides
+
+Various paths used for VitePress static generation can be overridden using
+the `dovecot` object in the `themeConfig` VitePress configuration setting.
+
+#### data_paths
+
+Allows custom mapping of data sources.
+
+It is used by the VitePress data loaders to determine what data to
+load for export.
+
+Keys are data identifiers, Values are location RELATIVE TO `/lib`
+DIRECTORY.
+
+#### man_includes
+
+A list of additional paths (other than the `include/` directory of the man
+folder) where include files can live.
+
+#### man_paths
+
+A listing of paths containing man files.
+Paths are relative to project base.
+
+Supports fast-glob: https://github.com/mrmlnc/fast-glob#pattern-syntax
+
+#### markdown_extend
+
+An object containing callbacks that enable additional labels to support in
+Dovecot-specific markdown processing (i.e. [[xyz,...]]).
+
+##### open
+
+Opening tag function. Returns opening tag.
+
+Example: `open: (mode, parts, opts, env) => { return '' }`
+
+##### body
+
+Body function. Returns body text.
+
+Example: `body: (mode, env) => { return '' }`
+
+##### close
+
+Close tag function. Returns closing tag.
+
+Example: `close: (mode, env) => { return '' }`
+
+#### plugin_paths
+
+A listing of paths containing plugin files.
+Paths are relative to project base.
+
+Supports fast-glob: https://github.com/mrmlnc/fast-glob#pattern-syntax
+
+#### watch_paths
+
+An array of file patterns to watch to refresh data loaders in dev mode.
+See: https://vitepress.dev/guide/data-loading#data-from-local-files
+Paths are relative to project base.
+
+Supports fast-glob: https://github.com/mrmlnc/fast-glob#pattern-syntax
+
### Dovecot Markdown Extensions
Markdown has been extended to allow various Dovecot-specific tasks to be
diff --git a/lib/data/doveadm.data.js b/lib/data/doveadm.data.js
index c76fd5e93..1c54c7e47 100644
--- a/lib/data/doveadm.data.js
+++ b/lib/data/doveadm.data.js
@@ -1,6 +1,6 @@
import { doveadm_arg_types, doveadm_flag_types, getDoveadmCmdLine } from '../doveadm.js'
import { getVitepressMd } from '../markdown.js'
-import { loadData, normalizeArrayData, watchFiles } from '../utility.js'
+import { addWatchPaths, loadData, normalizeArrayData } from '../utility.js'
import slugify from '@sindresorhus/slugify'
const doveadm_userargs = {
@@ -147,11 +147,10 @@ async function normalizeDoveadm(doveadm) {
}
}
-export default {
- watch: await watchFiles(),
+export default addWatchPaths({
async load() {
return await normalizeDoveadm(
structuredClone((await loadData('doveadm')).doveadm)
)
}
-}
+})
diff --git a/lib/data/event_categories.data.js b/lib/data/event_categories.data.js
index 4e009ab4f..37edcb12a 100644
--- a/lib/data/event_categories.data.js
+++ b/lib/data/event_categories.data.js
@@ -1,5 +1,5 @@
import { getVitepressMd } from '../markdown.js'
-import { loadData, watchFiles } from '../utility.js'
+import { addWatchPaths, loadData } from '../utility.js'
async function normalizeEventCategories(categories) {
const md = await getVitepressMd()
@@ -11,11 +11,10 @@ async function normalizeEventCategories(categories) {
return categories
}
-export default {
- watch: await watchFiles(),
+export default addWatchPaths({
async load() {
return await normalizeEventCategories(
structuredClone((await loadData('event_categories')).categories)
)
}
-}
+})
diff --git a/lib/data/event_reasons.data.js b/lib/data/event_reasons.data.js
index 9ddd930ac..5f3cdb11f 100644
--- a/lib/data/event_reasons.data.js
+++ b/lib/data/event_reasons.data.js
@@ -1,5 +1,5 @@
import { getVitepressMd } from '../markdown.js'
-import { loadData, watchFiles } from '../utility.js'
+import { addWatchPaths, loadData } from '../utility.js'
async function normalizeEventReasons(reasons) {
const md = await getVitepressMd()
@@ -11,11 +11,10 @@ async function normalizeEventReasons(reasons) {
return reasons
}
-export default {
- watch: await watchFiles(),
+export default addWatchPaths({
async load() {
return await normalizeEventReasons(
structuredClone((await loadData('event_reasons')).reasons)
)
}
-}
+})
diff --git a/lib/data/events.data.js b/lib/data/events.data.js
index 82696b976..e3446f255 100644
--- a/lib/data/events.data.js
+++ b/lib/data/events.data.js
@@ -1,4 +1,4 @@
-import { loadData, normalizeArrayData, watchFiles } from '../utility.js'
+import { addWatchPaths, loadData, normalizeArrayData } from '../utility.js'
import { getVitepressMd } from '../markdown.js'
/* Take the events list and normalize entries and process inheritance. */
@@ -124,8 +124,7 @@ async function normalizeEvents(events, global_inherits, inherits) {
return events
}
-export default {
- watch: await watchFiles(),
+export default addWatchPaths({
async load() {
const data = await loadData('events')
return await normalizeEvents(
@@ -134,4 +133,4 @@ export default {
structuredClone(data.inherits)
)
}
-}
+})
diff --git a/lib/data/lua.data.js b/lib/data/lua.data.js
index 9490c5c87..363982f5f 100644
--- a/lib/data/lua.data.js
+++ b/lib/data/lua.data.js
@@ -1,5 +1,5 @@
import { getVitepressMd } from '../markdown.js'
-import { loadData, watchFiles } from '../utility.js'
+import { addWatchPaths, loadData } from '../utility.js'
async function normalizeLuaConstants(lua) {
const md = await getVitepressMd()
@@ -62,8 +62,7 @@ async function normalizeLuaVariables(lua) {
return out
}
-export default {
- watch: await watchFiles(),
+export default addWatchPaths({
async load() {
const data = await(loadData('lua'))
@@ -73,4 +72,4 @@ export default {
variables: await normalizeLuaVariables(data.lua_variables)
}
}
-}
+})
diff --git a/lib/data/settings.data.js b/lib/data/settings.data.js
index 9311ee2a4..7df26c155 100644
--- a/lib/data/settings.data.js
+++ b/lib/data/settings.data.js
@@ -1,4 +1,4 @@
-import { loadData, normalizeArrayData, watchFiles } from '../utility.js'
+import { addWatchPaths, loadData, normalizeArrayData } from '../utility.js'
import { getVitepressMd } from '../markdown.js'
async function normalizeSettings(settings) {
@@ -69,11 +69,10 @@ async function normalizeSettings(settings) {
return data
}
-export default {
- watch: await watchFiles(),
+export default addWatchPaths({
async load() {
return await normalizeSettings(
structuredClone((await loadData('settings')).settings)
)
}
-}
+})
diff --git a/lib/markdown.js b/lib/markdown.js
index 9949e0ea1..60feacbee 100644
--- a/lib/markdown.js
+++ b/lib/markdown.js
@@ -6,34 +6,7 @@ import { createMarkdownRenderer } from 'vitepress'
import { frontmatterIter, loadData, manFiles, markdownExtension, pluginFiles, resolveURL } from './utility.js'
let md_conf = null
-export async function initDovecotMd(base) {
- if (md_conf !== null) {
- return
- }
-
- md_conf = {
- ...{
- base: base,
- doveadm: (await loadData('doveadm')).doveadm,
- events: (await loadData('events')).events,
- linkoverrides: (await loadData('links_overrides')).links_overrides,
- man: (await manFiles()).flatMap((x) => {
- return fg.sync(x).map((y) => {
- const str = path.basename(y)
- return str.substring(0, str.indexOf('.'))
- })
- }),
- plugins: (await pluginFiles()).flatMap((x) =>
- fg.sync(x).map((y) => path.basename(y, '.md'))
- ),
- settings: (await loadData('settings')).settings,
- updates: (await loadData('updates')).updates
- },
- ...(await markdownExtension())
- }
-}
-
-export function dovecotMdExtend(md) {
+export async function dovecotMdExtend(md) {
md.use(containerPlugin, 'todo', {
render: function(tokens, idx) {
if (tokens[idx].nesting === 1) {
@@ -46,7 +19,14 @@ export function dovecotMdExtend(md) {
md.use(deflistPlugin)
if (md_conf === null) {
- throw new Error('Must call initDovecotMd() before calling this function!')
+ md_conf = {
+ base: globalThis.VITEPRESS_CONFIG.site.base,
+ doveadm: (await loadData('doveadm')).doveadm,
+ events: (await loadData('events')).events,
+ linkoverrides: (await loadData('links_overrides')).links_overrides,
+ settings: (await loadData('settings')).settings,
+ updates: (await loadData('updates')).updates
+ }
}
md.use(dovecot_markdown, md_conf)
@@ -58,8 +38,7 @@ export async function getVitepressMd() {
if (vitepress_md === null) {
const config = globalThis.VITEPRESS_CONFIG
- await initDovecotMd(config.site.base)
- vitepress_md = dovecotMdExtend(await createMarkdownRenderer(
+ vitepress_md = await dovecotMdExtend(await createMarkdownRenderer(
config.srcDir,
config.markdown,
config.site.base,
@@ -219,6 +198,8 @@ function dovecot_markdown(md, opts) {
hash = parts[2] ? parts[2] : false;
env.args = parts[3] ? parts[3] : 1;
+ initManFiles()
+
if (!opts.man.includes(env.inner)) {
handle_error('man link missing: ' + env.inner)
return ''
@@ -233,6 +214,8 @@ function dovecot_markdown(md, opts) {
env.args = parts[2] ? parts[2] : undefined
const plugin = env.inner.replaceAll('-', '_')
+ initPluginFiles()
+
if (!opts.plugins.includes(plugin)) {
handle_error('plugin link missing: ' + env.inner)
return ''
@@ -296,6 +279,8 @@ function dovecot_markdown(md, opts) {
opts.base) + '">'
default:
+ initMarkdownExtend()
+
return handle_default(mode,
opts.markdown?.open?.(mode, parts, opts, env))
}
@@ -426,6 +411,31 @@ function dovecot_markdown(md, opts) {
opts.dovecotlinks = { ...links, ...opts.linkoverrides }
}
+ function initManFiles() {
+ if (!opts.man) {
+ opts.man = manFiles().flatMap((x) => {
+ return fg.sync(x).map((y) => {
+ const str = path.basename(y)
+ return str.substring(0, str.indexOf('.'))
+ })
+ })
+ }
+ }
+
+ function initMarkdownExtend() {
+ if (!opts.markdown) {
+ opts.markdown = markdownExtension()
+ }
+ }
+
+ function initPluginFiles() {
+ if (!opts.plugins) {
+ opts.plugins = pluginFiles().flatMap((x) =>
+ fg.sync(x).map((y) => path.basename(y, '.md'))
+ )
+ }
+ }
+
md.inline.ruler.after('emphasis', 'dovecot_brackets', process_brackets)
md.renderer.rules.dovecot_open = dovecot_open
md.renderer.rules.dovecot_body = dovecot_body
diff --git a/lib/utility.js b/lib/utility.js
index dbb5d10cf..e6587952a 100644
--- a/lib/utility.js
+++ b/lib/utility.js
@@ -9,8 +9,6 @@ import { fileURLToPath } from 'url'
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
-const local_conf_path = __dirname + '/../.vitepress/local.js'
-
export function normalizeArrayData(data, keys) {
for (const [k, v] of Object.entries(data)) {
if (v) {
@@ -28,34 +26,13 @@ export function normalizeArrayData(data, keys) {
return data
}
-let local_conf = null
-async function loadLocalConf() {
- if (local_conf === null) {
- if (fs.existsSync(local_conf_path)) {
- local_conf = await import(local_conf_path)
- } else {
- local_conf = false
- }
- }
-
- return local_conf
-}
-
-export async function markdownExtension() {
- const lconf = await loadLocalConf()
-
- return {
- ...{
- markdown: lconf.markdown_extend ?? {},
- },
- ...(lconf.markdown_extend?.init?.() ?? {})
- }
+export function markdownExtension() {
+ return globalThis.VITEPRESS_CONFIG.userConfig.themeConfig.dovecot?.markdown_extend
+ ?? {}
}
export async function loadData(id) {
- /* Check for config override file. */
- const lconf = await loadLocalConf()
- const path = lconf?.data_paths?.[id] ??
+ const path = globalThis.VITEPRESS_CONFIG.userConfig.themeConfig.dovecot?.data_paths?.[id] ??
('../data/' + id + '.js')
try {
@@ -66,34 +43,28 @@ export async function loadData(id) {
}
}
-export async function watchFiles() {
- /* Check for config override file. */
- const lconf = await loadLocalConf()
-
- return lconf?.watch_paths ??
- [ 'docs/**/*.md', 'docs/**/*.inc', 'data/**/*' ]
+export function addWatchPaths(obj) {
+ return {
+ ...obj,
+ ...{
+ watch: globalThis.VITEPRESS_CONFIG.userConfig.themeConfig.dovecot?.watch_paths ?? [ 'docs/**/*.md', 'docs/**/*.inc', 'data/**/*' ]
+ }
+ }
}
-export async function manFiles() {
- /* Check for config override file. */
- const lconf = await loadLocalConf()
-
- return lconf?.man_paths ??
- [ 'docs/core/man/*.[[:digit:]].md' ]
+export function manFiles() {
+ return globalThis.VITEPRESS_CONFIG.userConfig.themeConfig.dovecot?.man_paths
+ ?? [ 'docs/core/man/*.[[:digit:]].md' ]
}
-export async function manIncludes() {
- const lconf = await loadLocalConf()
- return lconf?.man_includes ??
- [ 'docs/core/man/include/*.inc' ]
+export function manIncludes() {
+ return globalThis.VITEPRESS_CONFIG.userConfig.themeConfig.dovecot?.man_includes
+ ?? [ 'docs/core/man/include/*.inc' ]
}
-export async function pluginFiles() {
- /* Check for config override file. */
- const lconf = await loadLocalConf()
-
- return lconf?.plugin_paths ??
- [ 'docs/core/plugins/*.md' ]
+export function pluginFiles() {
+ return globalThis.VITEPRESS_CONFIG.userConfig.themeConfig.dovecot?.plugin_paths
+ ?? [ 'docs/core/plugins/*.md' ]
}
export function getExcludes(srcDirs = [ 'docs' ]) {
diff --git a/util/generate_man.js b/util/generate_man.js
index a9e566775..d93fb270d 100755
--- a/util/generate_man.js
+++ b/util/generate_man.js
@@ -126,8 +126,8 @@ const main = async (component, outPath) => {
}
/* Generate list of man files. */
- const files = (await manFiles()).flatMap((x) => fg.sync(x))
- const includes = (await manIncludes()).flatMap((x) => fg.sync(x))
+ const files = manFiles().flatMap((x) => fg.sync(x))
+ const includes = manIncludes().flatMap((x) => fg.sync(x))
/* Get hash of last git commit. */
const gitHash = gitCommitInfo().shortHash