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