@vanilla-bean/components 1.1.1 → 2.0.1
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/Component/Component.js +222 -36
- package/Component/Component.test.js +425 -38
- package/Component/README.md +480 -0
- package/Elem/README.md +373 -0
- package/README.md +1 -1
- package/components/BottomSheet/BottomSheet.js +8 -16
- package/components/Button/Button.js +12 -18
- package/components/Calendar/Calendar.js +139 -59
- package/components/Calendar/CalendarEvent.js +9 -4
- package/components/Calendar/Toolbar.js +28 -20
- package/components/Calendar/index.js +1 -0
- package/components/Code/Code.js +26 -30
- package/components/ColorPicker/ColorPicker.js +57 -57
- package/components/Dialog/Dialog.js +78 -72
- package/components/Form/Form.js +12 -8
- package/components/Icon/Icon.js +19 -11
- package/components/Input/Input.js +85 -67
- package/components/Input/README.md +0 -2
- package/components/Keyboard/Key.js +7 -10
- package/components/Keyboard/Keyboard.js +38 -46
- package/components/Label/Label.js +52 -50
- package/components/Link/Link.js +14 -20
- package/components/List/List.js +28 -30
- package/components/Menu/Menu.js +19 -10
- package/components/Menu/Menu.lld.md +2 -2
- package/components/Notify/Notify.js +29 -19
- package/components/Popover/Popover.js +49 -44
- package/components/RadioButton/RadioButton.js +33 -29
- package/components/Router/Router.js +19 -21
- package/components/Select/Select.js +24 -30
- package/components/Table/Table.js +55 -36
- package/components/TagList/Tag.js +16 -14
- package/components/TagList/TagList.js +15 -8
- package/components/Tooltip/Tooltip.js +12 -25
- package/components/TooltipWrapper/TooltipWrapper.js +55 -49
- package/components/Whiteboard/Whiteboard.js +45 -43
- package/devTools/build.js +43 -0
- package/devTools/buildTypes.js +322 -0
- package/devTools/createComponent.js +155 -0
- package/devTools/extractJSDoc.js +395 -0
- package/devTools/processTemplate.js +500 -0
- package/devTools/updateComponentIndex.js +16 -0
- package/devTools/updateDemoViewIndex.js +90 -0
- package/eslint.config.cjs +6 -3
- package/index.d.ts +117 -59
- package/package.json +67 -22
- package/spellcheck.config.cjs +4 -0
- package/styled/README.md +329 -0
- package/theme/colors.js +15 -12
- package/theme/colors.test.js +28 -0
- package/utils/element.js +2 -2
- package/FontWithASyntaxHighlighter-Regular.woff2 +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts JSDoc information from JavaScript files for documentation generation.
|
|
5
|
+
*
|
|
6
|
+
* Parses JSDoc comments and extracts component descriptions, parameters, methods,
|
|
7
|
+
* properties, and events for automated README generation.
|
|
8
|
+
* @param {string} filePath - Path to JavaScript file to parse
|
|
9
|
+
* @returns {object} Extracted JSDoc information structured for documentation templates
|
|
10
|
+
*/
|
|
11
|
+
export function extractJSDoc(filePath) {
|
|
12
|
+
try {
|
|
13
|
+
const content = readFileSync(filePath, 'utf8');
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
description: extractDescription(content),
|
|
17
|
+
options: extractOptions(content),
|
|
18
|
+
methods: extractMethods(content),
|
|
19
|
+
properties: extractProperties(content),
|
|
20
|
+
events: extractEvents(content),
|
|
21
|
+
imports: extractImports(content),
|
|
22
|
+
examples: extractExamples(content),
|
|
23
|
+
};
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.warn(`Failed to extract JSDoc from ${filePath}:`, error.message);
|
|
26
|
+
return {
|
|
27
|
+
description: '',
|
|
28
|
+
options: [],
|
|
29
|
+
methods: [],
|
|
30
|
+
properties: [],
|
|
31
|
+
events: [],
|
|
32
|
+
imports: [],
|
|
33
|
+
examples: [],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extracts component description from class-level JSDoc comment.
|
|
40
|
+
* @param {string} content - File content to parse
|
|
41
|
+
* @returns {string} Component description text
|
|
42
|
+
*/
|
|
43
|
+
function extractDescription(content) {
|
|
44
|
+
// Match class JSDoc comment - handle both export default class and class patterns
|
|
45
|
+
const classMatches = [
|
|
46
|
+
content.match(/\/\*\*\s*\n([\s\S]*?)\*\/\s*export\s+default\s+class\s+\w+/),
|
|
47
|
+
content.match(/\/\*\*\s*\n([\s\S]*?)\*\/\s*(?:export\s+)?class\s+\w+/),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const classMatch = classMatches.find(match => match);
|
|
51
|
+
if (!classMatch) return '';
|
|
52
|
+
|
|
53
|
+
const jsdoc = classMatch[1];
|
|
54
|
+
|
|
55
|
+
// Extract description (everything before first @tag)
|
|
56
|
+
const descMatch = jsdoc.match(/^\s*\*\s*(.+?)(?=\s*^\s*\*\s*@|\s*$)/ms);
|
|
57
|
+
|
|
58
|
+
if (!descMatch) return '';
|
|
59
|
+
|
|
60
|
+
// Clean up the description text - handle multi-line descriptions
|
|
61
|
+
const lines = descMatch[1].split('\n');
|
|
62
|
+
const cleanedLines = lines.map(line => line.replace(/^\s*\*\s?/, '')).filter(line => line.trim() !== '');
|
|
63
|
+
|
|
64
|
+
return cleanedLines.join(' ').replace(/\s+/g, ' ').trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extracts constructor options from JSDoc `@param` tags.
|
|
69
|
+
* @param {string} content - File content to parse
|
|
70
|
+
* @returns {Array} Array of option objects with name, type, description, and default
|
|
71
|
+
*/
|
|
72
|
+
function extractOptions(content) {
|
|
73
|
+
// Find class-level JSDoc (which contains constructor info) or constructor JSDoc
|
|
74
|
+
const jsDocMatches = [
|
|
75
|
+
content.match(/\/\*\*\s*\n([\s\S]*?)\*\/\s*export\s+default\s+class\s+\w+/),
|
|
76
|
+
content.match(/\/\*\*\s*\n([\s\S]*?)\*\/\s*constructor\s*\(/),
|
|
77
|
+
content.match(/\/\*\*\s*\n([\s\S]*?)\*\/\s*(?:export\s+)?class\s+\w+/),
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const jsDocMatch = jsDocMatches.find(match => match);
|
|
81
|
+
if (!jsDocMatch) return [];
|
|
82
|
+
|
|
83
|
+
const jsdoc = jsDocMatch[1];
|
|
84
|
+
|
|
85
|
+
// Extract @param tags for options (handle both options.prop and direct param patterns)
|
|
86
|
+
const paramMatches = [
|
|
87
|
+
// options.property pattern
|
|
88
|
+
...jsdoc.matchAll(
|
|
89
|
+
/^\s*\*\s*@param\s+{([^}]+)}\s+(\[?options\.(\w+)(?:=([^\]]+))?\]?)\s*-\s*(.+?)(?=\s*^\s*\*\s*@|\s*^\s*\*\s*$|\s*\*\/)/gms,
|
|
90
|
+
),
|
|
91
|
+
// Direct parameter pattern (for constructor params like options={})
|
|
92
|
+
...jsdoc.matchAll(
|
|
93
|
+
/^\s*\*\s*@param\s+{([^}]+)}\s+(\[?(\w+)(?:=([^\]]+))?\]?)\s*-\s*(.+?)(?=\s*^\s*\*\s*@|\s*^\s*\*\s*$|\s*\*\/)/gms,
|
|
94
|
+
),
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
const codeDefaults = extractDefaultOptionsFromCode(content);
|
|
98
|
+
|
|
99
|
+
return paramMatches
|
|
100
|
+
.filter(match => match[2].includes('options.'))
|
|
101
|
+
.map(match => {
|
|
102
|
+
const [, type, fullParam, name, defaultValue, description] = match;
|
|
103
|
+
const optional = fullParam.startsWith('[') && fullParam.endsWith(']');
|
|
104
|
+
const optionName = name.replace('options.', '');
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
name: optionName,
|
|
108
|
+
type: type.trim(),
|
|
109
|
+
description: description.trim().replace(/\s+/g, ' '),
|
|
110
|
+
optional,
|
|
111
|
+
default: defaultValue || codeDefaults[optionName] || null,
|
|
112
|
+
isSubProperty: fullParam.includes('options.'),
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Reads the `defaultOptions` object literal from source and extracts simple key→value pairs.
|
|
119
|
+
* Only captures string, number, and boolean literals - skips getters and complex values.
|
|
120
|
+
* @param {string} content - File content
|
|
121
|
+
* @returns {object} Map of option name → default value string
|
|
122
|
+
*/
|
|
123
|
+
function extractDefaultOptionsFromCode(content) {
|
|
124
|
+
const defaults = {};
|
|
125
|
+
const block = content.match(/const\s+defaultOptions\s*=\s*\{([\s\S]*?)\};/);
|
|
126
|
+
if (!block) return defaults;
|
|
127
|
+
|
|
128
|
+
const pairs = [
|
|
129
|
+
...block[1].matchAll(/\b(\w+):\s*(?:'([^']*)'|"([^"]*)"|(`[^`]*`)|(-?\d+\.?\d*(?:e[+-]?\d+)?)|true|false)/g),
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
pairs.forEach(match => {
|
|
133
|
+
const [full, key, singleQuoted, doubleQuoted, template, number] = match;
|
|
134
|
+
if (singleQuoted !== undefined) defaults[key] = `'${singleQuoted}'`;
|
|
135
|
+
else if (doubleQuoted !== undefined) defaults[key] = `"${doubleQuoted}"`;
|
|
136
|
+
else if (template !== undefined) defaults[key] = template;
|
|
137
|
+
else if (number !== undefined) defaults[key] = number;
|
|
138
|
+
else if (full.endsWith('true')) defaults[key] = 'true';
|
|
139
|
+
else if (full.endsWith('false')) defaults[key] = 'false';
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return defaults;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Extracts method information from JSDoc comments.
|
|
147
|
+
* @param {string} content - File content to parse
|
|
148
|
+
* @returns {Array} Array of method objects with name, description, parameters, and return info
|
|
149
|
+
*/
|
|
150
|
+
function extractMethods(content) {
|
|
151
|
+
// Match method JSDoc comments (excluding constructor and private methods)
|
|
152
|
+
// Match JSDoc comments that are within the class body, not at class level
|
|
153
|
+
|
|
154
|
+
// Find class body content (everything between class declaration and closing brace)
|
|
155
|
+
const classBodyMatch = content.match(/class\s+\w+[^{]*\{([\s\S]*)\}[^}]*$/);
|
|
156
|
+
if (!classBodyMatch) return [];
|
|
157
|
+
|
|
158
|
+
const classBody = classBodyMatch[1];
|
|
159
|
+
|
|
160
|
+
// Match each JSDoc block with the method that IMMEDIATELY follows it.
|
|
161
|
+
// `(?:(?!\/\*\*)[\s\S])*?` - non-greedy content that cannot cross into a new `/**`,
|
|
162
|
+
// so the captured block can never span multiple JSDoc comments or method bodies.
|
|
163
|
+
// get/set accessors are excluded; underscore-prefixed names are excluded.
|
|
164
|
+
const matches = [
|
|
165
|
+
...classBody.matchAll(
|
|
166
|
+
/\/\*\*((?:(?!\/\*\*)[\s\S])*?)\*\/[ \t]*\n[ \t]*(?!constructor\b)(?!get\b)(?!set\b)(?!_)(\w+)\s*\([^)]*\)\s*\{/g,
|
|
167
|
+
),
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
return matches.map(match => {
|
|
171
|
+
const [, jsdoc, name] = match;
|
|
172
|
+
|
|
173
|
+
// Extract description
|
|
174
|
+
const descMatch = jsdoc.match(/^\s*\*\s*(.+?)(?=\s*^\s*\*\s*@|\s*$)/ms);
|
|
175
|
+
const description = descMatch
|
|
176
|
+
? descMatch[1]
|
|
177
|
+
.split('\n')
|
|
178
|
+
.map(line => line.replace(/^\s*\*\s?/, ''))
|
|
179
|
+
.join(' ')
|
|
180
|
+
.replace(/\s+/g, ' ')
|
|
181
|
+
.trim()
|
|
182
|
+
: '';
|
|
183
|
+
|
|
184
|
+
// Extract parameters
|
|
185
|
+
const paramMatches = [
|
|
186
|
+
...jsdoc.matchAll(
|
|
187
|
+
/^\s*\*\s*@param\s+{([^}]+)}\s+(\[?(\w+)(?:=([^\]]+))?\]?)\s*-\s*(.+?)(?=\s*^\s*\*\s*@|\s*^\s*\*\s*$)/gms,
|
|
188
|
+
),
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
const parameters = paramMatches.map(paramMatch => {
|
|
192
|
+
const [, type, fullParam, name, defaultValue, desc] = paramMatch;
|
|
193
|
+
const optional = fullParam.startsWith('[') && fullParam.endsWith(']');
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
name,
|
|
197
|
+
type: type.trim(),
|
|
198
|
+
description: desc.trim().replace(/\s+/g, ' '),
|
|
199
|
+
optional,
|
|
200
|
+
default: defaultValue || null,
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Extract return information - type only; description is not used in type generation.
|
|
205
|
+
const returnMatch = jsdoc.match(/^\s*\*\s*@returns?\s+\{([^}]+)\}/m);
|
|
206
|
+
const returnInfo = returnMatch ? { type: returnMatch[1].trim() } : null;
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
name,
|
|
210
|
+
description,
|
|
211
|
+
parameters,
|
|
212
|
+
returns: returnInfo,
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Extracts property information from JSDoc comments and getter methods.
|
|
219
|
+
* @param {string} content - File content to parse
|
|
220
|
+
* @returns {Array} Array of property objects with name, type, description, and access info
|
|
221
|
+
*/
|
|
222
|
+
function extractProperties(content) {
|
|
223
|
+
const properties = [];
|
|
224
|
+
|
|
225
|
+
// Extract getter properties - same anti-span pattern as extractMethods.
|
|
226
|
+
const getterMatches = [
|
|
227
|
+
...content.matchAll(/\/\*\*((?:(?!\/\*\*)[\s\S])*?)\*\/[ \t]*\n[ \t]*get\s+(\w+)\s*\(\)\s*\{/g),
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
getterMatches.forEach(match => {
|
|
231
|
+
const [, jsdoc, name] = match;
|
|
232
|
+
|
|
233
|
+
const descMatch = jsdoc.match(/^\s*\*\s*(.+?)(?=\s*^\s*\*\s*@|\s*$)/ms);
|
|
234
|
+
const description = descMatch
|
|
235
|
+
? descMatch[1]
|
|
236
|
+
.split('\n')
|
|
237
|
+
.map(line => line.replace(/^\s*\*\s?/, ''))
|
|
238
|
+
.join(' ')
|
|
239
|
+
.replace(/\s+/g, ' ')
|
|
240
|
+
.trim()
|
|
241
|
+
: '';
|
|
242
|
+
|
|
243
|
+
const returnMatch = jsdoc.match(/^\s*\*\s*@returns?\s+\{([^}]+)\}/m);
|
|
244
|
+
const type = returnMatch ? returnMatch[1].trim() : 'unknown';
|
|
245
|
+
|
|
246
|
+
properties.push({
|
|
247
|
+
name,
|
|
248
|
+
type,
|
|
249
|
+
description,
|
|
250
|
+
access: 'readonly',
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Extract documented properties (this.property assignments with JSDoc)
|
|
255
|
+
const propMatches = [...content.matchAll(/\/\*\*((?:(?!\/\*\*)[\s\S])*?)\*\/[ \t]*\n[ \t]*(?:this\.)?(\w+)\s*=/g)];
|
|
256
|
+
|
|
257
|
+
propMatches.forEach(match => {
|
|
258
|
+
const [, jsdoc, name] = match;
|
|
259
|
+
|
|
260
|
+
const descMatch = jsdoc.match(/^\s*\*\s*(.+?)(?=\s*^\s*\*\s*@|\s*$)/ms);
|
|
261
|
+
const description = descMatch
|
|
262
|
+
? descMatch[1]
|
|
263
|
+
.split('\n')
|
|
264
|
+
.map(line => line.replace(/^\s*\*\s?/, ''))
|
|
265
|
+
.join(' ')
|
|
266
|
+
.replace(/\s+/g, ' ')
|
|
267
|
+
.trim()
|
|
268
|
+
: '';
|
|
269
|
+
|
|
270
|
+
const typeMatch = jsdoc.match(/^\s*\*\s*@type\s+{([^}]+)}/m);
|
|
271
|
+
const type = typeMatch ? typeMatch[1].trim() : 'unknown';
|
|
272
|
+
|
|
273
|
+
properties.push({
|
|
274
|
+
name,
|
|
275
|
+
type,
|
|
276
|
+
description,
|
|
277
|
+
access: 'read-write',
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
return properties;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Extracts event information from emit() calls and JSDoc `@fires` tags.
|
|
286
|
+
* @param {string} content - File content to parse
|
|
287
|
+
* @returns {Array} Array of event objects with name and description
|
|
288
|
+
*/
|
|
289
|
+
function extractEvents(content) {
|
|
290
|
+
const events = [];
|
|
291
|
+
|
|
292
|
+
// Extract from emit() calls
|
|
293
|
+
const emitMatches = [...content.matchAll(/\.emit\s*\(\s*['"`]([^'"`]+)['"`]/g)];
|
|
294
|
+
|
|
295
|
+
emitMatches.forEach(match => {
|
|
296
|
+
const [, eventName] = match;
|
|
297
|
+
events.push({
|
|
298
|
+
name: eventName,
|
|
299
|
+
description: `Emitted when ${eventName} occurs`,
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Extract from dispatchEvent calls
|
|
304
|
+
const dispatchMatches = [...content.matchAll(/\.dispatchEvent\s*\(\s*new\s+CustomEvent\s*\(\s*['"`]([^'"`]+)['"`]/g)];
|
|
305
|
+
|
|
306
|
+
dispatchMatches.forEach(match => {
|
|
307
|
+
const [, eventName] = match;
|
|
308
|
+
if (!events.find(e => e.name === eventName)) {
|
|
309
|
+
events.push({
|
|
310
|
+
name: eventName,
|
|
311
|
+
description: `Custom event: ${eventName}`,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
return events;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Extracts import statements to determine component dependencies.
|
|
321
|
+
* @param {string} content - File content to parse
|
|
322
|
+
* @returns {Array} Array of import objects with module and imported items
|
|
323
|
+
*/
|
|
324
|
+
function extractImports(content) {
|
|
325
|
+
const importMatches = [
|
|
326
|
+
...content.matchAll(/^import\s+(?:{([^}]+)}|\*\s+as\s+(\w+)|(\w+))\s+from\s+['"`]([^'"`]+)['"`];/gm),
|
|
327
|
+
];
|
|
328
|
+
|
|
329
|
+
return importMatches
|
|
330
|
+
.filter(([, , , , module]) => /^\.\.\//.test(module)) // external relative only, not same-dir ./
|
|
331
|
+
.map(match => {
|
|
332
|
+
const [, namedImports, namespaceImport, defaultImport, module] = match;
|
|
333
|
+
|
|
334
|
+
let imports = [];
|
|
335
|
+
|
|
336
|
+
if (namedImports) {
|
|
337
|
+
imports = namedImports.split(',').map(imp => imp.trim());
|
|
338
|
+
} else if (namespaceImport) {
|
|
339
|
+
imports = [`* as ${namespaceImport}`];
|
|
340
|
+
} else if (defaultImport) {
|
|
341
|
+
imports = [defaultImport];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
module: module.replace(/^(\.\.?\/)+/, ''),
|
|
346
|
+
imports,
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Extracts example code from JSDoc `@example` tags.
|
|
353
|
+
* @param {string} content - File content to parse
|
|
354
|
+
* @returns {Array} Array of example objects with code and optional description
|
|
355
|
+
*/
|
|
356
|
+
function extractExamples(content) {
|
|
357
|
+
const examples = [];
|
|
358
|
+
|
|
359
|
+
// Find all JSDoc blocks with @example tags
|
|
360
|
+
const jsdocBlocks = [...content.matchAll(/\/\*\*\s*\n([\s\S]*?)\*\//g)];
|
|
361
|
+
|
|
362
|
+
jsdocBlocks.forEach(([, jsdoc]) => {
|
|
363
|
+
const exampleMatches = [...jsdoc.matchAll(/^\s*\*\s*@example\s*\n([\s\S]*?)(?=^\s*\*\s*@|\s*$)/gms)];
|
|
364
|
+
|
|
365
|
+
exampleMatches.forEach(([, exampleContent]) => {
|
|
366
|
+
const code = exampleContent
|
|
367
|
+
.split('\n')
|
|
368
|
+
.map(line => line.replace(/^\s*\*\s?/, ''))
|
|
369
|
+
.join('\n')
|
|
370
|
+
.trim();
|
|
371
|
+
|
|
372
|
+
if (code) {
|
|
373
|
+
examples.push({
|
|
374
|
+
code,
|
|
375
|
+
description: '', // Could extract description if needed
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
return examples;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// CLI usage
|
|
385
|
+
if (import.meta.main) {
|
|
386
|
+
const filePath = process.argv[2];
|
|
387
|
+
|
|
388
|
+
if (!filePath) {
|
|
389
|
+
console.error('Usage: bun extractJSDoc.js <path-to-js-file>');
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const result = extractJSDoc(filePath);
|
|
394
|
+
console.log(JSON.stringify(result, null, 2));
|
|
395
|
+
}
|