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