@forsakringskassan/docs-generator 1.26.0 → 1.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compile-example.js +47 -13313
- package/dist/create-markdown-renderer-CPrI2LLQ.js +640 -0
- package/dist/{generator.d.ts → index.d.ts} +45 -4
- package/dist/index.js +2283 -0
- package/dist/markdown.js +43 -19389
- package/dist/style/core.css +3 -6
- package/dist/style/index.css +30 -8
- package/dist/style/site.css +27 -2
- package/dist/vendor-uab7h6x-.js +56161 -0
- package/dist/vue2-CBDrnmVO.js +478 -0
- package/dist/vue3-xJIC7Aol.js +12908 -0
- package/package.json +10 -4
- package/templates/partials/version.njk.html +15 -0
- package/dist/generator.js +0 -86510
- package/templates/partials/version.html +0 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,2283 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('node:fs/promises');
|
|
4
|
+
var path = require('node:path');
|
|
5
|
+
var vendor = require('./vendor-uab7h6x-.js');
|
|
6
|
+
var createMarkdownRenderer = require('./create-markdown-renderer-CPrI2LLQ.js');
|
|
7
|
+
var require$$1 = require('crypto');
|
|
8
|
+
require('node:crypto');
|
|
9
|
+
var vueDocgenApi = require('vue-docgen-api');
|
|
10
|
+
var path$1 = require('node:path/posix');
|
|
11
|
+
var fs$1 = require('node:fs');
|
|
12
|
+
var sass = require('sass');
|
|
13
|
+
var node_url = require('node:url');
|
|
14
|
+
var esbuild = require('esbuild');
|
|
15
|
+
var node_util = require('node:util');
|
|
16
|
+
var nunjucks = require('nunjucks');
|
|
17
|
+
var vue = require('vue');
|
|
18
|
+
var vue2 = require('./vue2-CBDrnmVO.js');
|
|
19
|
+
var vue3 = require('./vue3-xJIC7Aol.js');
|
|
20
|
+
var express = require('express');
|
|
21
|
+
var childProcess = require('node:child_process');
|
|
22
|
+
require('path');
|
|
23
|
+
require('url');
|
|
24
|
+
require('fs');
|
|
25
|
+
require('fs/promises');
|
|
26
|
+
require('events');
|
|
27
|
+
require('stream');
|
|
28
|
+
require('string_decoder');
|
|
29
|
+
require('constants');
|
|
30
|
+
require('util');
|
|
31
|
+
require('assert');
|
|
32
|
+
require('readline');
|
|
33
|
+
require('child_process');
|
|
34
|
+
require('node:buffer');
|
|
35
|
+
require('node:process');
|
|
36
|
+
require('node:os');
|
|
37
|
+
require('node:timers/promises');
|
|
38
|
+
require('@vue/compiler-sfc');
|
|
39
|
+
require('typescript');
|
|
40
|
+
require('os');
|
|
41
|
+
require('http');
|
|
42
|
+
require('https');
|
|
43
|
+
require('buffer');
|
|
44
|
+
require('net');
|
|
45
|
+
require('tls');
|
|
46
|
+
require('tty');
|
|
47
|
+
require('querystring');
|
|
48
|
+
|
|
49
|
+
function defineSources(sources) {
|
|
50
|
+
return sources;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function formatSize(value) {
|
|
54
|
+
const suffices = ["kB", "MB"];
|
|
55
|
+
if (value < 1e3) {
|
|
56
|
+
return `${value}`;
|
|
57
|
+
}
|
|
58
|
+
for (const suffix of suffices) {
|
|
59
|
+
value = Math.round(value / 1e3 * 100) / 100;
|
|
60
|
+
if (value < 1e3) {
|
|
61
|
+
return `${value} ${suffix}`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
value = Math.round(value / 1e3 * 100) / 100;
|
|
65
|
+
return `${value} GB`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const matchHeading = /^(#+)(.*)$/gm;
|
|
69
|
+
function getHeadings(text) {
|
|
70
|
+
return Array.from(text.matchAll(matchHeading), parseHeading);
|
|
71
|
+
}
|
|
72
|
+
function parseHeading(match) {
|
|
73
|
+
const [, prefix, text] = match;
|
|
74
|
+
const rank = prefix.length;
|
|
75
|
+
const id = createMarkdownRenderer.generateId(text);
|
|
76
|
+
return {
|
|
77
|
+
rank,
|
|
78
|
+
title: text.trim(),
|
|
79
|
+
anchor: id
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function getDocumentOutline(text, _format) {
|
|
83
|
+
const entries = getHeadings(text);
|
|
84
|
+
const root = { title: "", rank: 0, anchor: "", subheadings: [] };
|
|
85
|
+
const stack = [root];
|
|
86
|
+
const top = () => stack[stack.length - 1];
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const heading = {
|
|
89
|
+
title: entry.title,
|
|
90
|
+
rank: entry.rank,
|
|
91
|
+
anchor: entry.anchor,
|
|
92
|
+
subheadings: []
|
|
93
|
+
};
|
|
94
|
+
while (top().rank >= entry.rank) {
|
|
95
|
+
stack.pop();
|
|
96
|
+
}
|
|
97
|
+
top().subheadings.push(heading);
|
|
98
|
+
stack.push(heading);
|
|
99
|
+
}
|
|
100
|
+
return root.subheadings;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getIntegrity(source) {
|
|
104
|
+
const hash = require$$1.createHash("sha384").update(source).digest("base64");
|
|
105
|
+
return `sha384-${hash}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function haveOutput(doc) {
|
|
109
|
+
const { fileInfo } = doc;
|
|
110
|
+
return Boolean(fileInfo.outputName);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function interpolate(value, data) {
|
|
114
|
+
return value.replace(/{{([^}]+)}}/g, (match, raw) => {
|
|
115
|
+
const key = raw.trim();
|
|
116
|
+
return data[key] ?? match;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parseImport(raw) {
|
|
121
|
+
const comments = [];
|
|
122
|
+
const stripped = raw.replace(/<!--.*?-->/gms, (match) => {
|
|
123
|
+
comments.push(match);
|
|
124
|
+
return "";
|
|
125
|
+
});
|
|
126
|
+
const filename = stripped.trim();
|
|
127
|
+
const extension = path.parse(filename).ext.slice(1);
|
|
128
|
+
return {
|
|
129
|
+
filename,
|
|
130
|
+
extension,
|
|
131
|
+
comments
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function quote(text) {
|
|
136
|
+
return String(text).replace(/"/g, """);
|
|
137
|
+
}
|
|
138
|
+
function* serializeAttr(key, value) {
|
|
139
|
+
if (value === null || value === false) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (value === true) {
|
|
143
|
+
yield key;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
147
|
+
yield `${key}="${quote(value)}"`;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
for (const [innerKey, innerValue] of Object.entries(value)) {
|
|
151
|
+
yield* serializeAttr([key, innerKey].join("-"), innerValue);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function* serializeAttrs(attrs) {
|
|
155
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
156
|
+
yield* serializeAttr(key, value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function difference(a, b) {
|
|
161
|
+
const _difference = new Set(a);
|
|
162
|
+
for (const elem of b) {
|
|
163
|
+
_difference.delete(elem);
|
|
164
|
+
}
|
|
165
|
+
return _difference;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function slugify(value) {
|
|
169
|
+
return value.toLowerCase().replace(/\//g, "--").replace(/[^a-z]+/g, "-").replace(/(^-+|-+$)/, "");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function toArray$3(value) {
|
|
173
|
+
return Array.isArray(value) ? value : [value];
|
|
174
|
+
}
|
|
175
|
+
function isIndexPage(parsed) {
|
|
176
|
+
const folder = path.basename(parsed.dir);
|
|
177
|
+
if (parsed.name === folder) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
if (parsed.name.toLowerCase() === "readme") {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
if (parsed.name === "index") {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
function getBadge(attrs) {
|
|
189
|
+
if (!attrs.status) {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
switch (attrs.status) {
|
|
193
|
+
case "Produktionsklar":
|
|
194
|
+
return "success";
|
|
195
|
+
case "Deprekerad":
|
|
196
|
+
case "Experimentell":
|
|
197
|
+
return "error";
|
|
198
|
+
case "Prelimin\xE4r":
|
|
199
|
+
case "Draft":
|
|
200
|
+
case "Beta":
|
|
201
|
+
return "info";
|
|
202
|
+
default:
|
|
203
|
+
return "info";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function getComponentAlias(attrs) {
|
|
207
|
+
if (attrs.component) {
|
|
208
|
+
return toArray$3(attrs.component).map((it) => `component:${it}`);
|
|
209
|
+
} else {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function parseFile$1(filePath, basePath, content) {
|
|
214
|
+
const relative = basePath ? path.relative(basePath, createMarkdownRenderer.normalizePath(filePath)) : createMarkdownRenderer.normalizePath(filePath);
|
|
215
|
+
const parsed = path.parse(relative);
|
|
216
|
+
const blocks = vendor.fm(content);
|
|
217
|
+
const attributes = blocks.attributes;
|
|
218
|
+
const isIndex = isIndexPage(parsed);
|
|
219
|
+
const filename = isIndex ? "index" : parsed.name.toLowerCase();
|
|
220
|
+
const name = attributes.name ? attributes.name : parsed.name;
|
|
221
|
+
const urlpath = parsed.dir;
|
|
222
|
+
const outline = getDocumentOutline(blocks.body);
|
|
223
|
+
return {
|
|
224
|
+
id: `fs:${filePath.replace(/\\/g, "/")}`,
|
|
225
|
+
name,
|
|
226
|
+
alias: [...getComponentAlias(attributes)],
|
|
227
|
+
visible: attributes.visible ?? true,
|
|
228
|
+
attributes: {
|
|
229
|
+
title: attributes.title ?? name,
|
|
230
|
+
layout: attributes.layout,
|
|
231
|
+
status: attributes.status,
|
|
232
|
+
badge: getBadge(attributes),
|
|
233
|
+
component: attributes.component ? toArray$3(attributes.component) : void 0,
|
|
234
|
+
sortorder: attributes.sortorder ?? Infinity
|
|
235
|
+
},
|
|
236
|
+
body: blocks.body,
|
|
237
|
+
outline,
|
|
238
|
+
format: "markdown",
|
|
239
|
+
tags: [],
|
|
240
|
+
template: blocks.attributes.layout ?? "default",
|
|
241
|
+
fileInfo: {
|
|
242
|
+
path: ["", "."].includes(urlpath) ? "." : `./${createMarkdownRenderer.normalizePath(urlpath)}`,
|
|
243
|
+
name: filename,
|
|
244
|
+
fullPath: createMarkdownRenderer.normalizePath(filePath),
|
|
245
|
+
outputName: `${filename}.html`
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async function frontMatterFileReader(filePath, basePath) {
|
|
250
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
251
|
+
const doc = parseFile$1(filePath, basePath, content);
|
|
252
|
+
return [doc];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const EMPTY_CHAR = "‐";
|
|
256
|
+
const EM_DASH = "—";
|
|
257
|
+
function translateProps(props) {
|
|
258
|
+
var _a, _b;
|
|
259
|
+
const translatedProps = [];
|
|
260
|
+
for (const prop of props) {
|
|
261
|
+
const translatedProp = {
|
|
262
|
+
name: prop.name,
|
|
263
|
+
description: prop.description ?? EMPTY_CHAR,
|
|
264
|
+
type: ((_a = prop.type) == null ? void 0 : _a.name) ?? EMPTY_CHAR,
|
|
265
|
+
required: prop.required ? "true" : "false",
|
|
266
|
+
default: ((_b = prop.defaultValue) == null ? void 0 : _b.value) ?? EMPTY_CHAR
|
|
267
|
+
};
|
|
268
|
+
translatedProps.push(translatedProp);
|
|
269
|
+
}
|
|
270
|
+
return translatedProps;
|
|
271
|
+
}
|
|
272
|
+
function translateEvents(events) {
|
|
273
|
+
const translatedEvents = [];
|
|
274
|
+
for (const event of events) {
|
|
275
|
+
const translatedEvent = {
|
|
276
|
+
name: event.name,
|
|
277
|
+
description: event.description ?? EMPTY_CHAR,
|
|
278
|
+
properties: translateEventProperties(event)
|
|
279
|
+
};
|
|
280
|
+
translatedEvents.push(translatedEvent);
|
|
281
|
+
}
|
|
282
|
+
return translatedEvents;
|
|
283
|
+
}
|
|
284
|
+
function translateEventProperties(event) {
|
|
285
|
+
const properties = event.properties;
|
|
286
|
+
const types = event.type ? Object.values(event.type.names) : [];
|
|
287
|
+
if (!properties && !types.length) {
|
|
288
|
+
return EMPTY_CHAR;
|
|
289
|
+
}
|
|
290
|
+
if (!properties) {
|
|
291
|
+
return `<anonymous>: ${String(types)}`;
|
|
292
|
+
}
|
|
293
|
+
const translatedProperties = [];
|
|
294
|
+
for (let i = 0; i < properties.length; i++) {
|
|
295
|
+
const property = properties[i];
|
|
296
|
+
const name = property.name ?? "<anonymous>";
|
|
297
|
+
const eventType = types[i];
|
|
298
|
+
const hasEventType = !eventType || eventType !== "undefined";
|
|
299
|
+
const resolvedType = hasEventType ? eventType : String(property.type.names);
|
|
300
|
+
const type = `: ${resolvedType}`;
|
|
301
|
+
const description = property.description ? ` ${EM_DASH} ${property.description}` : "";
|
|
302
|
+
translatedProperties.push(`${name}${type}${description}`);
|
|
303
|
+
}
|
|
304
|
+
return translatedProperties.join("\n");
|
|
305
|
+
}
|
|
306
|
+
function translateSlots(slots) {
|
|
307
|
+
const translatedSlots = [];
|
|
308
|
+
for (const slot of slots) {
|
|
309
|
+
const translatedSlot = {
|
|
310
|
+
name: slot.name,
|
|
311
|
+
description: slot.description ?? EMPTY_CHAR,
|
|
312
|
+
bindings: translateSlotBindings(slot)
|
|
313
|
+
};
|
|
314
|
+
translatedSlots.push(translatedSlot);
|
|
315
|
+
}
|
|
316
|
+
return translatedSlots;
|
|
317
|
+
}
|
|
318
|
+
function translateSlotBindings(slot) {
|
|
319
|
+
if (!slot.bindings) {
|
|
320
|
+
return EMPTY_CHAR;
|
|
321
|
+
}
|
|
322
|
+
const translatedBindings = [];
|
|
323
|
+
for (const binding of slot.bindings) {
|
|
324
|
+
translatedBindings.push(binding.name ? binding.name : EMPTY_CHAR);
|
|
325
|
+
}
|
|
326
|
+
return translatedBindings.join("\n");
|
|
327
|
+
}
|
|
328
|
+
async function translateAPI(filePath) {
|
|
329
|
+
const api = await vueDocgenApi.parse(filePath);
|
|
330
|
+
const props = api.props ? translateProps(api.props) : [];
|
|
331
|
+
const events = api.events ? translateEvents(api.events) : [];
|
|
332
|
+
const slots = api.slots ? translateSlots(api.slots) : [];
|
|
333
|
+
return {
|
|
334
|
+
props,
|
|
335
|
+
events,
|
|
336
|
+
slots
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const md = vendor.MarkdownIt({ breaks: true });
|
|
341
|
+
function generateTableHead(headers) {
|
|
342
|
+
return [
|
|
343
|
+
"<thead><tr>",
|
|
344
|
+
...headers.map((header) => `<th>${header}</th>`),
|
|
345
|
+
"</tr></thead>"
|
|
346
|
+
].join("");
|
|
347
|
+
}
|
|
348
|
+
function generateTableBody(content) {
|
|
349
|
+
const rows = content.map((row) => {
|
|
350
|
+
return [
|
|
351
|
+
"<tr>",
|
|
352
|
+
...row.map((value) => `<td>${md.render(value)}</td>`),
|
|
353
|
+
"</tr>"
|
|
354
|
+
].join("");
|
|
355
|
+
});
|
|
356
|
+
return ["<tbody>", ...rows, "</tbody>"].join("");
|
|
357
|
+
}
|
|
358
|
+
function generateTable(caption, headers, content) {
|
|
359
|
+
return [
|
|
360
|
+
'<div class="docs-table"><table>',
|
|
361
|
+
`<caption>${caption}</caption>`,
|
|
362
|
+
generateTableHead(headers),
|
|
363
|
+
generateTableBody(content),
|
|
364
|
+
"</table></div>"
|
|
365
|
+
].join("");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function capitalize(text) {
|
|
369
|
+
return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
|
|
370
|
+
}
|
|
371
|
+
function parseAPIToArray(api) {
|
|
372
|
+
const content = [];
|
|
373
|
+
const headers = Object.keys(api[0]);
|
|
374
|
+
const capitalizedHeaders = headers.map(capitalize);
|
|
375
|
+
content.push(capitalizedHeaders);
|
|
376
|
+
for (const row of api) {
|
|
377
|
+
content.push(Object.values(row));
|
|
378
|
+
}
|
|
379
|
+
return content;
|
|
380
|
+
}
|
|
381
|
+
function parseAPI(filePath, api) {
|
|
382
|
+
const relative = createMarkdownRenderer.normalizePath(filePath);
|
|
383
|
+
const parsedPath = path.parse(relative);
|
|
384
|
+
const componentName = parsedPath.name;
|
|
385
|
+
let html = "";
|
|
386
|
+
if (api.props.length) {
|
|
387
|
+
const propsArray = parseAPIToArray(api.props);
|
|
388
|
+
html += generateTable("Props", propsArray[0], propsArray.slice(1));
|
|
389
|
+
}
|
|
390
|
+
if (api.events.length) {
|
|
391
|
+
const eventsArray = parseAPIToArray(api.events);
|
|
392
|
+
html += generateTable("Events", eventsArray[0], eventsArray.slice(1));
|
|
393
|
+
}
|
|
394
|
+
if (api.slots.length) {
|
|
395
|
+
const slotsArray = parseAPIToArray(api.slots);
|
|
396
|
+
html += generateTable("Slots", slotsArray[0], slotsArray.slice(1));
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
id: `fs:${filePath.replace(/\\/g, "/")}`,
|
|
400
|
+
name: `vue:${componentName}`,
|
|
401
|
+
alias: [],
|
|
402
|
+
visible: false,
|
|
403
|
+
attributes: { sortorder: Infinity },
|
|
404
|
+
body: html,
|
|
405
|
+
outline: [],
|
|
406
|
+
format: "html",
|
|
407
|
+
tags: [],
|
|
408
|
+
template: "",
|
|
409
|
+
fileInfo: {
|
|
410
|
+
path: "",
|
|
411
|
+
name: "",
|
|
412
|
+
fullPath: "",
|
|
413
|
+
outputName: false
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
async function vueFileReader(filePath) {
|
|
418
|
+
const translated = await translateAPI(filePath);
|
|
419
|
+
const doc = parseAPI(filePath, translated);
|
|
420
|
+
return [doc];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async function globAll(pattern) {
|
|
424
|
+
if (!pattern) {
|
|
425
|
+
return /* @__PURE__ */ new Set();
|
|
426
|
+
}
|
|
427
|
+
if (!Array.isArray(pattern)) {
|
|
428
|
+
return globAll([pattern]);
|
|
429
|
+
}
|
|
430
|
+
const results = await Promise.all(pattern.map((it) => vendor.glob(it)));
|
|
431
|
+
return new Set(results.flat());
|
|
432
|
+
}
|
|
433
|
+
async function getDocumentsForSource(src) {
|
|
434
|
+
const { transform } = src;
|
|
435
|
+
const include = await globAll(src.include);
|
|
436
|
+
const exclude = await globAll(src.exclude);
|
|
437
|
+
const files = Array.from(difference(include, exclude));
|
|
438
|
+
const promises = files.map(async (it) => {
|
|
439
|
+
const docs2 = await src.fileReader(it, src.basePath);
|
|
440
|
+
if (transform) {
|
|
441
|
+
return docs2.map((it2) => transform(it2));
|
|
442
|
+
} else {
|
|
443
|
+
return docs2;
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
const docs = await Promise.all(promises);
|
|
447
|
+
return docs.flat();
|
|
448
|
+
}
|
|
449
|
+
async function getAllDocuments(sourceFiles) {
|
|
450
|
+
const result = await Promise.all(sourceFiles.map(getDocumentsForSource));
|
|
451
|
+
return result.flat();
|
|
452
|
+
}
|
|
453
|
+
function fileReaderProcessor(sourceFiles) {
|
|
454
|
+
return {
|
|
455
|
+
stage: "generate-docs",
|
|
456
|
+
name: "file-reader-processor",
|
|
457
|
+
async handler(context) {
|
|
458
|
+
const result = await getAllDocuments(sourceFiles);
|
|
459
|
+
context.addDocument(result);
|
|
460
|
+
context.log(result.length, "documents found");
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function parseFile(filePath, basePath, content) {
|
|
466
|
+
const attributes = JSON.parse(content);
|
|
467
|
+
const { title, href } = attributes;
|
|
468
|
+
if (!title) {
|
|
469
|
+
throw new Error(`No title property set in "${filePath}"`);
|
|
470
|
+
}
|
|
471
|
+
const relative = basePath ? path.relative(basePath, createMarkdownRenderer.normalizePath(filePath)) : createMarkdownRenderer.normalizePath(filePath);
|
|
472
|
+
const parsed = path.parse(relative);
|
|
473
|
+
const filename = parsed.name.toLowerCase();
|
|
474
|
+
const name = parsed.name;
|
|
475
|
+
const urlpath = parsed.dir;
|
|
476
|
+
return {
|
|
477
|
+
id: `nav:${filePath.replace(/\\/g, "/")}`,
|
|
478
|
+
name,
|
|
479
|
+
alias: [],
|
|
480
|
+
visible: attributes.href ? true : false,
|
|
481
|
+
attributes: {
|
|
482
|
+
title,
|
|
483
|
+
href,
|
|
484
|
+
sortorder: attributes.sortorder ?? Infinity
|
|
485
|
+
},
|
|
486
|
+
body: content,
|
|
487
|
+
outline: [],
|
|
488
|
+
format: "json",
|
|
489
|
+
tags: [],
|
|
490
|
+
template: "default",
|
|
491
|
+
fileInfo: {
|
|
492
|
+
path: ["", "."].includes(urlpath) ? "." : `./${createMarkdownRenderer.normalizePath(urlpath)}`,
|
|
493
|
+
name: filename,
|
|
494
|
+
fullPath: createMarkdownRenderer.normalizePath(filePath),
|
|
495
|
+
outputName: false
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
async function navigationFileReader(filePath, basePath) {
|
|
500
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
501
|
+
const doc = parseFile(filePath, basePath, content);
|
|
502
|
+
return [doc];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function resolveScssImport(filePath) {
|
|
506
|
+
const { dir, base } = path$1.parse(filePath);
|
|
507
|
+
const search = [`${base}.css`, `${base}.scss`, `_${base}.scss`, `${base}`];
|
|
508
|
+
for (const variant of search) {
|
|
509
|
+
try {
|
|
510
|
+
const moduleName = path$1.join(dir, variant);
|
|
511
|
+
const resolved = require.resolve(moduleName);
|
|
512
|
+
return node_url.pathToFileURL(resolved);
|
|
513
|
+
} catch (err) {
|
|
514
|
+
if (err.code !== "MODULE_NOT_FOUND") {
|
|
515
|
+
throw err;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
throw new Error(`Failed to resolve "${filePath}"`);
|
|
520
|
+
}
|
|
521
|
+
const tildeImporter = {
|
|
522
|
+
canonicalize(url) {
|
|
523
|
+
const indexOfTilde = url.indexOf("~");
|
|
524
|
+
if (indexOfTilde >= 0) {
|
|
525
|
+
return resolveScssImport(url.slice(indexOfTilde + 1));
|
|
526
|
+
}
|
|
527
|
+
if (url.startsWith("file://")) {
|
|
528
|
+
const path$1 = node_url.fileURLToPath(url);
|
|
529
|
+
const relative = path.isAbsolute(path$1) ? path.relative(__dirname, path$1) : path$1;
|
|
530
|
+
return resolveScssImport(relative.replace(/\\/g, "/"));
|
|
531
|
+
}
|
|
532
|
+
return null;
|
|
533
|
+
},
|
|
534
|
+
async load(url) {
|
|
535
|
+
const filepath = node_url.fileURLToPath(url);
|
|
536
|
+
const parsed = path.parse(filepath);
|
|
537
|
+
const contents = await fs.readFile(filepath, "utf-8");
|
|
538
|
+
return {
|
|
539
|
+
contents,
|
|
540
|
+
syntax: parsed.ext.slice(1) === "scss" ? "scss" : "css"
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
async function compileSassString(dst, style) {
|
|
546
|
+
const result = await sass.compileStringAsync(style, {
|
|
547
|
+
style: "expanded",
|
|
548
|
+
importers: [tildeImporter]
|
|
549
|
+
});
|
|
550
|
+
await fs.mkdir(path.dirname(dst), { recursive: true });
|
|
551
|
+
await fs.writeFile(dst, result.css, "utf-8");
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
async function compileStyle(assetFolder, name, src) {
|
|
555
|
+
try {
|
|
556
|
+
const outfile = path$1.join("temp", `asset-${name}.css`);
|
|
557
|
+
const source = await fs.readFile(src, "utf-8");
|
|
558
|
+
await compileSassString(outfile, source);
|
|
559
|
+
const content = await fs.readFile(outfile, "utf-8");
|
|
560
|
+
const fingerprint = createMarkdownRenderer.getFingerprint(content);
|
|
561
|
+
const integrity = getIntegrity(content);
|
|
562
|
+
const filename = `${name}-${fingerprint}.css`;
|
|
563
|
+
const dst = path$1.join(assetFolder, filename);
|
|
564
|
+
await fs.rename(outfile, dst);
|
|
565
|
+
const stat = await fs.stat(dst);
|
|
566
|
+
return {
|
|
567
|
+
name,
|
|
568
|
+
filename,
|
|
569
|
+
publicPath: `./assets/${filename}`,
|
|
570
|
+
integrity,
|
|
571
|
+
size: stat.size,
|
|
572
|
+
type: "css"
|
|
573
|
+
};
|
|
574
|
+
} catch (err) {
|
|
575
|
+
console.error(err);
|
|
576
|
+
throw new Error(`Failed to compile style "${name}"`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function cssAssetProcessor(assetFolder, assets) {
|
|
581
|
+
return {
|
|
582
|
+
name: "css-asset-processor",
|
|
583
|
+
after: "assets",
|
|
584
|
+
async handler(context) {
|
|
585
|
+
const assetInfo = context.getTemplateData("assets") ?? {};
|
|
586
|
+
const head = context.getTemplateData("injectHead") ?? [];
|
|
587
|
+
const body = context.getTemplateData("injectBody") ?? [];
|
|
588
|
+
for (const asset of assets) {
|
|
589
|
+
const info = await compileStyle(
|
|
590
|
+
assetFolder,
|
|
591
|
+
asset.name,
|
|
592
|
+
asset.src
|
|
593
|
+
);
|
|
594
|
+
const attrs = serializeAttrs(asset.options.attributes);
|
|
595
|
+
const inject = { ...info, attrs: Array.from(attrs).join(" ") };
|
|
596
|
+
assetInfo[asset.name] = info;
|
|
597
|
+
switch (asset.options.appendTo) {
|
|
598
|
+
case "none":
|
|
599
|
+
break;
|
|
600
|
+
case "head":
|
|
601
|
+
head.push(inject);
|
|
602
|
+
break;
|
|
603
|
+
case "body":
|
|
604
|
+
body.push(inject);
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
context.setTemplateData("injectHead", head);
|
|
608
|
+
context.setTemplateData("injectBody", body);
|
|
609
|
+
context.log(info.filename, formatSize(info.size));
|
|
610
|
+
}
|
|
611
|
+
context.setTemplateData("assets", assetInfo);
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function toArray$2(value) {
|
|
617
|
+
if (Array.isArray(value)) {
|
|
618
|
+
return value;
|
|
619
|
+
} else {
|
|
620
|
+
return [value];
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async function compileScript(assetFolder, name, src, options) {
|
|
624
|
+
const iconLib = process.env.DOCS_ICON_LIB ?? "@fkui/icon-lib-default";
|
|
625
|
+
try {
|
|
626
|
+
const outfile = path.join("temp", `asset-${name}.js`);
|
|
627
|
+
await esbuild.build({
|
|
628
|
+
entryPoints: toArray$2(src),
|
|
629
|
+
outfile,
|
|
630
|
+
bundle: true,
|
|
631
|
+
format: "iife",
|
|
632
|
+
platform: "browser",
|
|
633
|
+
external: ["vue", "@fkui/vue"],
|
|
634
|
+
tsconfig: path.join(__dirname, "../tsconfig-examples.json"),
|
|
635
|
+
define: {
|
|
636
|
+
"process.env.DOCS_ICON_LIB": JSON.stringify(iconLib)
|
|
637
|
+
},
|
|
638
|
+
...options
|
|
639
|
+
});
|
|
640
|
+
const content = await fs.readFile(outfile, "utf-8");
|
|
641
|
+
const fingerprint = createMarkdownRenderer.getFingerprint(content);
|
|
642
|
+
const integrity = getIntegrity(content);
|
|
643
|
+
const filename = `${name}-${fingerprint}.js`;
|
|
644
|
+
const dst = path.join(assetFolder, filename);
|
|
645
|
+
await fs.rename(outfile, dst);
|
|
646
|
+
const stat = await fs.stat(dst);
|
|
647
|
+
return {
|
|
648
|
+
name,
|
|
649
|
+
filename,
|
|
650
|
+
publicPath: `./assets/${filename}`,
|
|
651
|
+
integrity,
|
|
652
|
+
size: stat.size,
|
|
653
|
+
type: "js"
|
|
654
|
+
};
|
|
655
|
+
} catch {
|
|
656
|
+
throw new Error(`Failed to compile script "${name}"`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function jsAssetProcessor(assetFolder, assets) {
|
|
661
|
+
return {
|
|
662
|
+
name: "js-asset-processor",
|
|
663
|
+
after: "assets",
|
|
664
|
+
async handler(context) {
|
|
665
|
+
const assetInfo = context.getTemplateData("assets") ?? {};
|
|
666
|
+
const head = context.getTemplateData("injectHead") ?? [];
|
|
667
|
+
const body = context.getTemplateData("injectBody") ?? [];
|
|
668
|
+
for (const asset of assets) {
|
|
669
|
+
const info = await compileScript(
|
|
670
|
+
assetFolder,
|
|
671
|
+
asset.name,
|
|
672
|
+
asset.src
|
|
673
|
+
);
|
|
674
|
+
const attrs = serializeAttrs(asset.options.attributes);
|
|
675
|
+
const inject = { ...info, attrs: Array.from(attrs).join(" ") };
|
|
676
|
+
assetInfo[asset.name] = info;
|
|
677
|
+
switch (asset.options.appendTo) {
|
|
678
|
+
case "none":
|
|
679
|
+
break;
|
|
680
|
+
case "head":
|
|
681
|
+
head.push(inject);
|
|
682
|
+
break;
|
|
683
|
+
case "body":
|
|
684
|
+
body.push(inject);
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
context.setTemplateData("injectHead", head);
|
|
688
|
+
context.setTemplateData("injectBody", body);
|
|
689
|
+
context.log(info.filename, formatSize(info.size));
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function staticResourcesProcessor(assetFolder, resources) {
|
|
696
|
+
return {
|
|
697
|
+
name: "static-resources-processor",
|
|
698
|
+
after: "render",
|
|
699
|
+
async handler(context) {
|
|
700
|
+
for (const resource of [...resources, ...context.resources]) {
|
|
701
|
+
const src = resource.from;
|
|
702
|
+
const dst = path$1.join(assetFolder, resource.to);
|
|
703
|
+
await fs.cp(src, dst, {
|
|
704
|
+
recursive: true
|
|
705
|
+
});
|
|
706
|
+
context.log(src, "->", dst);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function getExampleImport(searchDirs, filename) {
|
|
713
|
+
const pattern = searchDirs.map((dir) => `${dir}/**/${filename}`);
|
|
714
|
+
const matches = vendor.globSync(pattern);
|
|
715
|
+
if (matches.length === 0) {
|
|
716
|
+
const message = `No files matched import "${filename}"`;
|
|
717
|
+
throw new Error(message);
|
|
718
|
+
} else if (matches.length > 1) {
|
|
719
|
+
const message = `Multiple files matched import "${filename}"`;
|
|
720
|
+
throw new Error(message);
|
|
721
|
+
} else {
|
|
722
|
+
return createMarkdownRenderer.normalizePath(matches[0]);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function getExampleName(filename) {
|
|
727
|
+
return path.parse(filename).name;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const vueMajor = parseInt(vue.version.split(".", 2)[0], 10);
|
|
731
|
+
const vueGenerator = {
|
|
732
|
+
[2]: vue2.generateCode,
|
|
733
|
+
[3]: vue3.generateCode
|
|
734
|
+
};
|
|
735
|
+
function generateExample(options) {
|
|
736
|
+
const { language } = options;
|
|
737
|
+
if (language === "import") {
|
|
738
|
+
const parsed = parseImport(options.source);
|
|
739
|
+
const filename = getExampleImport(
|
|
740
|
+
options.exampleFolders,
|
|
741
|
+
parsed.filename
|
|
742
|
+
);
|
|
743
|
+
const source = fs$1.readFileSync(filename, "utf-8");
|
|
744
|
+
const language2 = parsed.extension;
|
|
745
|
+
const comments = parsed.comments;
|
|
746
|
+
const example = generateExample({
|
|
747
|
+
...options,
|
|
748
|
+
source,
|
|
749
|
+
language: language2,
|
|
750
|
+
filename
|
|
751
|
+
});
|
|
752
|
+
return { ...example, comments };
|
|
753
|
+
}
|
|
754
|
+
switch (language) {
|
|
755
|
+
case "vue":
|
|
756
|
+
return generateVueExample(options);
|
|
757
|
+
case "html":
|
|
758
|
+
return generateStaticExample(options);
|
|
759
|
+
default:
|
|
760
|
+
return generateStaticExample(options);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
function generateVueExample(options) {
|
|
764
|
+
const { filename, source, parent, setupPath, tags } = options;
|
|
765
|
+
const fn = vueGenerator[vueMajor];
|
|
766
|
+
const slug = getExampleName(filename);
|
|
767
|
+
const fingerprint = createMarkdownRenderer.getFingerprint(source);
|
|
768
|
+
const { markup, sourcecode, output } = fn({
|
|
769
|
+
filename,
|
|
770
|
+
slug,
|
|
771
|
+
fingerprint,
|
|
772
|
+
code: source,
|
|
773
|
+
setupPath
|
|
774
|
+
});
|
|
775
|
+
return {
|
|
776
|
+
source,
|
|
777
|
+
language: options.language,
|
|
778
|
+
comments: [],
|
|
779
|
+
tags,
|
|
780
|
+
markup,
|
|
781
|
+
output,
|
|
782
|
+
runtime: true,
|
|
783
|
+
task: {
|
|
784
|
+
outputFile: output,
|
|
785
|
+
sourcecode,
|
|
786
|
+
sourceFile: filename,
|
|
787
|
+
parent
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
function generateStaticExample(options) {
|
|
792
|
+
const { source, language, tags } = options;
|
|
793
|
+
const runtimeLanguages = ["html"];
|
|
794
|
+
return {
|
|
795
|
+
source,
|
|
796
|
+
language: options.language,
|
|
797
|
+
comments: [],
|
|
798
|
+
tags,
|
|
799
|
+
markup: source,
|
|
800
|
+
output: null,
|
|
801
|
+
runtime: runtimeLanguages.includes(language)
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function isNavigationSection(node) {
|
|
806
|
+
return "key" in node;
|
|
807
|
+
}
|
|
808
|
+
function pathFromDoc({ fileInfo }) {
|
|
809
|
+
if (fileInfo.name === "index") {
|
|
810
|
+
return [fileInfo.path.replace(/\\/g, "/"), true];
|
|
811
|
+
} else {
|
|
812
|
+
return [
|
|
813
|
+
`./${path.join(fileInfo.path, fileInfo.name).replace(/\\/g, "/")}`,
|
|
814
|
+
false
|
|
815
|
+
];
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function getParentKey(name) {
|
|
819
|
+
return name.split("/").slice(0, -1).join("/");
|
|
820
|
+
}
|
|
821
|
+
function generateNavtree(docs) {
|
|
822
|
+
const section = {};
|
|
823
|
+
function createSection(key, title, sortorder) {
|
|
824
|
+
const existing = section[key];
|
|
825
|
+
if (existing) {
|
|
826
|
+
existing.title = title;
|
|
827
|
+
existing.sortorder = sortorder;
|
|
828
|
+
} else {
|
|
829
|
+
const node = {
|
|
830
|
+
key,
|
|
831
|
+
title,
|
|
832
|
+
path: "",
|
|
833
|
+
sortorder,
|
|
834
|
+
children: []
|
|
835
|
+
};
|
|
836
|
+
section[key] = node;
|
|
837
|
+
if (key !== ".") {
|
|
838
|
+
const parent = attach(key);
|
|
839
|
+
parent.children.push(node);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
function attach(key) {
|
|
844
|
+
const parentKey = getParentKey(key);
|
|
845
|
+
let parent = section[parentKey];
|
|
846
|
+
if (parent) {
|
|
847
|
+
return parent;
|
|
848
|
+
}
|
|
849
|
+
parent = {
|
|
850
|
+
key: parentKey,
|
|
851
|
+
title: parentKey.split("/").at(-1) ?? "(missing title)",
|
|
852
|
+
path: "",
|
|
853
|
+
sortorder: Infinity,
|
|
854
|
+
children: []
|
|
855
|
+
};
|
|
856
|
+
section[parentKey] = parent;
|
|
857
|
+
let anchestor = parentKey;
|
|
858
|
+
let current = parent;
|
|
859
|
+
while (anchestor !== ".") {
|
|
860
|
+
anchestor = getParentKey(anchestor);
|
|
861
|
+
if (section[anchestor]) {
|
|
862
|
+
section[anchestor].children.push(current);
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
const node = {
|
|
866
|
+
key: anchestor,
|
|
867
|
+
title: "",
|
|
868
|
+
path: "",
|
|
869
|
+
sortorder: Infinity,
|
|
870
|
+
children: [current]
|
|
871
|
+
};
|
|
872
|
+
section[anchestor] = node;
|
|
873
|
+
current = node;
|
|
874
|
+
}
|
|
875
|
+
return parent;
|
|
876
|
+
}
|
|
877
|
+
for (const doc of docs) {
|
|
878
|
+
const [name, isSection] = pathFromDoc(doc);
|
|
879
|
+
const title = doc.attributes.title ?? doc.fileInfo.name;
|
|
880
|
+
const sortorder = doc.attributes.sortorder;
|
|
881
|
+
if (doc.attributes.href) {
|
|
882
|
+
const parent2 = attach(name);
|
|
883
|
+
const leafNode = {
|
|
884
|
+
id: doc.id,
|
|
885
|
+
title,
|
|
886
|
+
path: doc.attributes.href,
|
|
887
|
+
sortorder,
|
|
888
|
+
external: true
|
|
889
|
+
};
|
|
890
|
+
parent2.children.push(leafNode);
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (!doc.fileInfo.outputName) {
|
|
894
|
+
if (isSection) {
|
|
895
|
+
createSection(name, title, sortorder);
|
|
896
|
+
}
|
|
897
|
+
continue;
|
|
898
|
+
}
|
|
899
|
+
if (!doc.visible) {
|
|
900
|
+
if (isSection) {
|
|
901
|
+
createSection(name, title, sortorder);
|
|
902
|
+
}
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
if (name === ".") {
|
|
906
|
+
createSection(name, title, sortorder);
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
const parent = attach(name);
|
|
910
|
+
if (isSection) {
|
|
911
|
+
const leafNode = {
|
|
912
|
+
id: doc.id,
|
|
913
|
+
title,
|
|
914
|
+
path: `${name}/`,
|
|
915
|
+
sortorder,
|
|
916
|
+
external: false
|
|
917
|
+
};
|
|
918
|
+
const existing = section[name];
|
|
919
|
+
if (existing) {
|
|
920
|
+
existing.title = title;
|
|
921
|
+
existing.path = leafNode.path;
|
|
922
|
+
existing.sortorder = sortorder;
|
|
923
|
+
existing.children.unshift(leafNode);
|
|
924
|
+
} else {
|
|
925
|
+
const sectionNode = {
|
|
926
|
+
key: name,
|
|
927
|
+
title,
|
|
928
|
+
path: leafNode.path,
|
|
929
|
+
sortorder,
|
|
930
|
+
children: [leafNode]
|
|
931
|
+
};
|
|
932
|
+
section[name] = sectionNode;
|
|
933
|
+
parent.children.push(sectionNode);
|
|
934
|
+
}
|
|
935
|
+
} else {
|
|
936
|
+
const leafNode = {
|
|
937
|
+
id: doc.id,
|
|
938
|
+
title,
|
|
939
|
+
path: `${doc.fileInfo.path}/${doc.fileInfo.outputName}`,
|
|
940
|
+
sortorder,
|
|
941
|
+
external: false
|
|
942
|
+
};
|
|
943
|
+
parent.children.push(leafNode);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
const root = section["."];
|
|
947
|
+
if (root) {
|
|
948
|
+
return root;
|
|
949
|
+
} else {
|
|
950
|
+
return {
|
|
951
|
+
key: ".",
|
|
952
|
+
title: "",
|
|
953
|
+
path: "",
|
|
954
|
+
sortorder: Infinity,
|
|
955
|
+
children: []
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function navigationProcessor() {
|
|
961
|
+
return {
|
|
962
|
+
stage: "generate-nav",
|
|
963
|
+
name: "navigation-processor",
|
|
964
|
+
handler(context) {
|
|
965
|
+
const navtree = generateNavtree(context.docs);
|
|
966
|
+
context.setTopNavigation(navtree);
|
|
967
|
+
context.setSideNavigation(navtree);
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function sortNavigationTree(tree) {
|
|
973
|
+
tree.children.sort((a, b) => {
|
|
974
|
+
if (a.sortorder < b.sortorder) {
|
|
975
|
+
return -1;
|
|
976
|
+
} else if (a.sortorder > b.sortorder) {
|
|
977
|
+
return 1;
|
|
978
|
+
}
|
|
979
|
+
const titleA = a.title.toUpperCase();
|
|
980
|
+
const titleB = b.title.toUpperCase();
|
|
981
|
+
if (titleA < titleB) {
|
|
982
|
+
return -1;
|
|
983
|
+
}
|
|
984
|
+
if (titleA > titleB) {
|
|
985
|
+
return 1;
|
|
986
|
+
}
|
|
987
|
+
return 0;
|
|
988
|
+
});
|
|
989
|
+
for (const child of tree.children) {
|
|
990
|
+
if (isNavigationSection(child)) {
|
|
991
|
+
sortNavigationTree(child);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
class TemplateLoader {
|
|
997
|
+
async = true;
|
|
998
|
+
folders;
|
|
999
|
+
templateCache;
|
|
1000
|
+
constructor(folders = []) {
|
|
1001
|
+
this.folders = [...folders, path.join(__dirname, "../templates")];
|
|
1002
|
+
this.templateCache = /* @__PURE__ */ new Map();
|
|
1003
|
+
}
|
|
1004
|
+
async getSource(name, callback) {
|
|
1005
|
+
try {
|
|
1006
|
+
const { content, filePath } = await this.resolveTemplate(name);
|
|
1007
|
+
callback(null, {
|
|
1008
|
+
src: content,
|
|
1009
|
+
path: filePath,
|
|
1010
|
+
noCache: false
|
|
1011
|
+
});
|
|
1012
|
+
} catch (err) {
|
|
1013
|
+
if (err instanceof Error) {
|
|
1014
|
+
callback(err, null);
|
|
1015
|
+
} else {
|
|
1016
|
+
callback(new Error(String(err)), null);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
async resolveTemplate(name) {
|
|
1021
|
+
const { templateCache, folders } = this;
|
|
1022
|
+
const cached = templateCache.get(name);
|
|
1023
|
+
if (cached) {
|
|
1024
|
+
return cached;
|
|
1025
|
+
}
|
|
1026
|
+
const searchPaths = folders.map((it) => path.join(it, name));
|
|
1027
|
+
const filePath = searchPaths.find((it) => fs$1.existsSync(it));
|
|
1028
|
+
if (!filePath) {
|
|
1029
|
+
const searched = folders.map((it) => ` - "${it}"`).join("\n");
|
|
1030
|
+
const message = `Failed to resolve template "${name}", searched in:
|
|
1031
|
+
|
|
1032
|
+
${searched}
|
|
1033
|
+
|
|
1034
|
+
Make sure the name is correct and the template file exists in one of the listed directories.`;
|
|
1035
|
+
throw new Error(message);
|
|
1036
|
+
}
|
|
1037
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
1038
|
+
const resolved = { content, filePath };
|
|
1039
|
+
templateCache.set(name, resolved);
|
|
1040
|
+
return resolved;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
function dump(value) {
|
|
1045
|
+
return `<pre>${JSON.stringify(value, null, 2)}</pre>`;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function json(value) {
|
|
1049
|
+
return JSON.stringify(value, null, 2);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function isExternalUrl(url) {
|
|
1053
|
+
return url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//");
|
|
1054
|
+
}
|
|
1055
|
+
function isAbsoluteUrl(url) {
|
|
1056
|
+
return url.startsWith("/");
|
|
1057
|
+
}
|
|
1058
|
+
function relative(url, { fileInfo }) {
|
|
1059
|
+
if (isExternalUrl(url)) {
|
|
1060
|
+
return url;
|
|
1061
|
+
}
|
|
1062
|
+
if (isAbsoluteUrl(url)) {
|
|
1063
|
+
url = `.${url}`;
|
|
1064
|
+
}
|
|
1065
|
+
url = createMarkdownRenderer.normalizePath(url);
|
|
1066
|
+
const outputPath = createMarkdownRenderer.normalizePath(fileInfo.path);
|
|
1067
|
+
if (outputPath === url || `${outputPath}/` === url) {
|
|
1068
|
+
return "./";
|
|
1069
|
+
}
|
|
1070
|
+
const relative2 = path$1.relative(outputPath, url);
|
|
1071
|
+
const prefix = relative2.startsWith(".") ? "" : "./";
|
|
1072
|
+
const suffix = url.endsWith("/") ? "/" : "";
|
|
1073
|
+
return [prefix, relative2, suffix].join("");
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function take(haystack, key, value) {
|
|
1077
|
+
return haystack.filter((it) => it[key] === value);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
class MissingTemplateError extends Error {
|
|
1081
|
+
searchDir;
|
|
1082
|
+
constructor(fileInfo, template, format, searchDir) {
|
|
1083
|
+
const message = `Failed to find template "${template}" (${format} format) when rendering "${fileInfo.fullPath}"`;
|
|
1084
|
+
super(message);
|
|
1085
|
+
this.name = "MissingTemplateError";
|
|
1086
|
+
this.searchDir = searchDir;
|
|
1087
|
+
}
|
|
1088
|
+
prettyError() {
|
|
1089
|
+
return [
|
|
1090
|
+
this.message,
|
|
1091
|
+
"",
|
|
1092
|
+
"Searched the following directories:",
|
|
1093
|
+
...this.searchDir.map((it) => ` - ${it}`)
|
|
1094
|
+
].join("\n");
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
const templateDirectory = path.join(__dirname, "../templates");
|
|
1098
|
+
const cache$1 = /* @__PURE__ */ new Map();
|
|
1099
|
+
function cacheKey(layout, extension) {
|
|
1100
|
+
return [layout, extension].join("|");
|
|
1101
|
+
}
|
|
1102
|
+
function findTemplate(from, src, format) {
|
|
1103
|
+
let layout;
|
|
1104
|
+
if (typeof src === "string") {
|
|
1105
|
+
layout = src;
|
|
1106
|
+
format ??= "html";
|
|
1107
|
+
} else {
|
|
1108
|
+
layout = src.template;
|
|
1109
|
+
format = src.format !== "json" ? "html" : "json";
|
|
1110
|
+
}
|
|
1111
|
+
const key = cacheKey(layout, format);
|
|
1112
|
+
const cached = cache$1.get(key);
|
|
1113
|
+
if (cached) {
|
|
1114
|
+
return cached;
|
|
1115
|
+
}
|
|
1116
|
+
const template = `${layout}.template.${format}`;
|
|
1117
|
+
const templateFile = path.join(templateDirectory, template);
|
|
1118
|
+
if (!fs$1.existsSync(templateFile)) {
|
|
1119
|
+
throw new MissingTemplateError(from, template, format, [
|
|
1120
|
+
templateDirectory
|
|
1121
|
+
]);
|
|
1122
|
+
}
|
|
1123
|
+
cache$1.set(key, template);
|
|
1124
|
+
return template;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const scriptPath = path.join(__dirname, "compile-example.js");
|
|
1128
|
+
let loader = null;
|
|
1129
|
+
function haveOutputFile(fileInfo) {
|
|
1130
|
+
return fileInfo.outputName !== false;
|
|
1131
|
+
}
|
|
1132
|
+
function fillActiveNavigation(doc, node) {
|
|
1133
|
+
if (isNavigationSection(node)) {
|
|
1134
|
+
const children = node.children.map(
|
|
1135
|
+
(it) => fillActiveNavigation(doc, it)
|
|
1136
|
+
);
|
|
1137
|
+
const active = children.some((it) => it.active);
|
|
1138
|
+
return {
|
|
1139
|
+
...node,
|
|
1140
|
+
active,
|
|
1141
|
+
children
|
|
1142
|
+
};
|
|
1143
|
+
} else {
|
|
1144
|
+
const active = doc.id === node.id;
|
|
1145
|
+
return {
|
|
1146
|
+
...node,
|
|
1147
|
+
active
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
function findSidenav(doc, tree) {
|
|
1152
|
+
const { path: path2, name } = doc.fileInfo;
|
|
1153
|
+
const category = path2 === "." ? name : path2.split("/")[1];
|
|
1154
|
+
const sidenav = tree.children.filter(isNavigationSection).find((it) => it.key === `./${category}`);
|
|
1155
|
+
if (sidenav) {
|
|
1156
|
+
return fillActiveNavigation(doc, sidenav);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
function cache(cacheFolder, outputFolder) {
|
|
1160
|
+
return (it) => {
|
|
1161
|
+
const cacheFile = path.join(cacheFolder, it.outputFile);
|
|
1162
|
+
const outputFile = path.join(outputFolder, it.outputFile);
|
|
1163
|
+
if (fs$1.existsSync(cacheFile)) {
|
|
1164
|
+
fs$1.copyFileSync(cacheFile, outputFile);
|
|
1165
|
+
return false;
|
|
1166
|
+
} else {
|
|
1167
|
+
return true;
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
async function compileExamples(options) {
|
|
1172
|
+
const { fileInfo, tasks, vendors } = options;
|
|
1173
|
+
if (tasks.length === 0) {
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
const cacheFolder = path.posix.join(options.cacheFolder, fileInfo.path);
|
|
1177
|
+
const outputFolder = path.posix.join(options.outputFolder, fileInfo.path);
|
|
1178
|
+
const cacheMiss = cache(cacheFolder, outputFolder);
|
|
1179
|
+
const dirtyTasks = tasks.filter(cacheMiss);
|
|
1180
|
+
const batch = {
|
|
1181
|
+
outputFolder,
|
|
1182
|
+
external: vendors.map((it) => it.package),
|
|
1183
|
+
tasks: dirtyTasks
|
|
1184
|
+
};
|
|
1185
|
+
const result = await vendor.execa("node", [scriptPath], {
|
|
1186
|
+
input: JSON.stringify(batch),
|
|
1187
|
+
all: true
|
|
1188
|
+
});
|
|
1189
|
+
const hasOutput = result.all ? result.all.length > 0 : false;
|
|
1190
|
+
if (hasOutput) {
|
|
1191
|
+
console.log(result.all);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
async function compileStandalones(options) {
|
|
1195
|
+
const { fileInfo, tasks, renderTemplate, templateData } = options;
|
|
1196
|
+
if (tasks.length === 0) {
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
const cacheFolder = path.posix.join(options.cacheFolder, fileInfo.path);
|
|
1200
|
+
const outputFolder = path.posix.join(options.outputFolder, fileInfo.path);
|
|
1201
|
+
const cacheMiss = cache(cacheFolder, outputFolder);
|
|
1202
|
+
const dirtyTasks = tasks.filter(cacheMiss);
|
|
1203
|
+
const standaloneTemplate = findTemplate(fileInfo, "example");
|
|
1204
|
+
for (const task of dirtyTasks) {
|
|
1205
|
+
const outputFile = path.join(outputFolder, task.outputFile);
|
|
1206
|
+
const content = await renderTemplate(standaloneTemplate, {
|
|
1207
|
+
...templateData,
|
|
1208
|
+
content: task.content
|
|
1209
|
+
});
|
|
1210
|
+
await fs.writeFile(outputFile, content, "utf-8");
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
function createTemplateLoader(folders) {
|
|
1214
|
+
loader = new TemplateLoader(folders);
|
|
1215
|
+
}
|
|
1216
|
+
async function render(doc, docs, nav, vendors, options) {
|
|
1217
|
+
const { fileInfo } = doc;
|
|
1218
|
+
const { outputFolder, cacheFolder } = options;
|
|
1219
|
+
if (!haveOutputFile(fileInfo)) {
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
const dst = createMarkdownRenderer.getOutputFilePath(outputFolder, fileInfo);
|
|
1223
|
+
const mkdir = fs.mkdir(path.dirname(dst), { recursive: true });
|
|
1224
|
+
const topnav = nav.topnav;
|
|
1225
|
+
const sidenav = findSidenav(doc, nav.sidenav);
|
|
1226
|
+
if (sidenav && isNavigationSection(sidenav)) {
|
|
1227
|
+
sortNavigationTree(sidenav);
|
|
1228
|
+
}
|
|
1229
|
+
const njk = new nunjucks.Environment(loader, { autoescape: false });
|
|
1230
|
+
const asyncRender = node_util.promisify(njk.render);
|
|
1231
|
+
const renderTemplate = asyncRender.bind(njk);
|
|
1232
|
+
const template = findTemplate(doc.fileInfo, doc);
|
|
1233
|
+
const templateData = {
|
|
1234
|
+
...options.templateData,
|
|
1235
|
+
site: options.site,
|
|
1236
|
+
doc,
|
|
1237
|
+
topnav,
|
|
1238
|
+
sidenav,
|
|
1239
|
+
vendors
|
|
1240
|
+
};
|
|
1241
|
+
const generatedExamples = [];
|
|
1242
|
+
const generatedStandalone = [];
|
|
1243
|
+
const markdownRenderer = createMarkdownRenderer.createMarkdownRenderer({
|
|
1244
|
+
docs,
|
|
1245
|
+
generateExample({ source, language, filename, tags }) {
|
|
1246
|
+
const example = generateExample({
|
|
1247
|
+
source,
|
|
1248
|
+
language,
|
|
1249
|
+
filename,
|
|
1250
|
+
parent: fileInfo.fullPath,
|
|
1251
|
+
setupPath: options.setupPath,
|
|
1252
|
+
exampleFolders: options.exampleFolders,
|
|
1253
|
+
tags
|
|
1254
|
+
});
|
|
1255
|
+
if (example.output) {
|
|
1256
|
+
const { dir, name } = path.parse(example.output);
|
|
1257
|
+
generatedExamples.push(example.task);
|
|
1258
|
+
generatedStandalone.push({
|
|
1259
|
+
outputFile: path.join(dir, `${name}.html`),
|
|
1260
|
+
content: example.markup
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
return example;
|
|
1264
|
+
},
|
|
1265
|
+
addResource(dst2, src) {
|
|
1266
|
+
if (!fs$1.existsSync(src)) {
|
|
1267
|
+
throw new Error(`Failed to find image "${src}"`);
|
|
1268
|
+
}
|
|
1269
|
+
options.addResource(dst2, src);
|
|
1270
|
+
},
|
|
1271
|
+
handleSoftError(error) {
|
|
1272
|
+
throw error;
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
njk.addFilter("marked", (content2) => {
|
|
1276
|
+
return markdownRenderer.render(doc, content2);
|
|
1277
|
+
});
|
|
1278
|
+
njk.addFilter("json", json);
|
|
1279
|
+
njk.addFilter("dump", dump);
|
|
1280
|
+
njk.addFilter("relative", relative);
|
|
1281
|
+
njk.addFilter("take", take);
|
|
1282
|
+
njk.addExtension("BlockContainer", {
|
|
1283
|
+
tags: ["container"],
|
|
1284
|
+
parse(parser, nodes) {
|
|
1285
|
+
const tok = parser.nextToken();
|
|
1286
|
+
const args = parser.parseSignature(null, true);
|
|
1287
|
+
parser.advanceAfterBlockEnd(tok.value);
|
|
1288
|
+
return new nodes.CallExtensionAsync(this, "run", args, []);
|
|
1289
|
+
},
|
|
1290
|
+
async run({ ctx }, container, callback) {
|
|
1291
|
+
const blocks = options.templateBlocks.get(container) ?? [];
|
|
1292
|
+
const promises = blocks.map(({ renderer }) => {
|
|
1293
|
+
if ("filename" in renderer) {
|
|
1294
|
+
const data = renderer.data;
|
|
1295
|
+
return renderTemplate(renderer.filename, {
|
|
1296
|
+
...ctx,
|
|
1297
|
+
...data
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
if ("render" in renderer) {
|
|
1301
|
+
return Promise.resolve(renderer.render());
|
|
1302
|
+
}
|
|
1303
|
+
return Promise.resolve("");
|
|
1304
|
+
});
|
|
1305
|
+
const content2 = await Promise.all(promises);
|
|
1306
|
+
const markup = content2.join("");
|
|
1307
|
+
return callback(null, new nunjucks.runtime.SafeString(markup));
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
await mkdir;
|
|
1311
|
+
let content;
|
|
1312
|
+
try {
|
|
1313
|
+
content = await renderTemplate(template, templateData);
|
|
1314
|
+
} catch (err) {
|
|
1315
|
+
const prefix = `Failed to render "${fileInfo.fullPath}"`;
|
|
1316
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1317
|
+
throw new Error(`${prefix}: ${message}`, { cause: err });
|
|
1318
|
+
}
|
|
1319
|
+
const expandedDst = dst.replace(/\[([^]+)\]/g, (match, key) => {
|
|
1320
|
+
if (key === "hash") {
|
|
1321
|
+
return createMarkdownRenderer.getFingerprint(doc.body);
|
|
1322
|
+
} else {
|
|
1323
|
+
return match;
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
const writeFile = fs.writeFile(expandedDst, content ?? "null", "utf-8");
|
|
1327
|
+
try {
|
|
1328
|
+
await compileExamples({
|
|
1329
|
+
fileInfo,
|
|
1330
|
+
cacheFolder,
|
|
1331
|
+
outputFolder,
|
|
1332
|
+
tasks: generatedExamples,
|
|
1333
|
+
vendors
|
|
1334
|
+
});
|
|
1335
|
+
} catch (err) {
|
|
1336
|
+
const prefix = `Failed to compile examples from "${fileInfo.fullPath}"`;
|
|
1337
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1338
|
+
throw new Error(`${prefix}: ${message}`, { cause: err });
|
|
1339
|
+
}
|
|
1340
|
+
try {
|
|
1341
|
+
await compileStandalones({
|
|
1342
|
+
fileInfo,
|
|
1343
|
+
cacheFolder,
|
|
1344
|
+
outputFolder,
|
|
1345
|
+
tasks: generatedStandalone,
|
|
1346
|
+
renderTemplate,
|
|
1347
|
+
templateData
|
|
1348
|
+
});
|
|
1349
|
+
} catch (err) {
|
|
1350
|
+
const prefix = `Failed to render standalone examples from "${fileInfo.fullPath}"`;
|
|
1351
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1352
|
+
throw new Error(`${prefix}: ${message}`, { cause: err });
|
|
1353
|
+
}
|
|
1354
|
+
await writeFile;
|
|
1355
|
+
return dst;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
const progressbar = new vendor.cliProgress.SingleBar({
|
|
1359
|
+
format: " {bar} {percentage}% | {value}/{total} documents | {filename}",
|
|
1360
|
+
barCompleteChar: "\u2588",
|
|
1361
|
+
barIncompleteChar: "\u2591",
|
|
1362
|
+
hideCursor: true,
|
|
1363
|
+
clearOnComplete: true
|
|
1364
|
+
});
|
|
1365
|
+
function nunjucksProcessor(options) {
|
|
1366
|
+
return {
|
|
1367
|
+
stage: "render",
|
|
1368
|
+
name: "nunjucks-renderer",
|
|
1369
|
+
async handler(context) {
|
|
1370
|
+
function renderDocument(doc) {
|
|
1371
|
+
const nav = {
|
|
1372
|
+
topnav: context.topnav,
|
|
1373
|
+
sidenav: context.sidenav
|
|
1374
|
+
};
|
|
1375
|
+
return render(doc, context.docs, nav, context.vendors, {
|
|
1376
|
+
...options,
|
|
1377
|
+
templateBlocks: context.getAllTemplateBlocks(),
|
|
1378
|
+
templateData: context.getAllTemplateData(),
|
|
1379
|
+
addResource(dst, src) {
|
|
1380
|
+
context.addResource(dst, src);
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
const docs = context.docs.filter((it) => {
|
|
1385
|
+
return it.fileInfo.outputName;
|
|
1386
|
+
});
|
|
1387
|
+
createTemplateLoader(options.templateFolders);
|
|
1388
|
+
progressbar.start(docs.length, 0, {
|
|
1389
|
+
filename: docs.length > 0 ? docs[0].fileInfo.fullPath : ""
|
|
1390
|
+
});
|
|
1391
|
+
const generatedFiles = [];
|
|
1392
|
+
try {
|
|
1393
|
+
for (const doc of context.docs) {
|
|
1394
|
+
const filePath = await renderDocument(doc);
|
|
1395
|
+
if (filePath) {
|
|
1396
|
+
generatedFiles.push(filePath);
|
|
1397
|
+
}
|
|
1398
|
+
progressbar.increment(1, {
|
|
1399
|
+
filename: doc.fileInfo.fullPath
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
} finally {
|
|
1403
|
+
progressbar.stop();
|
|
1404
|
+
}
|
|
1405
|
+
context.log(docs.length, "documents rendered");
|
|
1406
|
+
return generatedFiles;
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
function flattenOutline(outline) {
|
|
1412
|
+
return outline.flatMap((entry) => {
|
|
1413
|
+
return [
|
|
1414
|
+
{ heading: entry.title, anchor: entry.anchor },
|
|
1415
|
+
...flattenOutline(entry.subheadings)
|
|
1416
|
+
];
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
function findExamples(doc) {
|
|
1420
|
+
if (doc.format !== "markdown") {
|
|
1421
|
+
return [];
|
|
1422
|
+
}
|
|
1423
|
+
const collected = [];
|
|
1424
|
+
const md = vendor.MarkdownIt();
|
|
1425
|
+
md.renderer.rules.fence = (tokens, idx) => {
|
|
1426
|
+
const { content, info, map } = tokens[idx];
|
|
1427
|
+
const [language, ...tags] = info.split(/\s+/);
|
|
1428
|
+
const hashContent = `${map == null ? void 0 : map[0]}:${map == null ? void 0 : map[1]}:${info}:${content}`;
|
|
1429
|
+
const fingerprint = createMarkdownRenderer.getFingerprint(hashContent);
|
|
1430
|
+
collected.push({
|
|
1431
|
+
selector: `#example-${fingerprint}`,
|
|
1432
|
+
language,
|
|
1433
|
+
tags
|
|
1434
|
+
});
|
|
1435
|
+
return "";
|
|
1436
|
+
};
|
|
1437
|
+
md.render(doc.body);
|
|
1438
|
+
return collected;
|
|
1439
|
+
}
|
|
1440
|
+
function manifestPageFromDocument(doc) {
|
|
1441
|
+
const { fileInfo } = doc;
|
|
1442
|
+
return {
|
|
1443
|
+
path: createMarkdownRenderer.getOutputFilePath("", fileInfo),
|
|
1444
|
+
title: doc.attributes.title ?? doc.name ?? "",
|
|
1445
|
+
outline: flattenOutline(doc.outline),
|
|
1446
|
+
examples: findExamples(doc)
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
function customRequire(name) {
|
|
1451
|
+
if (typeof window.__modules__ === "undefined") {
|
|
1452
|
+
window.__modules__ = {};
|
|
1453
|
+
}
|
|
1454
|
+
const mod = window.__modules__[name];
|
|
1455
|
+
if (!mod) {
|
|
1456
|
+
throw new Error(`Cannot find module "${name}"`);
|
|
1457
|
+
}
|
|
1458
|
+
return mod;
|
|
1459
|
+
}
|
|
1460
|
+
function getAssetSource(asset, require) {
|
|
1461
|
+
const lines = [];
|
|
1462
|
+
const pkg = asset.package.replace(/\\/g, "/");
|
|
1463
|
+
if (!asset.expose) {
|
|
1464
|
+
return `import "${pkg}"`;
|
|
1465
|
+
}
|
|
1466
|
+
if (asset.expose === "named") {
|
|
1467
|
+
lines.push(`import * as lib from "${pkg}";`);
|
|
1468
|
+
} else {
|
|
1469
|
+
lines.push(`import lib from "${pkg}";`);
|
|
1470
|
+
}
|
|
1471
|
+
asset.subpaths.map((subpath, index) => {
|
|
1472
|
+
if (asset.expose === "named") {
|
|
1473
|
+
lines.push(`import * as lib$${index} from "${subpath}";`);
|
|
1474
|
+
} else {
|
|
1475
|
+
lines.push(`import lib$${index} from "${subpath}";`);
|
|
1476
|
+
}
|
|
1477
|
+
});
|
|
1478
|
+
lines.push(`window.require = window.require || ${require};`);
|
|
1479
|
+
lines.push(`window.__modules__ = window.__modules__ || {};`);
|
|
1480
|
+
lines.push(`window.__modules__["${asset.package}"] = lib;`);
|
|
1481
|
+
if (typeof asset.alias !== "undefined") {
|
|
1482
|
+
lines.push(`window.__modules__["${asset.alias}"] = lib;`);
|
|
1483
|
+
}
|
|
1484
|
+
asset.subpaths.map((subpath, index) => {
|
|
1485
|
+
lines.push(`window.__modules__["${subpath}"] = lib$${index};`);
|
|
1486
|
+
});
|
|
1487
|
+
if (typeof asset.global !== "undefined") {
|
|
1488
|
+
lines.push(`window["${asset.global}"] = lib;`);
|
|
1489
|
+
}
|
|
1490
|
+
return lines.join("\n");
|
|
1491
|
+
}
|
|
1492
|
+
async function compileVendor(assetFolder, vendor, options) {
|
|
1493
|
+
const name = path.isAbsolute(vendor.package) ? path.basename(vendor.package) : vendor.package;
|
|
1494
|
+
const slug = slugify(name);
|
|
1495
|
+
const outfile = `temp/vendor-${slug}.out.js`;
|
|
1496
|
+
const tmpfile = `temp/vendor-${slug}.in.js`;
|
|
1497
|
+
const source = getAssetSource(vendor, customRequire.toString());
|
|
1498
|
+
const tsconfig = path.resolve(__dirname, "../tsconfig-examples.json");
|
|
1499
|
+
await fs.writeFile(tmpfile, source, "utf-8");
|
|
1500
|
+
await esbuild.build({
|
|
1501
|
+
entryPoints: [tmpfile],
|
|
1502
|
+
outfile,
|
|
1503
|
+
bundle: true,
|
|
1504
|
+
format: "iife",
|
|
1505
|
+
platform: "browser",
|
|
1506
|
+
tsconfig,
|
|
1507
|
+
define: {
|
|
1508
|
+
"process.env.NODE_ENV": JSON.stringify(
|
|
1509
|
+
process.env.NODE_ENV ?? "production"
|
|
1510
|
+
)
|
|
1511
|
+
},
|
|
1512
|
+
alias: vendor.alias ? { [`${vendor.package}`]: vendor.alias } : void 0,
|
|
1513
|
+
...options
|
|
1514
|
+
});
|
|
1515
|
+
const content = await fs.readFile(outfile, "utf-8");
|
|
1516
|
+
const fingerprint = createMarkdownRenderer.getFingerprint(content);
|
|
1517
|
+
const integrity = getIntegrity(content);
|
|
1518
|
+
const filename = `vendor-${slug}-${fingerprint}.js`;
|
|
1519
|
+
const dst = path.join(assetFolder, filename).replace(/\\/g, "/");
|
|
1520
|
+
await fs.rename(outfile, dst);
|
|
1521
|
+
const stat = await fs.stat(dst);
|
|
1522
|
+
return {
|
|
1523
|
+
package: vendor.package,
|
|
1524
|
+
filename,
|
|
1525
|
+
publicPath: `./assets/${filename}`,
|
|
1526
|
+
integrity,
|
|
1527
|
+
size: stat.size
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
function normalizeVendorDefinition(vendor) {
|
|
1532
|
+
if (typeof vendor === "string") {
|
|
1533
|
+
return {
|
|
1534
|
+
package: vendor,
|
|
1535
|
+
global: void 0,
|
|
1536
|
+
expose: "named",
|
|
1537
|
+
subpaths: []
|
|
1538
|
+
};
|
|
1539
|
+
} else {
|
|
1540
|
+
return {
|
|
1541
|
+
package: vendor.package,
|
|
1542
|
+
global: vendor.global,
|
|
1543
|
+
expose: vendor.expose ?? "named",
|
|
1544
|
+
subpaths: vendor.subpaths ?? [],
|
|
1545
|
+
alias: vendor.alias ?? void 0
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
function getPackageName(vendor) {
|
|
1551
|
+
return typeof vendor === "string" ? vendor : vendor.package;
|
|
1552
|
+
}
|
|
1553
|
+
function generateVendorAssets(assetFolder, assets) {
|
|
1554
|
+
const packages = assets.map(getPackageName);
|
|
1555
|
+
const normalized = assets.map(normalizeVendorDefinition);
|
|
1556
|
+
const promises = normalized.map((it) => {
|
|
1557
|
+
const external = packages.filter((pkg) => pkg !== it.package);
|
|
1558
|
+
return compileVendor(assetFolder, it, {
|
|
1559
|
+
external
|
|
1560
|
+
});
|
|
1561
|
+
});
|
|
1562
|
+
return Promise.all(promises);
|
|
1563
|
+
}
|
|
1564
|
+
function vendorProcessor(assetFolder, vendor) {
|
|
1565
|
+
return {
|
|
1566
|
+
stage: "assets",
|
|
1567
|
+
name: "vendor-processor",
|
|
1568
|
+
async handler(context) {
|
|
1569
|
+
const assets = await generateVendorAssets(assetFolder, vendor);
|
|
1570
|
+
context.addVendorAsset(assets);
|
|
1571
|
+
for (const asset of assets) {
|
|
1572
|
+
const filename = path$1.basename(asset.publicPath);
|
|
1573
|
+
context.log(filename, formatSize(asset.size));
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
function livereloadProcessor(options) {
|
|
1580
|
+
const { enabled } = options;
|
|
1581
|
+
return {
|
|
1582
|
+
after: "generate-docs",
|
|
1583
|
+
name: "livereload-processor",
|
|
1584
|
+
enabled,
|
|
1585
|
+
handler(context) {
|
|
1586
|
+
context.addTemplateBlock("body:end", "livereload", {
|
|
1587
|
+
filename: "partials/livereload.html"
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
async function keypress() {
|
|
1594
|
+
process.stdin.setRawMode(true);
|
|
1595
|
+
process.stdin.resume();
|
|
1596
|
+
return new Promise((resolve) => {
|
|
1597
|
+
process.stdin.once("data", (data) => {
|
|
1598
|
+
process.stdin.setRawMode(false);
|
|
1599
|
+
process.stdin.resume();
|
|
1600
|
+
resolve(data.toString("utf-8"));
|
|
1601
|
+
});
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
function printMenu(addr) {
|
|
1606
|
+
console.log();
|
|
1607
|
+
console.group(
|
|
1608
|
+
`Starting development server at http://localhost:${addr.port}`
|
|
1609
|
+
);
|
|
1610
|
+
console.log();
|
|
1611
|
+
console.log("[q] quit");
|
|
1612
|
+
console.log();
|
|
1613
|
+
console.groupEnd();
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
function sleep(ms) {
|
|
1617
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1618
|
+
}
|
|
1619
|
+
function createRebuilder(callback, livereload) {
|
|
1620
|
+
let inflight = false;
|
|
1621
|
+
let queue = [];
|
|
1622
|
+
async function rebuild(filePath) {
|
|
1623
|
+
queue.push(filePath);
|
|
1624
|
+
if (inflight) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
inflight = true;
|
|
1628
|
+
try {
|
|
1629
|
+
while (queue.length > 0) {
|
|
1630
|
+
const modifiedFiles = queue;
|
|
1631
|
+
queue = [];
|
|
1632
|
+
const generatedFiles = await callback(modifiedFiles);
|
|
1633
|
+
livereload(generatedFiles);
|
|
1634
|
+
await sleep(200);
|
|
1635
|
+
}
|
|
1636
|
+
} finally {
|
|
1637
|
+
inflight = false;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
return rebuild;
|
|
1641
|
+
}
|
|
1642
|
+
async function serve(options) {
|
|
1643
|
+
const app = express();
|
|
1644
|
+
app.use(express.static(options.outputFolder));
|
|
1645
|
+
const server = app.listen(8080);
|
|
1646
|
+
printMenu(server.address());
|
|
1647
|
+
const livereload = vendor.tinylr();
|
|
1648
|
+
livereload.listen(35729);
|
|
1649
|
+
const watcher = vendor.chokidar.watch(options.watch);
|
|
1650
|
+
const rebuild = createRebuilder(options.rebuild, (filePath) => {
|
|
1651
|
+
const files = filePath.map((it) => {
|
|
1652
|
+
return path$1.relative(options.outputFolder, it);
|
|
1653
|
+
});
|
|
1654
|
+
livereload.changed({ body: { files } });
|
|
1655
|
+
});
|
|
1656
|
+
watcher.on("change", async (filePath) => {
|
|
1657
|
+
rebuild(filePath);
|
|
1658
|
+
});
|
|
1659
|
+
for (; ; ) {
|
|
1660
|
+
const key = await keypress();
|
|
1661
|
+
switch (key) {
|
|
1662
|
+
case "":
|
|
1663
|
+
case "q":
|
|
1664
|
+
console.log("Shutting down gracefully");
|
|
1665
|
+
server.close();
|
|
1666
|
+
watcher.close();
|
|
1667
|
+
livereload.close();
|
|
1668
|
+
process.stdin.unref();
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
function toArray$1(value) {
|
|
1675
|
+
return Array.isArray(value) ? value : [value];
|
|
1676
|
+
}
|
|
1677
|
+
function filterProcessors(stage2, processors) {
|
|
1678
|
+
const before = [];
|
|
1679
|
+
const during = [];
|
|
1680
|
+
const after = [];
|
|
1681
|
+
for (const processor of processors) {
|
|
1682
|
+
if (processor.enabled === false) {
|
|
1683
|
+
continue;
|
|
1684
|
+
}
|
|
1685
|
+
if (processor.before === stage2) {
|
|
1686
|
+
before.push(processor);
|
|
1687
|
+
}
|
|
1688
|
+
if (processor.stage === stage2) {
|
|
1689
|
+
during.push(processor);
|
|
1690
|
+
}
|
|
1691
|
+
if (processor.after === stage2) {
|
|
1692
|
+
after.push(processor);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return [...before, ...during, ...after];
|
|
1696
|
+
}
|
|
1697
|
+
async function stage(stage2, context, processors, { verbose } = { verbose: true }) {
|
|
1698
|
+
if (verbose) {
|
|
1699
|
+
console.log(`stage:${stage2}`);
|
|
1700
|
+
}
|
|
1701
|
+
let generatedFiles = [];
|
|
1702
|
+
const filteredProcessors = filterProcessors(stage2, processors);
|
|
1703
|
+
for (let i = 0; i < filteredProcessors.length; i++) {
|
|
1704
|
+
const processor = filteredProcessors[i];
|
|
1705
|
+
const isLast = i === filteredProcessors.length - 1;
|
|
1706
|
+
if (verbose) {
|
|
1707
|
+
console.log(isLast ? " \u2514\u2500" : " \u251C\u2500", processor.name);
|
|
1708
|
+
}
|
|
1709
|
+
try {
|
|
1710
|
+
const result = await processor.handler({
|
|
1711
|
+
...context,
|
|
1712
|
+
log(...args) {
|
|
1713
|
+
if (verbose) {
|
|
1714
|
+
console.log(isLast ? " " : " \u2502 ", ...args);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
if (result) {
|
|
1719
|
+
generatedFiles = [...generatedFiles, ...result];
|
|
1720
|
+
}
|
|
1721
|
+
} catch (err) {
|
|
1722
|
+
console.error(`When running processor "${processor.name}":`);
|
|
1723
|
+
console.error(err);
|
|
1724
|
+
throw err;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
return generatedFiles;
|
|
1728
|
+
}
|
|
1729
|
+
function createContext() {
|
|
1730
|
+
let docs = [];
|
|
1731
|
+
let vendors = [];
|
|
1732
|
+
const resources = [];
|
|
1733
|
+
const templateBlocks = /* @__PURE__ */ new Map();
|
|
1734
|
+
let topnav = {
|
|
1735
|
+
key: ".",
|
|
1736
|
+
title: "",
|
|
1737
|
+
path: "",
|
|
1738
|
+
sortorder: Infinity,
|
|
1739
|
+
children: []
|
|
1740
|
+
};
|
|
1741
|
+
let sidenav = {
|
|
1742
|
+
key: ".",
|
|
1743
|
+
title: "",
|
|
1744
|
+
path: "",
|
|
1745
|
+
sortorder: Infinity,
|
|
1746
|
+
children: []
|
|
1747
|
+
};
|
|
1748
|
+
const templateData = {};
|
|
1749
|
+
return {
|
|
1750
|
+
get docs() {
|
|
1751
|
+
return docs;
|
|
1752
|
+
},
|
|
1753
|
+
get vendors() {
|
|
1754
|
+
return vendors;
|
|
1755
|
+
},
|
|
1756
|
+
get resources() {
|
|
1757
|
+
return resources;
|
|
1758
|
+
},
|
|
1759
|
+
get topnav() {
|
|
1760
|
+
return topnav;
|
|
1761
|
+
},
|
|
1762
|
+
get sidenav() {
|
|
1763
|
+
return sidenav;
|
|
1764
|
+
},
|
|
1765
|
+
addDocument(document) {
|
|
1766
|
+
docs = [...docs, ...toArray$1(document)];
|
|
1767
|
+
},
|
|
1768
|
+
addVendorAsset(asset) {
|
|
1769
|
+
vendors = [...vendors, ...toArray$1(asset)];
|
|
1770
|
+
},
|
|
1771
|
+
addResource(dst, src) {
|
|
1772
|
+
resources.push({
|
|
1773
|
+
from: src.replace(/\\/g, "/"),
|
|
1774
|
+
to: dst
|
|
1775
|
+
});
|
|
1776
|
+
},
|
|
1777
|
+
addTemplateBlock(container, id, renderer) {
|
|
1778
|
+
const block = {
|
|
1779
|
+
container,
|
|
1780
|
+
id,
|
|
1781
|
+
renderer
|
|
1782
|
+
};
|
|
1783
|
+
const list = templateBlocks.get(container) ?? [];
|
|
1784
|
+
templateBlocks.set(container, [...list, block]);
|
|
1785
|
+
},
|
|
1786
|
+
getAllTemplateBlocks() {
|
|
1787
|
+
return templateBlocks;
|
|
1788
|
+
},
|
|
1789
|
+
setTopNavigation(root) {
|
|
1790
|
+
topnav = root;
|
|
1791
|
+
},
|
|
1792
|
+
setSideNavigation(root) {
|
|
1793
|
+
sidenav = root;
|
|
1794
|
+
},
|
|
1795
|
+
getAllTemplateData() {
|
|
1796
|
+
return templateData;
|
|
1797
|
+
},
|
|
1798
|
+
getTemplateData(key) {
|
|
1799
|
+
return templateData[key];
|
|
1800
|
+
},
|
|
1801
|
+
setTemplateData(key, value) {
|
|
1802
|
+
templateData[key] = value;
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
class Generator {
|
|
1807
|
+
site;
|
|
1808
|
+
outputFolder;
|
|
1809
|
+
cacheFolder;
|
|
1810
|
+
assetFolder;
|
|
1811
|
+
exampleFolders;
|
|
1812
|
+
templateFolders;
|
|
1813
|
+
processors;
|
|
1814
|
+
vendor;
|
|
1815
|
+
setupPath;
|
|
1816
|
+
scripts;
|
|
1817
|
+
styles;
|
|
1818
|
+
resources;
|
|
1819
|
+
sourceFiles;
|
|
1820
|
+
constructor(options) {
|
|
1821
|
+
if (typeof options.site === "undefined") {
|
|
1822
|
+
throw new Error("site metadata not set in configuration");
|
|
1823
|
+
}
|
|
1824
|
+
this.site = options.site;
|
|
1825
|
+
this.outputFolder = options.outputFolder;
|
|
1826
|
+
this.cacheFolder = options.cacheFolder;
|
|
1827
|
+
this.assetFolder = path.posix.join(options.outputFolder, "assets");
|
|
1828
|
+
this.exampleFolders = options.exampleFolders;
|
|
1829
|
+
this.templateFolders = options.templateFolders ?? [];
|
|
1830
|
+
this.processors = options.processors ?? [];
|
|
1831
|
+
this.vendor = options.vendor ?? [];
|
|
1832
|
+
this.setupPath = options.setupPath.replace(/\\/g, "/");
|
|
1833
|
+
this.scripts = [];
|
|
1834
|
+
this.styles = [];
|
|
1835
|
+
this.resources = [];
|
|
1836
|
+
this.sourceFiles = [];
|
|
1837
|
+
}
|
|
1838
|
+
compileScript(name, src, options) {
|
|
1839
|
+
this.scripts.push({
|
|
1840
|
+
name,
|
|
1841
|
+
src,
|
|
1842
|
+
options: {
|
|
1843
|
+
appendTo: "none",
|
|
1844
|
+
attributes: {},
|
|
1845
|
+
...options
|
|
1846
|
+
}
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
compileStyle(name, src, options) {
|
|
1850
|
+
this.styles.push({
|
|
1851
|
+
name,
|
|
1852
|
+
src,
|
|
1853
|
+
options: {
|
|
1854
|
+
appendTo: "none",
|
|
1855
|
+
attributes: {},
|
|
1856
|
+
...options
|
|
1857
|
+
}
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
/**
|
|
1861
|
+
* @param dst - Destination directory relative to asset folder.
|
|
1862
|
+
* @param src - File or directory to copy.
|
|
1863
|
+
*/
|
|
1864
|
+
copyResource(dst, src) {
|
|
1865
|
+
this.resources.push({
|
|
1866
|
+
from: src.replace(/\\/g, "/"),
|
|
1867
|
+
to: dst
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Generate a manifest listing all generated documents that will be present
|
|
1872
|
+
* in `outputFolder`.
|
|
1873
|
+
*
|
|
1874
|
+
* Note: this only collects documents from the `generate-docs` stage,
|
|
1875
|
+
* potential documents generated at later stages will not be present.
|
|
1876
|
+
*
|
|
1877
|
+
* @public
|
|
1878
|
+
*/
|
|
1879
|
+
async manifest(sourceFiles) {
|
|
1880
|
+
const processors = [
|
|
1881
|
+
fileReaderProcessor(sourceFiles),
|
|
1882
|
+
...this.processors
|
|
1883
|
+
];
|
|
1884
|
+
const context = createContext();
|
|
1885
|
+
await stage("generate-docs", context, processors, { verbose: false });
|
|
1886
|
+
const docs = context.docs.filter(haveOutput);
|
|
1887
|
+
const pages = docs.map(manifestPageFromDocument);
|
|
1888
|
+
return { pages };
|
|
1889
|
+
}
|
|
1890
|
+
async build(sourceFiles) {
|
|
1891
|
+
this.sourceFiles = sourceFiles;
|
|
1892
|
+
const {
|
|
1893
|
+
site,
|
|
1894
|
+
outputFolder,
|
|
1895
|
+
cacheFolder,
|
|
1896
|
+
assetFolder,
|
|
1897
|
+
exampleFolders,
|
|
1898
|
+
templateFolders,
|
|
1899
|
+
setupPath
|
|
1900
|
+
} = this;
|
|
1901
|
+
await this._prepareFolders();
|
|
1902
|
+
const processors = [
|
|
1903
|
+
fileReaderProcessor(sourceFiles),
|
|
1904
|
+
vendorProcessor(assetFolder, this.vendor),
|
|
1905
|
+
cssAssetProcessor(assetFolder, this.styles),
|
|
1906
|
+
jsAssetProcessor(assetFolder, this.scripts),
|
|
1907
|
+
staticResourcesProcessor(assetFolder, this.resources),
|
|
1908
|
+
navigationProcessor(),
|
|
1909
|
+
nunjucksProcessor({
|
|
1910
|
+
site: {
|
|
1911
|
+
lang: "en",
|
|
1912
|
+
...site
|
|
1913
|
+
},
|
|
1914
|
+
outputFolder,
|
|
1915
|
+
cacheFolder,
|
|
1916
|
+
exampleFolders,
|
|
1917
|
+
templateFolders,
|
|
1918
|
+
setupPath
|
|
1919
|
+
}),
|
|
1920
|
+
...this.processors
|
|
1921
|
+
];
|
|
1922
|
+
const context = createContext();
|
|
1923
|
+
const generatedFiles = [
|
|
1924
|
+
await stage("generate-docs", context, processors),
|
|
1925
|
+
await stage("generate-nav", context, processors),
|
|
1926
|
+
await stage("assets", context, processors),
|
|
1927
|
+
await stage("render", context, processors)
|
|
1928
|
+
];
|
|
1929
|
+
return generatedFiles.flat();
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Start a development server hosting the generated documentation.
|
|
1933
|
+
*/
|
|
1934
|
+
serve() {
|
|
1935
|
+
const { outputFolder, sourceFiles } = this;
|
|
1936
|
+
const watch = sourceFiles.map((it) => it.include).flat();
|
|
1937
|
+
return serve({
|
|
1938
|
+
outputFolder,
|
|
1939
|
+
watch,
|
|
1940
|
+
rebuild: (_filePath) => {
|
|
1941
|
+
return this.build(sourceFiles);
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
async _prepareFolders() {
|
|
1946
|
+
const { outputFolder, cacheFolder, assetFolder } = this;
|
|
1947
|
+
await fs.rm(cacheFolder, { force: true, recursive: true });
|
|
1948
|
+
if (fs$1.existsSync(outputFolder)) {
|
|
1949
|
+
await fs.mkdir(path.dirname(cacheFolder), { recursive: true });
|
|
1950
|
+
await vendor.fse.copy(outputFolder, cacheFolder);
|
|
1951
|
+
await fs.rm(outputFolder, { recursive: true });
|
|
1952
|
+
}
|
|
1953
|
+
await fs.mkdir(outputFolder, { recursive: true });
|
|
1954
|
+
await fs.mkdir(assetFolder, { recursive: true });
|
|
1955
|
+
await fs.mkdir("temp", { recursive: true });
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
function toArray(value) {
|
|
1960
|
+
return Array.isArray(value) ? value : [value];
|
|
1961
|
+
}
|
|
1962
|
+
function matomoProcessor(options) {
|
|
1963
|
+
const { enabled, siteId, apiUrl, trackerUrl, hostname } = options;
|
|
1964
|
+
return {
|
|
1965
|
+
after: "generate-docs",
|
|
1966
|
+
name: "matomo-processor",
|
|
1967
|
+
enabled,
|
|
1968
|
+
handler(context) {
|
|
1969
|
+
context.addTemplateBlock("head", "matomo", {
|
|
1970
|
+
filename: "partials/matomo.html",
|
|
1971
|
+
data: {
|
|
1972
|
+
siteId,
|
|
1973
|
+
apiUrl,
|
|
1974
|
+
trackerUrl,
|
|
1975
|
+
hostname: hostname ? toArray(hostname) : []
|
|
1976
|
+
}
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
function renderMarkdown(manifest) {
|
|
1983
|
+
return [
|
|
1984
|
+
"## Documentation manifest",
|
|
1985
|
+
"",
|
|
1986
|
+
"> Do not edit this file. It is a automatically generated by `@forsakringskassan/docs-generator`.",
|
|
1987
|
+
"",
|
|
1988
|
+
"```",
|
|
1989
|
+
...manifest.pages.map((it) => it.path),
|
|
1990
|
+
"```",
|
|
1991
|
+
""
|
|
1992
|
+
].join("\n");
|
|
1993
|
+
}
|
|
1994
|
+
function renderJSON(manifest) {
|
|
1995
|
+
return JSON.stringify(manifest, null, 2);
|
|
1996
|
+
}
|
|
1997
|
+
function manifestProcessor(options) {
|
|
1998
|
+
const { markdown, json } = options;
|
|
1999
|
+
return {
|
|
2000
|
+
name: "manifestProcessor",
|
|
2001
|
+
before: "render",
|
|
2002
|
+
async handler(context) {
|
|
2003
|
+
const docs = context.docs.filter(haveOutput);
|
|
2004
|
+
const pages = docs.map(manifestPageFromDocument);
|
|
2005
|
+
pages.sort((a, b) => {
|
|
2006
|
+
return a.path.localeCompare(b.path);
|
|
2007
|
+
});
|
|
2008
|
+
const manifest = { pages };
|
|
2009
|
+
if (markdown) {
|
|
2010
|
+
const content = renderMarkdown(manifest);
|
|
2011
|
+
await fs.writeFile(markdown, content, "utf-8");
|
|
2012
|
+
}
|
|
2013
|
+
if (json) {
|
|
2014
|
+
const content = renderJSON(manifest);
|
|
2015
|
+
await fs.writeFile(json, content, "utf-8");
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
function themeSelectProcessor() {
|
|
2022
|
+
return {
|
|
2023
|
+
name: "theme-select-processor",
|
|
2024
|
+
after: "generate-docs",
|
|
2025
|
+
handler(context) {
|
|
2026
|
+
context.addTemplateBlock("toolbar", "theme-select", {
|
|
2027
|
+
filename: "partials/theme-select.html"
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
function selectableVersionProcessor(pkg, container) {
|
|
2034
|
+
return {
|
|
2035
|
+
name: "selectable-version-processor",
|
|
2036
|
+
after: "generate-docs",
|
|
2037
|
+
handler(context) {
|
|
2038
|
+
context.addTemplateBlock(container, "version", {
|
|
2039
|
+
filename: "partials/selectable-version.html",
|
|
2040
|
+
data: { pkg }
|
|
2041
|
+
});
|
|
2042
|
+
}
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
function generateNavigation(entries) {
|
|
2047
|
+
const children = entries.map((it, index) => {
|
|
2048
|
+
const key = path$1.parse(it.path).name;
|
|
2049
|
+
return {
|
|
2050
|
+
key,
|
|
2051
|
+
sortorder: index,
|
|
2052
|
+
children: [],
|
|
2053
|
+
...it
|
|
2054
|
+
};
|
|
2055
|
+
});
|
|
2056
|
+
return {
|
|
2057
|
+
key: ".",
|
|
2058
|
+
path: "./index.html",
|
|
2059
|
+
sortorder: Infinity,
|
|
2060
|
+
children
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
function topnavProcessor(filename, title) {
|
|
2064
|
+
return {
|
|
2065
|
+
after: "generate-nav",
|
|
2066
|
+
name: "topnav-processor",
|
|
2067
|
+
async handler(context) {
|
|
2068
|
+
const content = await fs.readFile(filename, "utf-8");
|
|
2069
|
+
const parsed = JSON.parse(content);
|
|
2070
|
+
const rootNode = generateNavigation(parsed);
|
|
2071
|
+
context.setTopNavigation({
|
|
2072
|
+
title,
|
|
2073
|
+
...rootNode
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
function run(cmd, defaultValue) {
|
|
2080
|
+
return new Promise((resolve) => {
|
|
2081
|
+
childProcess.exec(cmd, { encoding: "utf-8" }, (error, stdout) => {
|
|
2082
|
+
if (error) {
|
|
2083
|
+
console.error(error);
|
|
2084
|
+
resolve(defaultValue);
|
|
2085
|
+
} else {
|
|
2086
|
+
resolve(stdout.trim());
|
|
2087
|
+
}
|
|
2088
|
+
});
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
async function getGitBranch() {
|
|
2092
|
+
const { CHANGE_BRANCH, CHANGE_NAME } = process.env;
|
|
2093
|
+
if (CHANGE_BRANCH) {
|
|
2094
|
+
return CHANGE_BRANCH;
|
|
2095
|
+
}
|
|
2096
|
+
if (CHANGE_NAME) {
|
|
2097
|
+
return CHANGE_NAME;
|
|
2098
|
+
}
|
|
2099
|
+
const branch = await run("git rev-parse --abbrev-ref HEAD", "unknown");
|
|
2100
|
+
if (branch === "HEAD" || branch === "") {
|
|
2101
|
+
return "unknown";
|
|
2102
|
+
} else {
|
|
2103
|
+
return branch;
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
function getPullRequestID() {
|
|
2107
|
+
return process.env.JOB_BASE_NAME;
|
|
2108
|
+
}
|
|
2109
|
+
async function getSCMData(pkg, options) {
|
|
2110
|
+
const commitHash = await run(`git rev-parse HEAD`);
|
|
2111
|
+
const commitShort = await run(`git rev-parse --short HEAD`);
|
|
2112
|
+
if (!commitHash || !commitShort) {
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
2115
|
+
const { homepage } = pkg;
|
|
2116
|
+
const pr = getPullRequestID();
|
|
2117
|
+
const prUrl = pr ? interpolate(options.prUrlFormat, { homepage, pr }) : void 0;
|
|
2118
|
+
return {
|
|
2119
|
+
branch: await getGitBranch(),
|
|
2120
|
+
commitShort,
|
|
2121
|
+
commitHash,
|
|
2122
|
+
commitUrl: interpolate(options.commitUrlFormat, {
|
|
2123
|
+
hash: commitHash,
|
|
2124
|
+
short: commitShort,
|
|
2125
|
+
homepage
|
|
2126
|
+
}),
|
|
2127
|
+
pr,
|
|
2128
|
+
prUrl
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
function getBuildDatetime() {
|
|
2132
|
+
const date = /* @__PURE__ */ new Date();
|
|
2133
|
+
return {
|
|
2134
|
+
date: date.toLocaleDateString("sv-SE"),
|
|
2135
|
+
time: date.toLocaleTimeString("sv-SE")
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
2138
|
+
function versionProcessor(pkg, container, options) {
|
|
2139
|
+
const { enabled = true, scm = false } = options ?? {};
|
|
2140
|
+
return {
|
|
2141
|
+
name: "version-processor",
|
|
2142
|
+
after: "generate-docs",
|
|
2143
|
+
async handler(context) {
|
|
2144
|
+
if (!enabled) {
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
const scmData = scm ? await getSCMData(pkg, scm) : null;
|
|
2148
|
+
const buildData = getBuildDatetime();
|
|
2149
|
+
const data = {
|
|
2150
|
+
pkg,
|
|
2151
|
+
scm: scmData,
|
|
2152
|
+
build: buildData
|
|
2153
|
+
};
|
|
2154
|
+
context.addTemplateBlock(container, "version", {
|
|
2155
|
+
filename: "partials/version.njk.html",
|
|
2156
|
+
data
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
function versionBannerProcessor(message, container) {
|
|
2163
|
+
return {
|
|
2164
|
+
name: "version-banner-processor",
|
|
2165
|
+
after: "generate-docs",
|
|
2166
|
+
handler(context) {
|
|
2167
|
+
context.addTemplateBlock(container, "version-banner", {
|
|
2168
|
+
filename: "partials/version-banner.html",
|
|
2169
|
+
data: { message }
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
function generateIndex(entries) {
|
|
2176
|
+
const index = {
|
|
2177
|
+
terms: [],
|
|
2178
|
+
results: [],
|
|
2179
|
+
mapping: {}
|
|
2180
|
+
};
|
|
2181
|
+
for (const entry of entries) {
|
|
2182
|
+
index.results.push({
|
|
2183
|
+
url: entry.url,
|
|
2184
|
+
title: entry.title
|
|
2185
|
+
});
|
|
2186
|
+
const resultIndex = index.results.length - 1;
|
|
2187
|
+
for (const word of entry.words) {
|
|
2188
|
+
index.terms.push(word);
|
|
2189
|
+
const termIndex = index.terms.length - 1;
|
|
2190
|
+
index.mapping[termIndex] = resultIndex;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
return index;
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
function extractTerms(doc) {
|
|
2197
|
+
const { title, component } = doc.attributes;
|
|
2198
|
+
if (title && component) {
|
|
2199
|
+
return component.map((it) => `${title} (${it})`);
|
|
2200
|
+
} else if (component) {
|
|
2201
|
+
return component;
|
|
2202
|
+
} else if (title) {
|
|
2203
|
+
return [title];
|
|
2204
|
+
} else {
|
|
2205
|
+
return [];
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
function isIndexable(doc) {
|
|
2209
|
+
return Boolean(doc.visible && doc.fileInfo.outputName);
|
|
2210
|
+
}
|
|
2211
|
+
function getTerms(doc) {
|
|
2212
|
+
return {
|
|
2213
|
+
url: createMarkdownRenderer.getOutputFilePath(".", doc.fileInfo) ?? "",
|
|
2214
|
+
title: doc.name,
|
|
2215
|
+
words: extractTerms(doc)
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
function searchProcessor() {
|
|
2219
|
+
return {
|
|
2220
|
+
after: "generate-docs",
|
|
2221
|
+
name: "search-processor",
|
|
2222
|
+
handler(context) {
|
|
2223
|
+
const entries = context.docs.filter(isIndexable).map(getTerms);
|
|
2224
|
+
const index = generateIndex(entries);
|
|
2225
|
+
const body = JSON.stringify(index);
|
|
2226
|
+
const fingerprint = createMarkdownRenderer.getFingerprint(body);
|
|
2227
|
+
const integrity = getIntegrity(body);
|
|
2228
|
+
const outputName = `search-data-[hash].json`;
|
|
2229
|
+
const expandedName = `search-data-${fingerprint}.json`;
|
|
2230
|
+
context.setTemplateData("searchDataUrl", expandedName);
|
|
2231
|
+
context.setTemplateData("searchDataIntegrity", integrity);
|
|
2232
|
+
context.addDocument({
|
|
2233
|
+
id: "search:data",
|
|
2234
|
+
name: "search-data",
|
|
2235
|
+
alias: [],
|
|
2236
|
+
visible: false,
|
|
2237
|
+
attributes: {
|
|
2238
|
+
sortorder: Infinity
|
|
2239
|
+
},
|
|
2240
|
+
body,
|
|
2241
|
+
outline: [],
|
|
2242
|
+
format: "json",
|
|
2243
|
+
tags: [],
|
|
2244
|
+
template: "json",
|
|
2245
|
+
fileInfo: {
|
|
2246
|
+
path: "",
|
|
2247
|
+
name: "virtual:search-data",
|
|
2248
|
+
fullPath: "virtual:search-data",
|
|
2249
|
+
outputName
|
|
2250
|
+
}
|
|
2251
|
+
});
|
|
2252
|
+
context.addTemplateBlock("toolbar", "search-toolbar", {
|
|
2253
|
+
filename: "partials/search-toolbar.html",
|
|
2254
|
+
data: {
|
|
2255
|
+
rootUrl(doc) {
|
|
2256
|
+
const { fileInfo } = doc;
|
|
2257
|
+
const relative = path$1.relative(fileInfo.path, ".");
|
|
2258
|
+
return relative !== "" ? relative : ".";
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2262
|
+
context.addTemplateBlock("body:end", "search-dialog", {
|
|
2263
|
+
filename: "partials/search-dialog.html"
|
|
2264
|
+
});
|
|
2265
|
+
context.log("Indexed", entries.length, "documents");
|
|
2266
|
+
}
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
exports.Generator = Generator;
|
|
2271
|
+
exports.defineSources = defineSources;
|
|
2272
|
+
exports.frontMatterFileReader = frontMatterFileReader;
|
|
2273
|
+
exports.livereloadProcessor = livereloadProcessor;
|
|
2274
|
+
exports.manifestProcessor = manifestProcessor;
|
|
2275
|
+
exports.matomoProcessor = matomoProcessor;
|
|
2276
|
+
exports.navigationFileReader = navigationFileReader;
|
|
2277
|
+
exports.searchProcessor = searchProcessor;
|
|
2278
|
+
exports.selectableVersionProcessor = selectableVersionProcessor;
|
|
2279
|
+
exports.themeSelectProcessor = themeSelectProcessor;
|
|
2280
|
+
exports.topnavProcessor = topnavProcessor;
|
|
2281
|
+
exports.versionBannerProcessor = versionBannerProcessor;
|
|
2282
|
+
exports.versionProcessor = versionProcessor;
|
|
2283
|
+
exports.vueFileReader = vueFileReader;
|