-
-
Notifications
You must be signed in to change notification settings - Fork 9.9k
Expand file tree
/
Copy pathsvelte-docgen.ts
More file actions
138 lines (115 loc) · 4.03 KB
/
svelte-docgen.ts
File metadata and controls
138 lines (115 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import type { PluginOption } from 'vite';
import MagicString from 'magic-string';
import path from 'path';
import fs from 'fs';
import svelteDoc from 'sveltedoc-parser';
import type { SvelteParserOptions } from 'sveltedoc-parser';
import { logger } from '@storybook/node-logger';
import { preprocess } from 'svelte/compiler';
import { createFilter } from 'vite';
import { replace, typescript } from 'svelte-preprocess';
/*
* Patch sveltedoc-parser internal options.
* Waiting for a fix for https://github.com/alexprey/sveltedoc-parser/issues/87
*/
const svelteDocParserOptions = require('sveltedoc-parser/lib/options.js');
svelteDocParserOptions.getAstDefaultOptions = () => ({
range: true,
loc: true,
comment: true,
tokens: true,
ecmaVersion: 12,
sourceType: 'module',
ecmaFeatures: {},
});
// Most of the code here should probably be exported by @storybook/svelte and reused here.
// See: https://github.com/storybookjs/storybook/blob/next/app/svelte/src/server/svelte-docgen-loader.ts
// From https://github.com/sveltejs/svelte/blob/8db3e8d0297e052556f0b6dde310ef6e197b8d18/src/compiler/compile/utils/get_name_from_filename.ts
// Copied because it is not exported from the compiler
function getNameFromFilename(filename: string) {
if (!filename) return null;
const parts = filename.split(/[/\\]/).map(encodeURI);
if (parts.length > 1) {
const indexMatch = parts[parts.length - 1].match(/^index(\.\w+)/);
if (indexMatch) {
parts.pop();
parts[parts.length - 1] += indexMatch[1];
}
}
const base = parts
.pop()
?.replace(/%/g, 'u')
.replace(/\.[^.]+$/, '')
.replace(/[^a-zA-Z_$0-9]+/g, '_')
.replace(/^_/, '')
.replace(/_$/, '')
.replace(/^(\d)/, '_$1');
if (!base) {
throw new Error(`Could not derive component name from file ${filename}`);
}
return base[0].toUpperCase() + base.slice(1);
}
export function svelteDocgen(svelteOptions: Record<string, any> = {}): PluginOption {
const cwd = process.cwd();
const { preprocess: preprocessOptions, logDocgen = false } = svelteOptions;
const include = /\.(svelte)$/;
const filter = createFilter(include);
let docPreprocessOptions: any = null;
if (preprocessOptions) {
/*
* We can't use vitePreprocess() for the documentation
* because it uses esbuild which removes jsdoc.
*
* By default, only typescript is transpiled, and style tags are removed.
*
* Note: these preprocessors are only used to make the component
* compatible to sveltedoc-parser (no ts), not to compile
* the component.
*/
docPreprocessOptions = [typescript(), replace([[/<style.+<\/style>/gims, '']])];
}
return {
name: 'storybook:svelte-docgen-plugin',
async transform(src: string, id: string) {
if (!filter(id)) return undefined;
const resource = path.relative(cwd, id);
let docOptions;
if (docPreprocessOptions) {
// eslint-disable-next-line @typescript-eslint/no-shadow
const src = fs.readFileSync(resource).toString();
const { code: fileContent } = await preprocess(src, docPreprocessOptions, {
filename: resource,
});
docOptions = {
fileContent,
};
} else {
docOptions = { filename: resource };
}
// set SvelteDoc options
const options: SvelteParserOptions = {
...docOptions,
version: 3,
};
const s = new MagicString(src);
let componentDoc: any;
try {
componentDoc = await svelteDoc.parse(options);
} catch (error: any) {
componentDoc = { keywords: [], data: [] };
if (logDocgen) {
logger.error(error);
}
}
// get filename for source content
const file = path.basename(resource);
componentDoc.name = path.basename(file);
const componentName = getNameFromFilename(resource);
s.append(`;${componentName}.__docgen = ${JSON.stringify(componentDoc)}`);
return {
code: s.toString(),
map: s.generateMap({ hires: true, source: id }),
};
},
};
}