|
| 1 | +import { getVitepressMd } from './markdown.js' |
| 2 | +import { loadData, normalizeArrayData } from './utility.js' |
| 3 | + |
1 | 4 | /* List of Dovecot settings value types. */
|
2 | 5 | export const setting_types = {
|
3 | 6 | BOOLEAN: {
|
@@ -91,3 +94,137 @@ export const setting_types = {
|
91 | 94 | url: '[[link,settings_groups_includes]]'
|
92 | 95 | }
|
93 | 96 | }
|
| 97 | + |
| 98 | +function wrapInTag(str, tag) { |
| 99 | + if (tag) |
| 100 | + return `<${tag}>${str}</${tag}>` |
| 101 | + return str |
| 102 | +} |
| 103 | + |
| 104 | +/* Resolve links in given parameter. If no singular link is detected, it is |
| 105 | + * rendered with the provided tag surrounding the value. */ |
| 106 | +function normalizeString(md, str, tag = null) { |
| 107 | + let out = '' |
| 108 | + |
| 109 | + if (str) { |
| 110 | + /* FIXME: This makes the following .startsWith() call work, |
| 111 | + * but might lead to type-specific errors, e.g. String({}) |
| 112 | + * yields '[object Object]'. This still needs to be verified |
| 113 | + * manually. */ |
| 114 | + out = String(str) |
| 115 | + if (!out.startsWith('[[')) { |
| 116 | + out = wrapInTag(out, tag) |
| 117 | + } |
| 118 | + return md.renderInline(out) |
| 119 | + } |
| 120 | + |
| 121 | + return str |
| 122 | +} |
| 123 | + |
| 124 | +/* Mark a plain item as an inter-settings dovecot-specific link, i.e. |
| 125 | + * [[setting,<item>]]. Don't process already marked links. */ |
| 126 | +function normalizeArray(md, arr) { |
| 127 | + if (arr) { |
| 128 | + return arr.map(entry => ( |
| 129 | + md.renderInline( |
| 130 | + entry.startsWith('[[') |
| 131 | + ? entry |
| 132 | + : `[[setting,${entry}]]` |
| 133 | + ) |
| 134 | + )) |
| 135 | + } |
| 136 | + |
| 137 | + return arr |
| 138 | +} |
| 139 | + |
| 140 | +async function normalizeSettings(settings) { |
| 141 | + const data = normalizeArrayData( |
| 142 | + settings, |
| 143 | + ['dependencies', 'seealso', 'tags', 'values_enum'] |
| 144 | + ) |
| 145 | + |
| 146 | + const md = await getVitepressMd() |
| 147 | + |
| 148 | + for (const [k, v] of Object.entries(data)) { |
| 149 | + if (!v) { |
| 150 | + delete data[k] |
| 151 | + continue |
| 152 | + } |
| 153 | + |
| 154 | + /* Style default entry. */ |
| 155 | + if (!!v.default) { |
| 156 | + if (['string', 'number'].includes(typeof v.default) || |
| 157 | + v.default instanceof String) |
| 158 | + v.default = normalizeString(md, v.default, 'code') |
| 159 | + else { |
| 160 | + let out = normalizeString(md, v.default.value ?? '', 'code') |
| 161 | + if (out.length > 0) |
| 162 | + out += '<br />' |
| 163 | + if (!!v.default.text) |
| 164 | + out += `${normalizeString(md, v.default.text ?? '')}` |
| 165 | + v.default = out |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + /* Add list of dependencies. */ |
| 170 | + v.dependencies = normalizeArray(md, v.dependencies) |
| 171 | + |
| 172 | + /* Add markdown to seealso settings. */ |
| 173 | + v.seealso = normalizeArray(md, v.seealso) |
| 174 | + |
| 175 | + /* Plugin. */ |
| 176 | + if (v.plugin) { |
| 177 | + v.plugin = [ v.plugin ].flat() |
| 178 | + v.plugin_link = v.plugin.map((x) => |
| 179 | + md.renderInline('[[plugin,' + x + ']]') |
| 180 | + ).join(', ') |
| 181 | + } |
| 182 | + |
| 183 | + /* There can be multiple value entries. */ |
| 184 | + if (!Array.isArray(v.values)) { |
| 185 | + v.values = [ v.values ] |
| 186 | + } |
| 187 | + |
| 188 | + for (const v2 of v.values) { |
| 189 | + if (!v2) { |
| 190 | + throw new Error("Incorrect value type for " + k) |
| 191 | + } |
| 192 | + |
| 193 | + if (v2.default_required && (v.default === undefined)) { |
| 194 | + throw new Error("Default value missing for " + k) |
| 195 | + } |
| 196 | + if (v2.enum_required && !v.values_enum) { |
| 197 | + throw new Error("Enum array missing for " + k) |
| 198 | + } |
| 199 | + |
| 200 | + v2.url = md.renderInline(v2.url) |
| 201 | + |
| 202 | + if (v2.no_default) { |
| 203 | + v.no_default = true |
| 204 | + } |
| 205 | + } |
| 206 | + |
| 207 | + for (const k2 of ['added', 'changed', 'deprecated', 'removed']) { |
| 208 | + if (v[k2]) { |
| 209 | + const changes = [] |
| 210 | + for (const[k3, v3] of Object.entries(v[k2])) { |
| 211 | + changes.push({ |
| 212 | + text: v3 ? md.render(v3.trim()) : null, |
| 213 | + version: md.renderInline('[[' + k2 + ',' + k3 + ']]') |
| 214 | + }) |
| 215 | + } |
| 216 | + v[k2] = changes |
| 217 | + } |
| 218 | + } |
| 219 | + |
| 220 | + v.text = md.render(v.text.trim()) |
| 221 | + } |
| 222 | + |
| 223 | + return data |
| 224 | +} |
| 225 | + |
| 226 | +export async function loadSettings() { |
| 227 | + return await normalizeSettings( |
| 228 | + structuredClone(loadData('settings').settings) |
| 229 | + ) |
| 230 | +} |
0 commit comments