@vanilla-bean/components 1.1.0 → 2.0.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.
Files changed (75) hide show
  1. package/Component/Component.js +226 -40
  2. package/Component/Component.scenarios.js +2 -2
  3. package/Component/Component.test.js +427 -40
  4. package/Component/README.md +480 -0
  5. package/Component/observeElementConnection.js +1 -1
  6. package/Elem/README.md +373 -0
  7. package/README.md +2 -2
  8. package/components/BottomSheet/BottomSheet.js +8 -16
  9. package/components/BottomSheet/README.md +1 -1
  10. package/components/Button/Button.js +12 -18
  11. package/components/Button/Button.lld.md +1 -1
  12. package/components/Calendar/Calendar.js +139 -59
  13. package/components/Calendar/CalendarEvent.js +9 -4
  14. package/components/Calendar/Toolbar.js +28 -20
  15. package/components/Calendar/index.js +1 -0
  16. package/components/Code/Code.js +26 -30
  17. package/components/Code/Code.lld.md +1 -1
  18. package/components/ColorPicker/ColorPicker.js +58 -58
  19. package/components/Dialog/Dialog.js +79 -73
  20. package/components/Dialog/Dialog.lld.md +1 -1
  21. package/components/Dialog/README.md +10 -10
  22. package/components/Form/Form.js +21 -17
  23. package/components/Form/Form.lld.md +2 -2
  24. package/components/Form/README.md +3 -3
  25. package/components/Icon/Icon.js +19 -11
  26. package/components/Input/Input.js +85 -67
  27. package/components/Input/README.md +7 -9
  28. package/components/Keyboard/Key.js +7 -10
  29. package/components/Keyboard/Keyboard.js +38 -46
  30. package/components/Keyboard/Keyboard.lld.md +1 -1
  31. package/components/Label/Label.js +52 -50
  32. package/components/Link/Link.js +14 -20
  33. package/components/List/List.js +28 -30
  34. package/components/Menu/Menu.js +19 -10
  35. package/components/Menu/Menu.lld.md +2 -2
  36. package/components/Notify/Notify.js +29 -19
  37. package/components/Notify/Notify.lld.md +2 -2
  38. package/components/Page/Page.lld.md +1 -1
  39. package/components/Popover/Popover.js +49 -44
  40. package/components/RadioButton/RadioButton.js +33 -29
  41. package/components/RadioButton/RadioButton.lld.md +1 -1
  42. package/components/Router/README.md +12 -12
  43. package/components/Router/Router.js +19 -21
  44. package/components/Select/README.md +5 -5
  45. package/components/Select/Select.js +25 -31
  46. package/components/Table/README.md +4 -4
  47. package/components/Table/Table.js +55 -36
  48. package/components/Table/Table.lld.md +1 -1
  49. package/components/TagList/Tag.js +16 -14
  50. package/components/TagList/TagList.js +15 -8
  51. package/components/TagList/TagList.lld.md +1 -1
  52. package/components/Tooltip/Tooltip.js +12 -25
  53. package/components/TooltipWrapper/TooltipWrapper.js +55 -49
  54. package/components/TooltipWrapper/TooltipWrapper.lld.md +2 -2
  55. package/components/Whiteboard/Whiteboard.js +45 -43
  56. package/components/Whiteboard/Whiteboard.lld.md +1 -1
  57. package/devTools/build.js +43 -0
  58. package/devTools/buildTypes.js +322 -0
  59. package/devTools/createComponent.js +155 -0
  60. package/devTools/extractJSDoc.js +395 -0
  61. package/devTools/processTemplate.js +500 -0
  62. package/devTools/updateComponentIndex.js +16 -0
  63. package/devTools/updateDemoViewIndex.js +90 -0
  64. package/eslint.config.cjs +5 -2
  65. package/index.d.ts +120 -62
  66. package/package.json +23 -22
  67. package/spellcheck.config.cjs +3 -0
  68. package/styled/README.md +329 -0
  69. package/theme/.test.js +2 -2
  70. package/theme/README.md +8 -8
  71. package/theme/colors.js +16 -13
  72. package/theme/colors.test.js +28 -0
  73. package/utils/browser.js +1 -1
  74. package/utils/element.js +2 -2
  75. package/FontWithASyntaxHighlighter-Regular.woff2 +0 -0
@@ -0,0 +1,500 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { extractJSDoc } from './extractJSDoc.js';
4
+
5
+ const codeCell = str => {
6
+ const escaped = (str || '')
7
+ .replace(/&/g, '&')
8
+ .replace(/</g, '&lt;')
9
+ .replace(/>/g, '&gt;')
10
+ .replace(/\|/g, '&#124;');
11
+ return `<code>${escaped}</code>`;
12
+ };
13
+
14
+ /**
15
+ * Processes README templates by replacing extraction commands with JSDoc-derived content.
16
+ * @param {string} templateContent - Template content with extraction commands
17
+ * @param {string} componentPath - Path to component directory for resolving relative paths
18
+ * @returns {string} Processed template with commands replaced by generated content
19
+ */
20
+ export function processTemplate(templateContent, componentPath = '.') {
21
+ // Replace extraction commands that start with known command words
22
+ return templateContent.replace(/\[\[(extract-[\w-]+|import)\s+([^\]]+)\]\]/g, (match, command, args) => {
23
+ try {
24
+ return processCommand(`${command} ${args}`.trim(), componentPath);
25
+ } catch (error) {
26
+ console.warn(`Failed to process command "${command} ${args}":`, error.message);
27
+ return match; // Return original if processing fails
28
+ }
29
+ });
30
+ }
31
+
32
+ /**
33
+ * Processes individual template commands and returns generated markdown content.
34
+ * @param {string} command - Command string (e.g., "extract-description Button.js")
35
+ * @param {string} basePath - Base path for resolving file references
36
+ * @returns {string} Generated markdown content for the command
37
+ */
38
+ function processCommand(command, basePath) {
39
+ const [action, ...args] = command.split(/\s+/);
40
+ const fileName = args[0];
41
+
42
+ if (!fileName) {
43
+ throw new Error(`No filename provided for command: ${action}`);
44
+ }
45
+
46
+ const filePath = join(basePath, fileName);
47
+
48
+ switch (action) {
49
+ case 'extract-description':
50
+ return extractDescriptionMarkdown(filePath);
51
+
52
+ case 'extract-options':
53
+ return extractOptionsMarkdown(filePath);
54
+
55
+ case 'extract-methods':
56
+ return extractMethodsMarkdown(filePath);
57
+
58
+ case 'extract-properties':
59
+ return extractPropertiesMarkdown(filePath);
60
+
61
+ case 'extract-events':
62
+ return extractEventsMarkdown(filePath);
63
+
64
+ case 'extract-imports':
65
+ return extractImportsMarkdown(filePath);
66
+
67
+ case 'extract-examples':
68
+ return extractExamplesMarkdown(filePath);
69
+
70
+ case 'extract-enums':
71
+ return extractEnumsMarkdown(filePath);
72
+
73
+ case 'extract-custom-events':
74
+ return extractCustomEventsMarkdown(filePath);
75
+
76
+ case 'extract-design':
77
+ return extractDesignMarkdown(filePath, basePath);
78
+
79
+ case 'import':
80
+ return importFileContent(filePath, args[1]); // args[1] might be regex pattern
81
+
82
+ default:
83
+ throw new Error(`Unknown extraction command: ${action}`);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Extracts and formats component description as markdown.
89
+ * @param {string} filePath - Path to component file
90
+ * @returns {string} Formatted description markdown
91
+ */
92
+ function extractDescriptionMarkdown(filePath) {
93
+ const { description } = extractJSDoc(filePath);
94
+
95
+ if (!description) {
96
+ return '*No description available.*';
97
+ }
98
+
99
+ return description;
100
+ }
101
+
102
+ /**
103
+ * Extracts and formats component options as markdown table.
104
+ * @param {string} filePath - Path to component file
105
+ * @returns {string} Formatted options table markdown
106
+ */
107
+ function extractOptionsMarkdown(filePath) {
108
+ const { options } = extractJSDoc(filePath);
109
+
110
+ if (!options.length) {
111
+ return '';
112
+ }
113
+
114
+ let markdown = '| Option | Type | Default | Description |\n';
115
+ markdown += '|--------|------|---------|-------------|\n';
116
+
117
+ options.forEach(option => {
118
+ const name = option.optional ? `[${option.name}]` : option.name;
119
+ const type = codeCell(option.type);
120
+ let defaultValue;
121
+ if (option.default) defaultValue = codeCell(option.default);
122
+ else if (option.optional) defaultValue = codeCell('undefined');
123
+ else defaultValue = '*required*';
124
+ const description = (option.description || '*No description*').replace(/\|/g, '&#124;');
125
+
126
+ markdown += `| ${name} | ${type} | ${defaultValue} | ${description} |\n`;
127
+ });
128
+
129
+ return markdown;
130
+ }
131
+
132
+ /**
133
+ * Extracts and formats component methods as markdown sections.
134
+ * @param {string} filePath - Path to component file
135
+ * @returns {string} Formatted methods markdown
136
+ */
137
+ function extractMethodsMarkdown(filePath) {
138
+ const { methods } = extractJSDoc(filePath);
139
+
140
+ if (!methods.length) {
141
+ return '';
142
+ }
143
+
144
+ let markdown = '';
145
+
146
+ methods.forEach((method, index) => {
147
+ if (index > 0) markdown += '\n';
148
+
149
+ // Method signature
150
+ const params = method.parameters
151
+ .map(p => {
152
+ const name = p.optional ? `[${p.name}]` : p.name;
153
+ return p.default ? `${name}=${p.default}` : name;
154
+ })
155
+ .join(', ');
156
+
157
+ markdown += `### \`${method.name}(${params})\`\n\n`;
158
+
159
+ // Description
160
+ if (method.description) {
161
+ markdown += `${method.description}\n\n`;
162
+ }
163
+
164
+ // Parameters
165
+ if (method.parameters.length > 0) {
166
+ markdown += '**Parameters:**\n\n';
167
+ method.parameters.forEach(param => {
168
+ const name = param.optional ? `[${param.name}]` : param.name;
169
+ markdown += `- \`${name}\` (\`${param.type}\`) - ${param.description}`;
170
+ if (param.default) markdown += ` (default: \`${param.default}\`)`;
171
+ markdown += '\n';
172
+ });
173
+ markdown += '\n';
174
+ }
175
+
176
+ // Returns
177
+ if (method.returns) {
178
+ markdown += `**Returns:** \`${method.returns.type}\` - ${method.returns.description}\n\n`;
179
+ }
180
+ });
181
+
182
+ return markdown.trim();
183
+ }
184
+
185
+ /**
186
+ * Extracts and formats component properties as markdown table.
187
+ * @param {string} filePath - Path to component file
188
+ * @returns {string} Formatted properties table markdown
189
+ */
190
+ function extractPropertiesMarkdown(filePath) {
191
+ const { properties } = extractJSDoc(filePath);
192
+
193
+ if (!properties.length) {
194
+ return '';
195
+ }
196
+
197
+ let markdown = '| Property | Type | Access | Description |\n';
198
+ markdown += '|----------|------|--------|-------------|\n';
199
+
200
+ properties.forEach(prop => {
201
+ const access = prop.access === 'readonly' ? 'Read-only' : 'Read/Write';
202
+ const description = (prop.description || '*No description*').replace(/\|/g, '&#124;');
203
+
204
+ markdown += `| ${codeCell(prop.name)} | ${codeCell(prop.type)} | ${access} | ${description} |\n`;
205
+ });
206
+
207
+ return markdown;
208
+ }
209
+
210
+ /**
211
+ * Extracts and formats component events as markdown list.
212
+ * @param {string} filePath - Path to component file
213
+ * @returns {string} Formatted events markdown
214
+ */
215
+ function extractEventsMarkdown(filePath) {
216
+ const { events } = extractJSDoc(filePath);
217
+
218
+ if (!events.length) {
219
+ return '';
220
+ }
221
+
222
+ let markdown = '';
223
+
224
+ events.forEach(event => {
225
+ markdown += `- **\`${event.name}\`** - ${event.description}\n`;
226
+ });
227
+
228
+ return markdown;
229
+ }
230
+
231
+ /**
232
+ * Extracts and formats component dependencies as markdown list.
233
+ * @param {string} filePath - Path to component file
234
+ * @returns {string} Formatted imports markdown
235
+ */
236
+ const COMPONENT_DEMO_ROUTES = {
237
+ EventTarget: 'https://developer.mozilla.org/docs/Web/API/EventTarget',
238
+ Elem: '#/documentation/Elem',
239
+ Component: '#/documentation/Component',
240
+ };
241
+
242
+ const MODULE_ROUTES = {
243
+ utils: '#/documentation/utils',
244
+ styled: '#/documentation/styled',
245
+ '@vanilla-bean/oxject': '#/dependencies/oxject',
246
+ '@ctrl/tinycolor': '#/dependencies/tinycolor',
247
+ };
248
+
249
+ /**
250
+ * Extracts and formats component dependencies as markdown list.
251
+ * @param {string} filePath - Path to component file
252
+ * @returns {string} Formatted imports markdown
253
+ */
254
+ function extractImportsMarkdown(filePath) {
255
+ const { imports } = extractJSDoc(filePath);
256
+
257
+ if (!imports.length) {
258
+ return '';
259
+ }
260
+
261
+ const seen = new Set();
262
+ let markdown = '';
263
+
264
+ imports.forEach(imp => {
265
+ const moduleRoot = imp.module.replace(/\/.*$/, '');
266
+ const moduleRoute = MODULE_ROUTES[moduleRoot] || MODULE_ROUTES[imp.module];
267
+
268
+ imp.imports.forEach(name => {
269
+ if (seen.has(name)) return;
270
+ seen.add(name);
271
+
272
+ const route = moduleRoute ?? COMPONENT_DEMO_ROUTES[name] ?? (/^[A-Z]/.test(name) ? `#/${name}` : null);
273
+ markdown += route ? `- [${name}](${route})\n` : `- \`${name}\`\n`;
274
+ });
275
+ });
276
+
277
+ return markdown;
278
+ }
279
+
280
+ /**
281
+ * Extracts and formats JSDoc examples as markdown code blocks.
282
+ * @param {string} filePath - Path to component file
283
+ * @returns {string} Formatted examples markdown
284
+ */
285
+ function extractExamplesMarkdown(filePath) {
286
+ const { examples } = extractJSDoc(filePath);
287
+
288
+ if (!examples.length) {
289
+ return '';
290
+ }
291
+
292
+ let markdown = '';
293
+
294
+ examples.forEach((example, index) => {
295
+ if (index > 0) markdown += '\n';
296
+
297
+ if (example.description) {
298
+ markdown += `${example.description}\n\n`;
299
+ }
300
+
301
+ markdown += '```js\n';
302
+ markdown += example.code;
303
+ markdown += '\n```\n';
304
+ });
305
+
306
+ return markdown.trim();
307
+ }
308
+
309
+ /**
310
+ * Extracts usage examples from demo file.
311
+ * @param {string} filePath - Path to component file
312
+ * @param {string} basePath - Base path for resolving demo file
313
+ * @returns {string} Formatted usage examples markdown
314
+ */
315
+
316
+ /**
317
+ * Extracts enum definitions from JSDoc comments.
318
+ * @param {string} filePath - Path to component file
319
+ * @returns {string} Formatted enums markdown
320
+ */
321
+ function extractEnumsMarkdown(filePath) {
322
+ try {
323
+ const content = readFileSync(filePath, 'utf8');
324
+
325
+ // Look for @enum JSDoc tags
326
+ const enumMatches = [
327
+ ...content.matchAll(/\/\*\*\s*\n([\s\S]*?)@enum\s*{([^}]+)}\s*([\s\S]*?)\*\/\s*(const|let|var)\s+(\w+)/g),
328
+ ];
329
+
330
+ if (!enumMatches.length) {
331
+ return '';
332
+ }
333
+
334
+ let markdown = '';
335
+ enumMatches.forEach(match => {
336
+ const [, preEnum, enumType, , , enumName] = match;
337
+
338
+ const description = preEnum.match(/^\s*\*\s*(.+?)(?=\s*^\s*\*\s*@|\s*$)/ms);
339
+ const desc = description
340
+ ? description[1]
341
+ .split('\n')
342
+ .map(line => line.replace(/^\s*\*\s?/, ''))
343
+ .join(' ')
344
+ .trim()
345
+ : '';
346
+
347
+ markdown += `### \`${enumName}\` (\`${enumType}\`)\n\n`;
348
+ if (desc) {
349
+ markdown += `${desc}\n\n`;
350
+ }
351
+ });
352
+
353
+ return markdown.trim();
354
+ } catch {
355
+ return '*Error extracting enums.*';
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Extracts custom event definitions from JSDoc `@fires` tags and CustomEvent calls.
361
+ * @param {string} filePath - Path to component file
362
+ * @returns {string} Formatted custom events markdown
363
+ */
364
+ function extractCustomEventsMarkdown(filePath) {
365
+ try {
366
+ const content = readFileSync(filePath, 'utf8');
367
+ const customEvents = [];
368
+
369
+ // Extract from @fires JSDoc tags
370
+ const firesMatches = [...content.matchAll(/^\s*\*\s*@fires\s+(\w+)\s*-?\s*(.*)$/gm)];
371
+ firesMatches.forEach(match => {
372
+ const [, eventName, description] = match;
373
+ customEvents.push({
374
+ name: eventName,
375
+ description: description || `Custom event: ${eventName}`,
376
+ });
377
+ });
378
+
379
+ // Extract from dispatchEvent calls
380
+ const dispatchMatches = [
381
+ ...content.matchAll(/dispatchEvent\s*\(\s*new\s+CustomEvent\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*{([^}]*)}\s*\)/g),
382
+ ];
383
+ dispatchMatches.forEach(match => {
384
+ const [, eventName] = match;
385
+ if (!customEvents.find(e => e.name === eventName)) {
386
+ customEvents.push({
387
+ name: eventName,
388
+ description: `Custom event: ${eventName}`,
389
+ });
390
+ }
391
+ });
392
+
393
+ if (!customEvents.length) {
394
+ return '';
395
+ }
396
+
397
+ let markdown = '';
398
+ customEvents.forEach(event => {
399
+ markdown += `- **\`${event.name}\`** - ${event.description}\n`;
400
+ });
401
+
402
+ return markdown.trim();
403
+ } catch {
404
+ return '*Error extracting custom events.*';
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Extracts design information from DESIGN.md file or JSDoc `@design` tags.
410
+ * @param {string} filePath - Path to component file
411
+ * @param {string} basePath - Base path for resolving design file
412
+ * @returns {string} Formatted design information markdown
413
+ */
414
+ function extractDesignMarkdown(filePath, basePath) {
415
+ try {
416
+ // First try to find DESIGN.md file in component directory
417
+ const designPath = join(basePath, 'DESIGN.md');
418
+ try {
419
+ const designContent = readFileSync(designPath, 'utf8');
420
+ return designContent;
421
+ } catch {
422
+ // DESIGN.md doesn't exist, try extracting from JSDoc
423
+ }
424
+
425
+ // Extract from JSDoc @design tags
426
+ const content = readFileSync(filePath, 'utf8');
427
+ const designMatches = [...content.matchAll(/^\s*\*\s*@design\s+(.+?)(?=\s*^\s*\*\s*@|\s*^\s*\*\s*$)/gms)];
428
+
429
+ if (!designMatches.length) {
430
+ return '';
431
+ }
432
+
433
+ let markdown = '';
434
+ designMatches.forEach(match => {
435
+ const designText = match[1]
436
+ .split('\n')
437
+ .map(line => line.replace(/^\s*\*\s?/, ''))
438
+ .join(' ')
439
+ .trim();
440
+
441
+ markdown += designText + '\n\n';
442
+ });
443
+
444
+ return markdown.trim();
445
+ } catch {
446
+ return '*Error extracting design information.*';
447
+ }
448
+ }
449
+
450
+ /**
451
+ * Imports file content with optional regex pattern matching.
452
+ * @param {string} filePath - Path to file to import
453
+ * @param {string} [pattern] - Optional regex pattern to extract specific content
454
+ * @returns {string} File content or pattern-matched content
455
+ */
456
+ function importFileContent(filePath, pattern) {
457
+ try {
458
+ const content = readFileSync(filePath, 'utf8');
459
+
460
+ if (!pattern) {
461
+ return content;
462
+ }
463
+
464
+ // Parse regex pattern from string
465
+ const regexMatch = pattern.match(/^\/(.+)\/([gimsu]*)$/);
466
+ if (regexMatch) {
467
+ const [, regexPattern, flags] = regexMatch;
468
+ const regex = new RegExp(regexPattern, flags);
469
+ const match = content.match(regex);
470
+
471
+ if (match) {
472
+ return match[0];
473
+ }
474
+ }
475
+
476
+ return content;
477
+ } catch {
478
+ return `*Error importing ${filePath}: file not found*`;
479
+ }
480
+ }
481
+
482
+ // CLI usage
483
+ if (import.meta.main) {
484
+ const templatePath = process.argv[2];
485
+ const componentDir = process.argv[3] || '.';
486
+
487
+ if (!templatePath) {
488
+ console.error('Usage: bun processTemplate.js <template-file> [component-directory]');
489
+ process.exit(1);
490
+ }
491
+
492
+ try {
493
+ const templateContent = readFileSync(templatePath, 'utf8');
494
+ const processedContent = processTemplate(templateContent, componentDir);
495
+ console.log(processedContent);
496
+ } catch {
497
+ console.error('Error processing template: template processing failed');
498
+ process.exit(1);
499
+ }
500
+ }
@@ -0,0 +1,16 @@
1
+ import { basename } from 'node:path';
2
+ import { Glob } from 'bun';
3
+
4
+ import { orderBy } from '../utils/data';
5
+
6
+ const components = [...new Glob('components/*/index.js').scanSync('.')].sort(orderBy([{ direction: 'asc' }]));
7
+
8
+ let result = `/* This file is automatically managed (bun build:index) (${basename(process.argv[1])}), do not edit by hand */\n\n`;
9
+
10
+ components.forEach(file => {
11
+ const [, , name] = file.match(/(.+?)\/(.+)\/(.+?)(\..+)?$/);
12
+
13
+ result += `export * from './${name}';\n`;
14
+ });
15
+
16
+ await Bun.write('components/index.js', result);
@@ -0,0 +1,90 @@
1
+ import { basename } from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { Glob } from 'bun';
4
+
5
+ import { orderBy } from '../utils/data';
6
+
7
+ const demos = [...new Glob('components/*/demo.js').scanSync('.')].sort(orderBy([{ direction: 'asc' }]));
8
+ const examples = [...new Glob('demo/examples/*.js').scanSync('.')]
9
+ .filter(file => !file.endsWith('.test.js'))
10
+ .sort(orderBy([{ direction: 'asc' }]));
11
+ const documentation = [...new Glob('./*/README.md').scanSync('.')].sort(orderBy([{ direction: 'asc' }]));
12
+
13
+ let result = `/* This file is automatically managed (bun build:index) (${basename(process.argv[1])}), do not edit by hand */\n\n`;
14
+
15
+ result += `import { styled } from '../styled/styled.js';\n\n`;
16
+
17
+ demos.forEach(file => {
18
+ const match = file.match(/(.+?)\/(.+)\/(.+?)(\..+)?$/);
19
+ result += `import ${match[2]}Demo from '../components/${match[2]}/demo.js';\n`;
20
+ });
21
+
22
+ result += '\n';
23
+
24
+ examples.forEach(file => {
25
+ const match = file.match(/(.+)\/(.+?)(\..+)?$/);
26
+ result += `import ${match[2]}Example from './examples/${match[2]}';\n`;
27
+ });
28
+
29
+ result += `\nimport DocumentationView from './DemoView/DocumentationView.js';\n\n`;
30
+
31
+ result += `const startDocumentation = styled(DocumentationView, () => '', { fileName: 'docs/GETTING_STARTED.md', nextLabel: 'Elem', nextUrl: '#/documentation/Elem' });\nconst MarkdownDocumentation = styled(DocumentationView, () => '', { folderName: 'markdown' });\n`;
32
+
33
+ const documentationChain = {
34
+ demo: `, nextLabel: 'Getting Started', nextUrl: '#/documentation/start'`,
35
+ Elem: `, nextLabel: 'Component', nextUrl: '#/documentation/Component'`,
36
+ Component: `, nextLabel: 'styled', nextUrl: '#/documentation/styled'`,
37
+ styled: `, nextLabel: 'theme', nextUrl: '#/documentation/theme'`,
38
+ theme: `, nextLabel: 'utils', nextUrl: '#/documentation/utils'`,
39
+ utils: ``,
40
+ };
41
+
42
+ documentation.forEach(file => {
43
+ const [, , name] = file.match(/(.+?)\/(.+)\/(.+?)(\..+)?$/);
44
+ result += `const ${name}Documentation = styled(DocumentationView, () => '', { folderName: '${name}'${documentationChain[name] || ''} });\n`;
45
+ });
46
+
47
+ const { dependencies = {} } = JSON.parse(await Bun.file('./package.json').text());
48
+ const npmDocs = Object.keys(dependencies)
49
+ .map(pkg => {
50
+ const name = pkg.replace(/^@[^/]+\//, '');
51
+ const readmePath = `node_modules/${pkg}/README.md`;
52
+ return existsSync(readmePath) ? { name, pkg } : null;
53
+ })
54
+ .filter(Boolean)
55
+ .sort((a, b) => a.name.localeCompare(b.name));
56
+
57
+ npmDocs.forEach(({ name }) => {
58
+ const varName = name.replace(/[^a-zA-Z0-9]/g, '_');
59
+ result += `const dep_${varName} = styled(DocumentationView, () => '', { folderName: 'dependencies/${name}' });\n`;
60
+ });
61
+
62
+ result += '\nexport default {\n';
63
+
64
+ demos.forEach(file => {
65
+ const match = file.match(/(.+?)\/(.+)\/(.+?)(\..+)?$/);
66
+ result += `\t['/${match[2]}']: ${match[2]}Demo,\n`;
67
+ });
68
+
69
+ result += '\n';
70
+
71
+ examples.forEach(file => {
72
+ const match = file.match(/(.+)\/(.+?)(\..+)?$/);
73
+ result += `\t['/examples/${match[2]}']: ${match[2]}Example,\n`;
74
+ });
75
+
76
+ result += `\n\t['/documentation/start']: startDocumentation,\n\t['/documentation/markdown']: MarkdownDocumentation,\n`;
77
+
78
+ documentation.forEach(file => {
79
+ const match = file.match(/(.+?)\/(.+)\/(.+?)(\..+)?$/);
80
+ result += `\t['/documentation/${match[2]}']: ${match[2]}Documentation,\n`;
81
+ });
82
+
83
+ npmDocs.forEach(({ name }) => {
84
+ const varName = name.replace(/[^a-zA-Z0-9]/g, '_');
85
+ result += `\t['/dependencies/${name}']: dep_${varName},\n`;
86
+ });
87
+
88
+ result += '};\n';
89
+
90
+ await Bun.write('demo/views.js', result);
package/eslint.config.cjs CHANGED
@@ -27,8 +27,8 @@ module.exports = [
27
27
  files: ['**/*.js', '**/*.mjs', '**/*.cjs'],
28
28
  plugins: {
29
29
  import: importPlugin,
30
- 'write-good-comments': writeGoodComments,
31
- spellcheck,
30
+ 'write-good-comments': fixupPluginRules(writeGoodComments),
31
+ spellcheck: fixupPluginRules(spellcheck),
32
32
  },
33
33
  rules: {
34
34
  'no-console': 'warn',
@@ -52,6 +52,9 @@ module.exports = [
52
52
 
53
53
  'spellcheck/spell-checker': ['warn', require('./spellcheck.config.cjs')],
54
54
  'jsdoc/no-undefined-types': ['warn', { definedTypes: ['Component', 'TemplateStringsArray'] }],
55
+ // {*} and {Function} are deliberate here - buildTypes maps them to TS types
56
+ 'jsdoc/reject-any-type': 'off',
57
+ 'jsdoc/reject-function-type': 'off',
55
58
  },
56
59
  },
57
60
  {