Skip to content

Commit 9af60ce

Browse files
slusarzcmouse
authored andcommitted
config.js: Use internal VitePress path parsing
Two Dovecot-specific documentation features need access to the list of pages, and their mapping rewrite paths (files to URL path). We previously created our system to handle this, but this is already done internally by VitePress, so take advantage of that. This does require one change to delay dovecot link parsing, for bootstrapping reasons (the page/mapping list only becomes available after the defineConfig() call is completed in .vitepress/config.js).
1 parent b719f8e commit 9af60ce

File tree

6 files changed

+68
-107
lines changed

6 files changed

+68
-107
lines changed

.vitepress/config.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
import gitCommitInfo from 'git-commit-info'
2-
import path from 'path'
32
import { defineConfig } from 'vitepress'
43
import { pagefindPlugin } from 'vitepress-plugin-pagefind'
54
import { generateSidebar } from 'vitepress-sidebar'
65
import { dovecotMdExtend, initDovecotMd } from '../lib/markdown.js'
7-
import { frontmatterIter } from '../lib/utility.js'
6+
import { getExcludes } from '../lib/utility.js'
87

98
const base = '/2.4'
109

11-
// No need to include the "dummy" index files, used to build titles
12-
// for the sidebar, into the final documentation bundles
13-
const excludes = []
14-
await frontmatterIter(function (f, data) {
15-
if (data.exclude) {
16-
excludes.push(path.relative('docs/', f))
17-
}
18-
})
19-
2010
// Need to bootstrap configuration for Dovecot markdown driver (specifically,
2111
// loading all data files to allow existence checking), or else the markdown
2212
// processing will begin before Dovecot link markup is enabled
@@ -27,8 +17,11 @@ export default defineConfig({
2717
description: "Dovecot CE Documentation",
2818
lang: "en-us",
2919

30-
srcDir: "docs",
31-
srcExclude: excludes,
20+
srcDir: ".",
21+
srcExclude: [ '*.md' ].concat(getExcludes()),
22+
rewrites: {
23+
'docs/:path(.*)': ':path',
24+
},
3225

3326
base: base,
3427
sitemap: {

.vitepress/local.js.dist

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ export const data_paths = {
1313
// doveadm: '../data/doveadm.js',
1414
}
1515

16-
// A listing of source files (.md) for the documentation.
17-
// Paths are relative to project base.
18-
//
19-
// Supports fast-glob: https://github.com/mrmlnc/fast-glob#pattern-syntax
20-
//
21-
// Default: [ 'docs/**/*.md' ]
22-
export const source_paths = []
23-
2416
// A listing of files to watch to refresh data loaders in dev mode.
2517
// See: https://vitepress.dev/guide/data-loading#data-from-local-files
2618
// Paths are relative to project base.
@@ -30,14 +22,6 @@ export const source_paths = []
3022
// Default: [ 'docs/**/*.md', 'docs/**/*.inc', 'data/**/*' ]
3123
export const watch_paths = []
3224

33-
// A listing of source path translations.
34-
//
35-
// This is the same format as VitePress' 'rewrite' setting:
36-
// https://vitepress.dev/reference/site-config#rewrites
37-
//
38-
// Default: { 'docs/:path(.*)': ':path' }
39-
export const source_path_translations = {}
40-
4125
// A listing of paths containing man files.
4226
// Paths are relative to project base.
4327
//

lib/markdown.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fg from 'fast-glob'
33
import deflistPlugin from 'markdown-it-deflist'
44
import path from 'path'
55
import { createMarkdownRenderer } from 'vitepress'
6-
import { loadData, loadDovecotLinks, manFiles, markdownExtension, pluginFiles, resolveURL } from './utility.js'
6+
import { frontmatterIter, loadData, manFiles, markdownExtension, pluginFiles, resolveURL } from './utility.js'
77

88
let md_conf = null
99
export async function initDovecotMd(base) {
@@ -15,8 +15,8 @@ export async function initDovecotMd(base) {
1515
...{
1616
base: base,
1717
doveadm: (await loadData('doveadm')).doveadm,
18-
dovecotlinks: await loadDovecotLinks(base),
1918
events: (await loadData('events')).events,
19+
linkoverrides: (await loadData('links_overrides')).links_overrides,
2020
man: (await manFiles()).flatMap((x) => {
2121
return fg.sync(x).map((y) => {
2222
const str = path.basename(y)
@@ -202,6 +202,8 @@ function dovecot_markdown(md, opts) {
202202
let url = '#'
203203
env.inner = false
204204

205+
initDovecotLinks()
206+
205207
if (!opts.dovecotlinks[parts[1]]) {
206208
handle_error('Dovecot link missing: ' + parts[1])
207209
return '<a>'
@@ -388,6 +390,42 @@ function dovecot_markdown(md, opts) {
388390
console.error(msg)
389391
}
390392

393+
function initDovecotLinks() {
394+
if (opts.dovecotlinks) {
395+
return
396+
}
397+
398+
const links = {}
399+
const rewrites = globalThis.VITEPRESS_CONFIG.rewrites.map
400+
401+
frontmatterIter(Object.keys(rewrites), function (f, data) {
402+
if (!data.dovecotlinks) {
403+
return
404+
}
405+
406+
for (const [k, v] of Object.entries(data.dovecotlinks)) {
407+
if (links[k]) {
408+
throw new Error("Duplicate Dovecot Link key: " + k)
409+
}
410+
411+
links[k] = {
412+
url: resolveURL(rewrites[f].substring(0, rewrites[f].lastIndexOf('.')) + '.html', opts.base)
413+
}
414+
415+
if ((typeof v) == 'object') {
416+
links[k].text = v.text
417+
if (v.hash) {
418+
links[k].url += '#' + v.hash
419+
}
420+
} else {
421+
links[k].text = v
422+
}
423+
}
424+
})
425+
426+
opts.dovecotlinks = { ...links, ...opts.linkoverrides }
427+
}
428+
391429
md.inline.ruler.after('emphasis', 'dovecot_brackets', process_brackets)
392430
md.renderer.rules.dovecot_open = dovecot_open
393431
md.renderer.rules.dovecot_body = dovecot_body

lib/utility.js

Lines changed: 22 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
import fg from 'fast-glob'
44
import fs from 'fs'
55
import matter from 'gray-matter'
6-
import { dirname } from 'path';
7-
import { compile, match } from 'path-to-regexp'
8-
import { fileURLToPath } from 'url';
6+
import { dirname } from 'path'
7+
import { fileURLToPath } from 'url'
98

109
const __filename = fileURLToPath(import.meta.url);
1110
const __dirname = dirname(__filename);
@@ -67,13 +66,6 @@ export async function loadData(id) {
6766
}
6867
}
6968

70-
export async function sourceFiles() {
71-
/* Check for config override file. */
72-
const lconf = await loadLocalConf()
73-
74-
return lconf?.source_paths ?? [ 'docs/**/*.md' ]
75-
}
76-
7769
export async function watchFiles() {
7870
/* Check for config override file. */
7971
const lconf = await loadLocalConf()
@@ -104,68 +96,33 @@ export async function pluginFiles() {
10496
[ 'docs/core/plugins/*.md' ]
10597
}
10698

107-
export async function frontmatterIter(callback) {
108-
const sf = await sourceFiles()
109-
const files = sf.flatMap((x) => fg.sync(x))
110-
111-
/* Check for config override file. */
112-
const lconf = await loadLocalConf()
113-
const spt_conf = lconf?.source_path_translations ??
114-
{ 'docs/:path': ':path' }
115-
116-
const spt = Object.entries(spt_conf).map(([from, to]) => ({
117-
toPath: compile(`/${to}`, { validate: false }),
118-
matchUrl: match(from)
119-
}))
99+
export function getExcludes(srcDirs = [ 'docs' ]) {
100+
const excludes = []
120101

121-
for (let f of files) {
122-
const str = fs.readFileSync(f, 'utf8')
123-
const data = matter(str).data
124-
125-
for (const { matchUrl, toPath } of spt) {
126-
const res = matchUrl(f)
127-
if (res) {
128-
f = toPath(res.params).slice(1)
129-
break
102+
frontmatterIter(
103+
srcDirs.flatMap((x) => fg.sync(x + '/**/*.md')),
104+
function (f, data) {
105+
/* Exclude all pages with "exclude" frontmatter present. */
106+
if (data.exclude) {
107+
excludes.push(f)
130108
}
131109
}
110+
)
132111

133-
callback(f, data)
134-
}
112+
return excludes
135113
}
136114

137-
export async function loadDovecotLinks(base) {
138-
const links = {}
139-
140-
await frontmatterIter(function (f, data) {
141-
if (!data.dovecotlinks) {
142-
return
143-
}
144-
145-
for (const [k,v] of Object.entries(data.dovecotlinks)) {
146-
if (links[k]) {
147-
throw new Error("Duplicate Dovecot Link key: " + k)
148-
}
149-
150-
links[k] = {
151-
url: resolveURL(f.substring(0, f.lastIndexOf('.')) + '.html', base)
152-
}
153-
154-
if ((typeof v) == 'object') {
155-
links[k].text = v.text
156-
if (v.hash) {
157-
links[k].url += '#' + v.hash
158-
}
159-
} else {
160-
links[k].text = v
161-
}
115+
export function frontmatterIter(files, callback) {
116+
for (let f of files) {
117+
try {
118+
const str = fs.readFileSync(f, 'utf8')
119+
callback(f, matter(str).data)
120+
} catch (err) {
121+
/* Ignore file not exist errors, since they can occur for
122+
* dynamically generated paths (e.g. Release Notes). */
123+
if (err.code !== 'ENOENT') throw err
162124
}
163-
})
164-
165-
const data = await loadData('links_overrides')
166-
167-
/* Merge the two lists together. */
168-
return { ...links, ...data.links_overrides }
125+
}
169126
}
170127

171128
export function resolveURL(url, base) {

package-lock.json

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
"markdown-it-deflist": "^3.0.0",
1010
"markdown-it-mathjax3": "^4.3.2",
1111
"pagefind": "^1.1.1",
12-
"path-to-regexp": "^8.1.0",
1312
"pdc": "^0.2.3",
1413
"remark-definition-list": "^2.0.0",
1514
"remark-man": "^9.0.0",

0 commit comments

Comments
 (0)