@forsakringskassan/docs-generator 2.12.0 → 2.14.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/README.md CHANGED
@@ -11,10 +11,17 @@ npm install --save-dev --save-exact @forsakringskassan/docs-generator
11
11
  You can start a development server with watch for incremental builds using `.serve()`.
12
12
 
13
13
  ```ts
14
+ import {
15
+ Generator,
16
+ livereloadProcessor,
17
+ } from "@forsakringskassan/docs-generator";
18
+
14
19
  const docs = new Generator({
15
- /* ... */
20
+ site: {
21
+ name: "My Awesome Site",
22
+ },
23
+ setupPath: "docs/src/setup.ts",
16
24
  processors: [livereloadProcessor({ enabled: true })],
17
- /* ... */
18
25
  });
19
26
 
20
27
  docs.build([
@@ -29,8 +36,17 @@ docs.serve();
29
36
  Vendor alias is optional, it lets you substitute one package for another when bundling. The example below substitutes the package `vue` with the package `vue/dist/esm.bundle.js`. When an alias is given the package can be imported using the given alias as well as the original package name. For instance, when using the following configuration "vue" can be imported and resolved as "vue/dist/esm.bundle.js".
30
37
 
31
38
  ```ts
39
+ import {
40
+ Generator,
41
+ livereloadProcessor,
42
+ } from "@forsakringskassan/docs-generator";
43
+
32
44
  const docs = new Generator({
33
- /* ... */
45
+ site: {
46
+ name: "My Awesome Site",
47
+ },
48
+ setupPath: "docs/src/setup.ts",
49
+
34
50
  vendor: [
35
51
  {
36
52
  package: "vue",
@@ -39,7 +55,6 @@ const docs = new Generator({
39
55
  alias: "vue/dist/esm.bundle.js",
40
56
  },
41
57
  ],
42
- /* ... */
43
58
  });
44
59
  ```
45
60
 
@@ -55,7 +70,7 @@ npm install --save-dev --save-exact @vue/component-compiler
55
70
 
56
71
  Create the `setup` callback in `docs/src/setup.ts`:
57
72
 
58
- ```ts
73
+ ```ts nocompile
59
74
  import Vue from "vue";
60
75
  import { type SetupOptions } from "@forsakringskassan/docs-generator";
61
76
 
@@ -73,7 +88,7 @@ export function setup(options: SetupOptions): void {
73
88
 
74
89
  Create the `setup` callback in `docs/src/setup.ts`:
75
90
 
76
- ```ts
91
+ ```ts nocompile
77
92
  import { createApp } from "vue";
78
93
  import { type SetupOptions } from "@forsakringskassan/docs-generator";
79
94
 
@@ -102,8 +117,13 @@ Three CSS files are provided:
102
117
  Style can be compiled with `compileStyle(name, src, [options])`:
103
118
 
104
119
  ```ts
120
+ import { Generator } from "@forsakringskassan/docs-generator";
121
+
105
122
  const docs = new Generator({
106
- /* ... */
123
+ site: {
124
+ name: "My Awesome Site",
125
+ },
126
+ setupPath: "docs/src/setup.ts",
107
127
  });
108
128
 
109
129
  docs.compileStyle("docs", "./docs/src/style.scss", {
@@ -274,7 +294,7 @@ Defaults to `@fkui/icon-lib-default`.
274
294
  Makes it possible to dynamically load icons and get access to icon metadata.
275
295
  The library must have been built using `@fkui/icon-lib-builder`.
276
296
 
277
- ```js
297
+ ```js nocompile
278
298
  const icons = await import(process.env.DOCS_ICON_LIB);
279
299
 
280
300
  for (const entry of Object.values(icons)) {
@@ -3,10 +3,10 @@
3
3
  var path$1 = require('node:path');
4
4
  var esbuild = require('esbuild');
5
5
  var vue = require('vue');
6
- var vue3 = require('./vue3-rkJOanAd.js');
6
+ var vue3 = require('./vue3-BqgRpcI0.js');
7
7
  var path = require('node:path/posix');
8
8
  require('@vue/compiler-sfc');
9
- require('./vendor-CJBsNZrh.js');
9
+ require('./vendor-Bb38Rlio.js');
10
10
  require('node:url');
11
11
  require('fs');
12
12
  require('node:fs');
@@ -14,6 +14,7 @@ require('node:fs/promises');
14
14
  require('node:events');
15
15
  require('node:stream');
16
16
  require('node:string_decoder');
17
+ require('typescript');
17
18
  require('constants');
18
19
  require('stream');
19
20
  require('util');
@@ -25,7 +26,6 @@ require('node:process');
25
26
  require('node:util');
26
27
  require('node:child_process');
27
28
  require('node:stream/promises');
28
- require('typescript');
29
29
  require('fs/promises');
30
30
  require('os');
31
31
  require('http');
@@ -1,11 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var vendor = require('./vendor-CJBsNZrh.js');
3
+ var vendor = require('./vendor-Bb38Rlio.js');
4
4
  var path = require('node:path/posix');
5
5
  var path$1 = require('node:path');
6
6
  require('crypto');
7
7
  var crypto = require('node:crypto');
8
8
  require('node:child_process');
9
+ var fs = require('node:fs');
10
+ var vue = require('vue');
11
+ var vue3 = require('./vue3-BqgRpcI0.js');
9
12
 
10
13
  function findDocument(haystack, needle) {
11
14
  const match = haystack.find((it) => {
@@ -65,6 +68,236 @@ function getOutputFilePath(outputFolder, fileInfo) {
65
68
  return normalizePath(outputFolder, path, outputName);
66
69
  }
67
70
 
71
+ function parseImport(raw) {
72
+ const comments = [];
73
+ const stripped = raw.replace(/<!--.*?-->/gms, (match) => {
74
+ comments.push(match);
75
+ return "";
76
+ });
77
+ const filename = stripped.trim();
78
+ const extension = path$1.parse(filename).ext.slice(1);
79
+ return {
80
+ filename,
81
+ extension,
82
+ comments
83
+ };
84
+ }
85
+
86
+ function getExampleImport(searchDirs, filename) {
87
+ const pattern = searchDirs.map((dir) => `${dir}/**/${filename}`);
88
+ const matches = vendor.globSync(pattern);
89
+ if (matches.length === 0) {
90
+ const message = `No files matched import "${filename}"`;
91
+ throw new Error(message);
92
+ } else if (matches.length > 1) {
93
+ const message = `Multiple files matched import "${filename}"`;
94
+ throw new Error(message);
95
+ } else {
96
+ return normalizePath(matches[0]);
97
+ }
98
+ }
99
+
100
+ function getExampleName(filename) {
101
+ return path$1.parse(filename).name;
102
+ }
103
+
104
+ function removeLinesInline(lines, { begin, end }) {
105
+ for (let i = begin; i <= end; i++) {
106
+ lines[i] = "";
107
+ }
108
+ if (lines[begin - 1] === "\n") {
109
+ lines[begin - 1] = "";
110
+ begin--;
111
+ }
112
+ if (lines[end + 1] === "\n") {
113
+ lines[end + 1] = "";
114
+ }
115
+ if (end === lines.length - 1 && begin > 0) {
116
+ lines[begin - 1] = lines[begin - 1].slice(0, -1);
117
+ }
118
+ }
119
+ function cutSnippets(code) {
120
+ const lines = code.split(/\n/).map((it, index, lines2) => index < lines2.length - 1 ? `${it}
121
+ ` : it);
122
+ const directive = /\/\* +--- +cut (above|below|begin|end) +--- +\*\//;
123
+ let buffer = -1;
124
+ lines.forEach((line, index) => {
125
+ const match = line.match(directive);
126
+ const [, instruction] = match ?? ["", null];
127
+ if (!instruction) {
128
+ return;
129
+ }
130
+ switch (instruction) {
131
+ /* --- cut above --- */
132
+ case "above":
133
+ removeLinesInline(lines, {
134
+ begin: 0,
135
+ end: index
136
+ });
137
+ break;
138
+ /* --- cut below --- */
139
+ case "below":
140
+ removeLinesInline(lines, {
141
+ begin: index,
142
+ end: lines.length - 1
143
+ });
144
+ break;
145
+ /* --- cut begin --- */
146
+ case "begin":
147
+ if (buffer === -1) {
148
+ buffer = index;
149
+ }
150
+ break;
151
+ /* --- cut end --- */
152
+ case "end":
153
+ removeLinesInline(lines, {
154
+ begin: Math.max(buffer, 0),
155
+ end: index
156
+ });
157
+ buffer = -1;
158
+ break;
159
+ }
160
+ });
161
+ if (buffer >= 0) {
162
+ removeLinesInline(lines, {
163
+ begin: buffer,
164
+ end: lines.length - 1
165
+ });
166
+ }
167
+ return lines.join("");
168
+ }
169
+ function stripEslintComments(code) {
170
+ const matchLine = /^[ \t]*(\/\* eslint-disable[^*]*\*\/|\/\/ eslint-disable.*)\n/gm;
171
+ const matchEmbedded = /[ \t]*(\/\* eslint-disable[^*]*\*\/|\/\/ eslint-disable.*)/g;
172
+ return code.replace(matchLine, "").replace(matchEmbedded, "");
173
+ }
174
+ function maybeDedent(value) {
175
+ if (/^\s*$/.test(value)) {
176
+ return value;
177
+ }
178
+ if (/^\s/.test(value)) {
179
+ return vendor.dedent(value);
180
+ } else {
181
+ return value;
182
+ }
183
+ }
184
+ const transformations = {
185
+ javascript: [cutSnippets, stripEslintComments, maybeDedent],
186
+ typescript: [cutSnippets, stripEslintComments, maybeDedent]
187
+ };
188
+ function transformCode(code, lang) {
189
+ const fns = transformations[lang] ?? [];
190
+ for (const fn of fns) {
191
+ code = fn(code);
192
+ }
193
+ return code;
194
+ }
195
+
196
+ const vueMajor = parseInt(vue.version.split(".", 2)[0], 10);
197
+ function vueGenerator() {
198
+ switch (vueMajor) {
199
+ case 2:
200
+ throw new Error(
201
+ "Vue 2 is no longer supported, upgrade to Vue 3 or downgrade docs-generator"
202
+ );
203
+ case 3:
204
+ return vue3.generateCode;
205
+ }
206
+ }
207
+ function generateExample(options) {
208
+ const { language } = options;
209
+ if (language === "import") {
210
+ const parsed = parseImport(options.source);
211
+ const filename = getExampleImport(
212
+ options.exampleFolders,
213
+ parsed.filename
214
+ );
215
+ const language2 = parsed.extension;
216
+ const comments = parsed.comments;
217
+ const content = fs.readFileSync(filename, "utf-8");
218
+ const source = transformCode(content, language2);
219
+ const example = generateExample({
220
+ ...options,
221
+ source,
222
+ language: language2,
223
+ filename
224
+ });
225
+ return { ...example, comments };
226
+ }
227
+ switch (language) {
228
+ case "vue":
229
+ return generateVueExample(options);
230
+ case "html":
231
+ return generateStaticExample(options);
232
+ default:
233
+ return generateStaticExample(options);
234
+ }
235
+ }
236
+ function generateVueExample(options) {
237
+ const { filename, source, parent, setupPath, tags } = options;
238
+ const fn = vueGenerator();
239
+ const slug = getExampleName(filename);
240
+ const fingerprint = getFingerprint(source);
241
+ const { markup, sourcecode, output } = fn({
242
+ filename,
243
+ slug,
244
+ fingerprint,
245
+ code: source,
246
+ setupPath
247
+ });
248
+ return {
249
+ source,
250
+ language: options.language,
251
+ comments: [],
252
+ tags,
253
+ markup,
254
+ output,
255
+ runtime: true,
256
+ task: {
257
+ outputFile: output,
258
+ sourcecode,
259
+ sourceFile: filename,
260
+ parent
261
+ }
262
+ };
263
+ }
264
+ function generateStaticExample(options) {
265
+ const { filename, source, language, tags } = options;
266
+ const slug = getExampleName(filename);
267
+ const fingerprint = getFingerprint(source);
268
+ const asset = `${slug}-${fingerprint}.${language}`;
269
+ const runtimeLanguages = ["html"];
270
+ const runtime = runtimeLanguages.includes(language);
271
+ return {
272
+ source,
273
+ language: options.language,
274
+ comments: [],
275
+ tags,
276
+ markup: source,
277
+ output: runtime ? asset : null,
278
+ runtime,
279
+ task: null
280
+ };
281
+ }
282
+
283
+ function normalizeLanguage(lang) {
284
+ switch (lang) {
285
+ case "ts":
286
+ return "typescript";
287
+ case "js":
288
+ return "javascript";
289
+ default:
290
+ return lang;
291
+ }
292
+ }
293
+ function parseInfostring(infostring) {
294
+ const [rawLanguage, ...rawTags] = infostring.trim().split(/\s+/);
295
+ return {
296
+ language: normalizeLanguage(rawLanguage),
297
+ tags: rawTags
298
+ };
299
+ }
300
+
68
301
  class SoftError extends Error {
69
302
  code;
70
303
  details;
@@ -274,16 +507,17 @@ function codePreview(options) {
274
507
  };
275
508
  function fence(_md, tokens, idx, _options, env) {
276
509
  const { fileInfo } = env;
277
- const { content: source, info, map } = tokens[idx];
278
- const [language, ...rawTags] = info.split(/\s+/);
510
+ const { content: rawSource, info, map } = tokens[idx];
511
+ const { language, tags: rawTags } = parseInfostring(info);
279
512
  if (language === "mermaid") {
280
513
  return (
281
514
  /* HTML */
282
515
  `
283
- <pre class="mermaid">${htmlencode(source)}</pre>
516
+ <pre class="mermaid">${htmlencode(rawSource)}</pre>
284
517
  `
285
518
  );
286
519
  }
520
+ const source = transformCode(rawSource, language);
287
521
  const example = generateExample({
288
522
  source,
289
523
  language,
@@ -774,7 +1008,9 @@ function createMarkdownRenderer(options) {
774
1008
 
775
1009
  exports.SoftError = SoftError;
776
1010
  exports.createMarkdownRenderer = createMarkdownRenderer;
1011
+ exports.generateExample = generateExample;
777
1012
  exports.generateId = generateId;
778
1013
  exports.getFingerprint = getFingerprint;
779
1014
  exports.getOutputFilePath = getOutputFilePath;
780
1015
  exports.normalizePath = normalizePath;
1016
+ exports.parseInfostring = parseInfostring;
package/dist/index.d.ts CHANGED
@@ -112,6 +112,23 @@ export declare interface DocumentOutlineEntry {
112
112
  subheadings: DocumentOutline;
113
113
  }
114
114
 
115
+ /**
116
+ * Options for `extractExamplesProcessor`.
117
+ *
118
+ * @public
119
+ */
120
+ export declare interface ExtractExamplesOptions extends ProcessorOptions {
121
+ outputFolder: string;
122
+ languages?: string[];
123
+ }
124
+
125
+ /**
126
+ * Processor to extract and write examples to separate files.
127
+ *
128
+ * @public
129
+ */
130
+ export declare function extractExamplesProcessor(options: ExtractExamplesOptions): Processor;
131
+
115
132
  /**
116
133
  * @public
117
134
  */
@@ -199,10 +216,12 @@ export { Generator_2 as Generator }
199
216
  export declare interface GeneratorOptions {
200
217
  /** Site options */
201
218
  site: GeneratorSiteOptions;
202
- outputFolder: string;
203
- cacheFolder: string;
204
- /** List of folders to search when locating examples (searched recursively) */
205
- exampleFolders: string[];
219
+ /** Output folder. Default: `public` */
220
+ outputFolder?: string;
221
+ /** Where cached content is stored. Default: `temp/docs` */
222
+ cacheFolder?: string;
223
+ /** List of folders to search when locating examples (searched recursively). Default: `[]` */
224
+ exampleFolders?: string[];
206
225
  /** List of folders to search when locating templates. */
207
226
  templateFolders?: string[];
208
227
  /** List of extra processors to run */
@@ -216,11 +235,11 @@ export declare interface GeneratorOptions {
216
235
  /**
217
236
  * Options for markdown renderer.
218
237
  */
219
- markdown: {
238
+ markdown?: {
220
239
  /**
221
240
  * Options for markdown messagebox container.
222
241
  */
223
- messagebox: {
242
+ messagebox?: {
224
243
  /**
225
244
  * Default titles for messageboxes.
226
245
  *
@@ -345,7 +364,7 @@ export declare interface MOTDOptions {
345
364
  *
346
365
  * @public
347
366
  */
348
- export declare function motdProcessor(options: MOTDOptions): Processor;
367
+ export declare function motdProcessor(options?: MOTDOptions): Processor;
349
368
 
350
369
  /**
351
370
  * Read a JSON file with arbitrary navigation entries.