@forsakringskassan/docs-generator 2.12.0 → 2.13.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,226 @@ 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
+ const transformations = {
175
+ javascript: [cutSnippets, stripEslintComments],
176
+ typescript: [cutSnippets, stripEslintComments]
177
+ };
178
+ function transformCode(code, lang) {
179
+ const fns = transformations[lang] ?? [];
180
+ for (const fn of fns) {
181
+ code = fn(code);
182
+ }
183
+ return code;
184
+ }
185
+
186
+ const vueMajor = parseInt(vue.version.split(".", 2)[0], 10);
187
+ function vueGenerator() {
188
+ switch (vueMajor) {
189
+ case 2:
190
+ throw new Error(
191
+ "Vue 2 is no longer supported, upgrade to Vue 3 or downgrade docs-generator"
192
+ );
193
+ case 3:
194
+ return vue3.generateCode;
195
+ }
196
+ }
197
+ function generateExample(options) {
198
+ const { language } = options;
199
+ if (language === "import") {
200
+ const parsed = parseImport(options.source);
201
+ const filename = getExampleImport(
202
+ options.exampleFolders,
203
+ parsed.filename
204
+ );
205
+ const language2 = parsed.extension;
206
+ const comments = parsed.comments;
207
+ const content = fs.readFileSync(filename, "utf-8");
208
+ const source = transformCode(content, language2);
209
+ const example = generateExample({
210
+ ...options,
211
+ source,
212
+ language: language2,
213
+ filename
214
+ });
215
+ return { ...example, comments };
216
+ }
217
+ switch (language) {
218
+ case "vue":
219
+ return generateVueExample(options);
220
+ case "html":
221
+ return generateStaticExample(options);
222
+ default:
223
+ return generateStaticExample(options);
224
+ }
225
+ }
226
+ function generateVueExample(options) {
227
+ const { filename, source, parent, setupPath, tags } = options;
228
+ const fn = vueGenerator();
229
+ const slug = getExampleName(filename);
230
+ const fingerprint = getFingerprint(source);
231
+ const { markup, sourcecode, output } = fn({
232
+ filename,
233
+ slug,
234
+ fingerprint,
235
+ code: source,
236
+ setupPath
237
+ });
238
+ return {
239
+ source,
240
+ language: options.language,
241
+ comments: [],
242
+ tags,
243
+ markup,
244
+ output,
245
+ runtime: true,
246
+ task: {
247
+ outputFile: output,
248
+ sourcecode,
249
+ sourceFile: filename,
250
+ parent
251
+ }
252
+ };
253
+ }
254
+ function generateStaticExample(options) {
255
+ const { filename, source, language, tags } = options;
256
+ const slug = getExampleName(filename);
257
+ const fingerprint = getFingerprint(source);
258
+ const asset = `${slug}-${fingerprint}.${language}`;
259
+ const runtimeLanguages = ["html"];
260
+ const runtime = runtimeLanguages.includes(language);
261
+ return {
262
+ source,
263
+ language: options.language,
264
+ comments: [],
265
+ tags,
266
+ markup: source,
267
+ output: runtime ? asset : null,
268
+ runtime,
269
+ task: null
270
+ };
271
+ }
272
+
273
+ function normalizeLanguage(lang) {
274
+ switch (lang) {
275
+ case "ts":
276
+ return "typescript";
277
+ case "js":
278
+ return "javascript";
279
+ default:
280
+ return lang;
281
+ }
282
+ }
283
+ function parseInfostring(infostring) {
284
+ const [rawLanguage, ...rawTags] = infostring.trim().split(/\s+/);
285
+ return {
286
+ language: normalizeLanguage(rawLanguage),
287
+ tags: rawTags
288
+ };
289
+ }
290
+
68
291
  class SoftError extends Error {
69
292
  code;
70
293
  details;
@@ -274,16 +497,17 @@ function codePreview(options) {
274
497
  };
275
498
  function fence(_md, tokens, idx, _options, env) {
276
499
  const { fileInfo } = env;
277
- const { content: source, info, map } = tokens[idx];
278
- const [language, ...rawTags] = info.split(/\s+/);
500
+ const { content: rawSource, info, map } = tokens[idx];
501
+ const { language, tags: rawTags } = parseInfostring(info);
279
502
  if (language === "mermaid") {
280
503
  return (
281
504
  /* HTML */
282
505
  `
283
- <pre class="mermaid">${htmlencode(source)}</pre>
506
+ <pre class="mermaid">${htmlencode(rawSource)}</pre>
284
507
  `
285
508
  );
286
509
  }
510
+ const source = transformCode(rawSource, language);
287
511
  const example = generateExample({
288
512
  source,
289
513
  language,
@@ -774,7 +998,9 @@ function createMarkdownRenderer(options) {
774
998
 
775
999
  exports.SoftError = SoftError;
776
1000
  exports.createMarkdownRenderer = createMarkdownRenderer;
1001
+ exports.generateExample = generateExample;
777
1002
  exports.generateId = generateId;
778
1003
  exports.getFingerprint = getFingerprint;
779
1004
  exports.getOutputFilePath = getOutputFilePath;
780
1005
  exports.normalizePath = normalizePath;
1006
+ 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.