From 0c12d5e74cde9b64b877a51ff251a8b6be557e36 Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Tue, 5 Nov 2024 17:58:20 -0700 Subject: [PATCH] utility.js: Make loadData() a sync function We always want loadData() to be a blocking call, as we use that code to read the data JS files so that we can do further processing. However, since data JS paths are dynamic, we must use import() which is required to be an async call. For bootstrapping purposes, this means there is no way to block markdown processing to begin before the Dovecot markdown plugin is ready. This is because markdown-it code MUST NOT use async code, so we need to load all the data (via loadData()) before it can be initialized. Dovecot markdown code also requires VitePress paths to be available in global config object, so the only place we can realistically add the Dovecot markdown processing is via the "config" markdown option. Unfortunately, the internal VitePress code does not call this function with await, so there is simply no way to block processing via this mechanism. Solution- the import-sync nodejs package allows import behavior to be done synchronously. This allows us to no longer need to pre-configure the dovecot markdown plugin, as the loadData() calls can now be called on-demand inside of the plugin. Thus, we can guarantee that the Dovecot markdown plugin is initialized at the time the first page is processed by VitePress now. --- .vitepress/config.js | 2 +- lib/data/doveadm.data.js | 2 +- lib/data/event_categories.data.js | 2 +- lib/data/event_reasons.data.js | 2 +- lib/data/events.data.js | 2 +- lib/data/lua.data.js | 2 +- lib/data/settings.data.js | 2 +- lib/markdown.js | 60 ++++++++++++++++++++++--------- lib/utility.js | 5 +-- package-lock.json | 19 ++++++++++ package.json | 1 + 11 files changed, 73 insertions(+), 26 deletions(-) diff --git a/.vitepress/config.js b/.vitepress/config.js index 89ea56ba9..6228ebeb2 100644 --- a/.vitepress/config.js +++ b/.vitepress/config.js @@ -131,7 +131,7 @@ export default defineConfig({ }, markdown: { - config: async (md) => await dovecotMdExtend(md), + config: (md) => dovecotMdExtend(md), image: { lazyLoading: true, }, diff --git a/lib/data/doveadm.data.js b/lib/data/doveadm.data.js index 1c54c7e47..c60fe6ab3 100644 --- a/lib/data/doveadm.data.js +++ b/lib/data/doveadm.data.js @@ -150,7 +150,7 @@ async function normalizeDoveadm(doveadm) { export default addWatchPaths({ async load() { return await normalizeDoveadm( - structuredClone((await loadData('doveadm')).doveadm) + structuredClone(loadData('doveadm').doveadm) ) } }) diff --git a/lib/data/event_categories.data.js b/lib/data/event_categories.data.js index 37edcb12a..7a72fdbb3 100644 --- a/lib/data/event_categories.data.js +++ b/lib/data/event_categories.data.js @@ -14,7 +14,7 @@ async function normalizeEventCategories(categories) { export default addWatchPaths({ async load() { return await normalizeEventCategories( - structuredClone((await loadData('event_categories')).categories) + structuredClone(loadData('event_categories').categories) ) } }) diff --git a/lib/data/event_reasons.data.js b/lib/data/event_reasons.data.js index 5f3cdb11f..15f8f2abf 100644 --- a/lib/data/event_reasons.data.js +++ b/lib/data/event_reasons.data.js @@ -14,7 +14,7 @@ async function normalizeEventReasons(reasons) { export default addWatchPaths({ async load() { return await normalizeEventReasons( - structuredClone((await loadData('event_reasons')).reasons) + structuredClone(loadData('event_reasons').reasons) ) } }) diff --git a/lib/data/events.data.js b/lib/data/events.data.js index e3446f255..f9c4df88b 100644 --- a/lib/data/events.data.js +++ b/lib/data/events.data.js @@ -126,7 +126,7 @@ async function normalizeEvents(events, global_inherits, inherits) { export default addWatchPaths({ async load() { - const data = await loadData('events') + const data = loadData('events') return await normalizeEvents( structuredClone(data.events), structuredClone(data.global_inherits), diff --git a/lib/data/lua.data.js b/lib/data/lua.data.js index 363982f5f..1caf96cb2 100644 --- a/lib/data/lua.data.js +++ b/lib/data/lua.data.js @@ -64,7 +64,7 @@ async function normalizeLuaVariables(lua) { export default addWatchPaths({ async load() { - const data = await(loadData('lua')) + const data = loadData('lua') return { constants: await normalizeLuaConstants(data.lua_constants), diff --git a/lib/data/settings.data.js b/lib/data/settings.data.js index 7df26c155..24f7213b9 100644 --- a/lib/data/settings.data.js +++ b/lib/data/settings.data.js @@ -72,7 +72,7 @@ async function normalizeSettings(settings) { export default addWatchPaths({ async load() { return await normalizeSettings( - structuredClone((await loadData('settings')).settings) + structuredClone(loadData('settings').settings) ) } }) diff --git a/lib/markdown.js b/lib/markdown.js index 43c26d879..810ff7710 100644 --- a/lib/markdown.js +++ b/lib/markdown.js @@ -5,8 +5,7 @@ import path from 'path' import { createMarkdownRenderer } from 'vitepress' import { dovecotSetting, frontmatterIter, loadData } from './utility.js' -let md_conf = null -export async function dovecotMdExtend(md) { +export function dovecotMdExtend(md) { md.use(containerPlugin, 'todo', { render: function(tokens, idx) { if (tokens[idx].nesting === 1) { @@ -17,18 +16,7 @@ export async function dovecotMdExtend(md) { } }) md.use(deflistPlugin) - - if (md_conf === null) { - 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) + md.use(dovecot_markdown) return md } @@ -38,7 +26,7 @@ export async function getVitepressMd() { if (vitepress_md === null) { const config = globalThis.VITEPRESS_CONFIG - vitepress_md = await dovecotMdExtend(await createMarkdownRenderer( + vitepress_md = dovecotMdExtend(await createMarkdownRenderer( config.srcDir, config.markdown, config.site.base, @@ -52,7 +40,7 @@ export async function getVitepressMd() { /* This is a dovecot markdown extension to support the "[[...]]" syntax. * Much of this is copied from existing markdown-it plugins. See, e.g., * https://github.com/markdown-it/markdown-it-sub/blob/master/index.mjs */ -function dovecot_markdown(md, opts) { +function dovecot_markdown(md) { function process_brackets(state, silent) { const max = state.posMax const start = state.pos @@ -142,6 +130,8 @@ function dovecot_markdown(md, opts) { let page = mode switch (mode) { case 'doveadm': + initDoveadm() + if (!opts.doveadm[env.inner]) { if (!Object.values(opts.doveadm).find((x) => (x.man == 'doveadm-' + env.inner))) { handle_error('doveadm link missing: ' + env.inner) @@ -151,6 +141,8 @@ function dovecot_markdown(md, opts) { break case 'event': + initEvents() + if (!opts.events[env.inner]) { handle_error('event link missing: ' + env.inner) return '' @@ -160,6 +152,8 @@ function dovecot_markdown(md, opts) { case 'setting': case 'setting_text': + initSettings() + /* Settings names can have brackets, so we need to unescape * input for purposes of searching settings keys. */ const search_str = env.inner.replaceAll('>', '>') @@ -293,6 +287,8 @@ function dovecot_markdown(md, opts) { case 'changed': case 'deprecated': case 'removed': + initUpdates() + if (!opts.updates[env.args]) { handle_error('Missing updates entry for: ' + env.args) return env.args @@ -373,6 +369,12 @@ function dovecot_markdown(md, opts) { console.error(msg) } + function initDoveadm() { + if (!opts.doveadm) { + opts.doveadm = loadData('doveadm').doveadm + } + } + function initDovecotLinks() { if (opts.dovecotlinks) { return @@ -406,7 +408,15 @@ function dovecot_markdown(md, opts) { } }) - opts.dovecotlinks = { ...links, ...opts.linkoverrides } + opts.dovecotlinks = { + ...links, ...(loadData('links_overrides').links_overrides) + } + } + + function initEvents() { + if (!opts.events) { + opts.events = loadData('events').events + } } function initManFiles() { @@ -434,6 +444,18 @@ function dovecot_markdown(md, opts) { } } + function initSettings() { + if (!opts.settings) { + opts.settings = loadData('settings').settings + } + } + + function initUpdates() { + if (!opts.updates) { + opts.updates = loadData('updates').updates + } + } + function resolveURL(url) { if (!('url_rewrite' in opts)) { opts.url_rewrite = dovecotSetting('url_rewrite') @@ -445,6 +467,10 @@ function dovecot_markdown(md, opts) { return (opts.url_rewrite) ? opts.url_rewrite(new_url) : new_url } + const opts = { + base: globalThis.VITEPRESS_CONFIG.site.base + } + 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 185f5385d..07352df8f 100644 --- a/lib/utility.js +++ b/lib/utility.js @@ -3,6 +3,7 @@ import fg from 'fast-glob' import fs from 'fs' import matter from 'gray-matter' +import importSync from 'import-sync' import { dirname } from 'path' import { fileURLToPath } from 'url' @@ -26,12 +27,12 @@ export function normalizeArrayData(data, keys) { return data } -export async function loadData(id) { +export function loadData(id) { const path = globalThis.VITEPRESS_CONFIG.userConfig.themeConfig.dovecot?.data_paths?.[id] ?? ('../data/' + id + '.js') try { - return await import(__dirname + '/' + path) + return importSync(__dirname + '/' + path) } catch (e) { throw new Error('Unable to import module (' + __dirname + '/' + path + '):' + e) diff --git a/package-lock.json b/package-lock.json index d1f9ddd21..26511a0d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dayjs": "^1.11.13", "fast-glob": "^3.3.2", "git-commit-info": "^2.0.2", + "import-sync": "^2.2.2", "markdown-it-container": "^4.0.0", "markdown-it-deflist": "^3.0.0", "markdown-it-mathjax3": "^4.3.2", @@ -799,6 +800,15 @@ "node": ">=12" } }, + "node_modules/@httptoolkit/esm": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@httptoolkit/esm/-/esm-3.3.0.tgz", + "integrity": "sha512-gS+TH14750cveYP5p2q/oLyjVV5+qRFZMOpsPzK1KrgacqKc6RLDmt3ioGaMjsn1aianQbROkl4QXDJmO5swPA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2888,6 +2898,15 @@ "node": ">=8.12.0" } }, + "node_modules/import-sync": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/import-sync/-/import-sync-2.2.2.tgz", + "integrity": "sha512-MQ+8aB1LZu0dHqTmpkOJ5DLCqQNVNqbMrH/S9nvHChUSMsAlX6R3ydQhX3R5/YfCU6+qU3cbASiX59hJisMKgg==", + "dev": true, + "dependencies": { + "@httptoolkit/esm": "^3.3.0" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", diff --git a/package.json b/package.json index d7f5e6838..40f510de4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dayjs": "^1.11.13", "fast-glob": "^3.3.2", "git-commit-info": "^2.0.2", + "import-sync": "^2.2.2", "markdown-it-container": "^4.0.0", "markdown-it-deflist": "^3.0.0", "markdown-it-mathjax3": "^4.3.2",