bejamas 0.0.0-canary.0138fd3

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.
@@ -0,0 +1,1205 @@
1
+ import { _ as getConfig, a as extractComponentTagsFromMDX, b as logger, c as extractPropsFromDeclaredProps, d as parseJsDocMetadata, f as resolveOutDir, g as spinner, h as toIdentifier, i as discoverExamples, l as normalizeBlockMDX, m as slugify, n as createSourceFileFromFrontmatter, o as extractFrontmatter, p as resolveUiRoot, r as detectHasImportTopLevel, s as extractPropsFromAstroProps, st as __require, t as RESERVED_COMPONENTS, u as normalizeUsageMDX } from "./utils-Cxc_kZGv.js";
2
+ import { dirname, extname, join, relative } from "path";
3
+ import { existsSync, mkdirSync } from "fs";
4
+ import { readFile, readdir, writeFile } from "fs/promises";
5
+
6
+ //#region src/docs/generate-mdx/examples.ts
7
+ function createImplicitSection() {
8
+ return {
9
+ title: null,
10
+ intro: [],
11
+ items: []
12
+ };
13
+ }
14
+ function normalizeSection(section) {
15
+ return {
16
+ title: section.title,
17
+ introMD: section.intro.join("\n").trim(),
18
+ items: section.items.map((item) => ({
19
+ title: item.title,
20
+ body: item.body.join("\n").trim()
21
+ }))
22
+ };
23
+ }
24
+ function pushItem(section, item) {
25
+ if (!section || !item) return;
26
+ if (!item.title.trim().length && !item.body.join("\n").trim().length) return;
27
+ section.items.push(item);
28
+ }
29
+ function pushSection(sections, section) {
30
+ if (!section) return;
31
+ const normalized = normalizeSection(section);
32
+ if (!normalized.title && !normalized.introMD.length && normalized.items.length === 0) return;
33
+ sections.push(normalized);
34
+ }
35
+ function extractHeading(line, level) {
36
+ const pattern = level === 2 ? /^##(?!#)\s+(.+)$/ : /^###(?!#)\s+(.+)$/;
37
+ const match = line.trim().match(pattern);
38
+ return match ? match[1].trim() : null;
39
+ }
40
+ function splitDescriptionAndSnippetRaw(body) {
41
+ if (!body || !body.trim().length) return {
42
+ descriptionMD: "",
43
+ snippet: ""
44
+ };
45
+ const lines = body.split("\n");
46
+ let snippetStartIdx = -1;
47
+ for (let i = 0; i < lines.length; i += 1) {
48
+ const ln = lines[i];
49
+ if (!ln.trim().length) continue;
50
+ if (/^\s*[<{]/.test(ln)) {
51
+ snippetStartIdx = i;
52
+ break;
53
+ }
54
+ }
55
+ if (snippetStartIdx <= 0) return {
56
+ descriptionMD: "",
57
+ snippet: body.trim()
58
+ };
59
+ return {
60
+ descriptionMD: lines.slice(0, snippetStartIdx).join("\n").trim(),
61
+ snippet: lines.slice(snippetStartIdx).join("\n").trim()
62
+ };
63
+ }
64
+ function splitAstroFrontmatter(sourceCode) {
65
+ const match = sourceCode.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
66
+ if (!match) return {
67
+ frontmatter: "",
68
+ markup: sourceCode.trim()
69
+ };
70
+ return {
71
+ frontmatter: match[1].trim(),
72
+ markup: match[2].trim()
73
+ };
74
+ }
75
+ function stripFrontmatterComments(frontmatter) {
76
+ if (!frontmatter || !frontmatter.length) return "";
77
+ return frontmatter.replace(/\/\*[\s\S]*?\*\//g, "\n").replace(/^[ \t]*\/\/.*$/gm, "").trim();
78
+ }
79
+ function isImportOnlyFrontmatter(frontmatter) {
80
+ const cleaned = stripFrontmatterComments(frontmatter);
81
+ if (!cleaned.length) return true;
82
+ let remaining = cleaned;
83
+ remaining = remaining.replace(/(?:^|\n)\s*import\s+(?:type\s+)?[\s\S]*?\s+from\s+['"][^'"]+['"]\s*;?(?=\n|$)/g, "\n").replace(/(?:^|\n)\s*import\s+['"][^'"]+['"]\s*;?(?=\n|$)/g, "\n").replace(/(?:^|\n)\s*export\s+(?:\*\s+from|\{[\s\S]*?\}\s+from)\s+['"][^'"]+['"]\s*;?(?=\n|$)/g, "\n").trim();
84
+ return remaining.replace(/[;\s]/g, "").length === 0;
85
+ }
86
+ function isAstroFrontmatterPreviewUnsafe(frontmatter) {
87
+ const cleaned = stripFrontmatterComments(frontmatter);
88
+ if (!cleaned.length) return false;
89
+ if (/\b(?:const|let|var|function|class|if|for|while|switch|try|catch|return|throw|await|new)\b|=>|Astro\./.test(cleaned)) return true;
90
+ return !isImportOnlyFrontmatter(cleaned);
91
+ }
92
+ function parseFenceInfo(infoRaw) {
93
+ const parts = (infoRaw || "").trim().split(/\s+/).filter((v) => v.length).map((v) => v.toLowerCase());
94
+ if (!parts.length) return {
95
+ lang: "",
96
+ flags: /* @__PURE__ */ new Set()
97
+ };
98
+ const [lang, ...flags] = parts;
99
+ return {
100
+ lang,
101
+ flags: new Set(flags)
102
+ };
103
+ }
104
+ function decodeEscapedScriptTags(source) {
105
+ if (!source || !source.length) return source;
106
+ return source.replace(/&lt;script&gt;/gi, "<script>").replace(/&lt;\/script&gt;/gi, "<\/script>");
107
+ }
108
+ const CONSOLE_SCRIPT_FENCE_LANGS = /* @__PURE__ */ new Set([
109
+ "js",
110
+ "ts",
111
+ "javascript",
112
+ "typescript"
113
+ ]);
114
+ function parseFenceBlocks(body) {
115
+ const lines = body.split("\n");
116
+ const fences = [];
117
+ let active = null;
118
+ for (let i = 0; i < lines.length; i += 1) {
119
+ const line = lines[i];
120
+ const trimmed = line.trim();
121
+ if (!trimmed.startsWith("```")) {
122
+ if (active) active.bodyLines.push(line);
123
+ continue;
124
+ }
125
+ if (!active) {
126
+ const parsed = parseFenceInfo(trimmed.slice(3).trim());
127
+ active = {
128
+ startLine: i,
129
+ openLine: line,
130
+ lang: parsed.lang,
131
+ flags: parsed.flags,
132
+ bodyLines: []
133
+ };
134
+ continue;
135
+ }
136
+ fences.push({
137
+ openLine: active.openLine,
138
+ closeLine: line,
139
+ lang: active.lang,
140
+ flags: active.flags,
141
+ body: active.bodyLines.join("\n"),
142
+ startLine: active.startLine,
143
+ endLine: i
144
+ });
145
+ active = null;
146
+ }
147
+ return {
148
+ lines,
149
+ fences
150
+ };
151
+ }
152
+ function serializeFenceBlock(fence) {
153
+ const parts = [fence.openLine];
154
+ if (fence.body.length) parts.push(fence.body);
155
+ parts.push(fence.closeLine);
156
+ return parts.join("\n");
157
+ }
158
+ function buildRemainingMarkdown(lines, fences, consumedFenceIndexes) {
159
+ const skippedLines = /* @__PURE__ */ new Set();
160
+ for (const [index, fence] of fences.entries()) {
161
+ if (!consumedFenceIndexes.has(index)) continue;
162
+ for (let line = fence.startLine; line <= fence.endLine; line += 1) skippedLines.add(line);
163
+ }
164
+ return lines.filter((_, index) => !skippedLines.has(index)).join("\n").trim();
165
+ }
166
+ function extractFirstAstroFence(body) {
167
+ if (!body || !body.trim().length) return null;
168
+ const { lines, fences } = parseFenceBlocks(body);
169
+ const astroFenceIndex = fences.findIndex((fence) => fence.lang === "astro");
170
+ if (astroFenceIndex === -1) return null;
171
+ const astroFence = fences[astroFenceIndex];
172
+ const consumedFenceIndexes = /* @__PURE__ */ new Set([astroFenceIndex]);
173
+ let companionSourceMD = "";
174
+ let consoleScripts = [];
175
+ let companionIntroMD = "";
176
+ let trailingDescriptionMD = "";
177
+ let mergeCompanionWithSource = false;
178
+ if (astroFence.flags.has("console")) {
179
+ const nextFence = fences[astroFenceIndex + 1];
180
+ if (nextFence && CONSOLE_SCRIPT_FENCE_LANGS.has(nextFence.lang)) {
181
+ consumedFenceIndexes.add(astroFenceIndex + 1);
182
+ companionSourceMD = serializeFenceBlock(nextFence);
183
+ companionIntroMD = lines.slice(astroFence.endLine + 1, nextFence.startLine).join("\n").trim();
184
+ trailingDescriptionMD = lines.slice(nextFence.endLine + 1).join("\n").trim();
185
+ mergeCompanionWithSource = companionIntroMD.length === 0;
186
+ const normalizedScript = nextFence.body.trim();
187
+ if (normalizedScript.length) consoleScripts = [normalizedScript];
188
+ }
189
+ }
190
+ const descriptionMD = companionSourceMD.length ? lines.slice(0, astroFence.startLine).join("\n").trim() : buildRemainingMarkdown(lines, fences, consumedFenceIndexes);
191
+ return {
192
+ sourceCode: decodeEscapedScriptTags(astroFence.body.trim()),
193
+ descriptionMD,
194
+ skipPreview: astroFence.flags.has("nopreview"),
195
+ enableConsolePanel: astroFence.flags.has("console"),
196
+ consoleScripts,
197
+ companionSourceMD,
198
+ companionIntroMD,
199
+ trailingDescriptionMD,
200
+ mergeCompanionWithSource
201
+ };
202
+ }
203
+ function parseExamplesSections(examplesMDX) {
204
+ if (!examplesMDX || !examplesMDX.trim().length) return [];
205
+ const sections = [];
206
+ const lines = examplesMDX.split("\n");
207
+ let currentSection = null;
208
+ let currentItem = null;
209
+ let inFence = false;
210
+ for (const line of lines) {
211
+ if (line.trim().startsWith("```")) {
212
+ inFence = !inFence;
213
+ if (!currentSection) currentSection = createImplicitSection();
214
+ if (currentItem) currentItem.body.push(line);
215
+ else currentSection.intro.push(line);
216
+ continue;
217
+ }
218
+ if (!inFence) {
219
+ const h2 = extractHeading(line, 2);
220
+ if (h2) {
221
+ pushItem(currentSection, currentItem);
222
+ currentItem = null;
223
+ pushSection(sections, currentSection);
224
+ currentSection = {
225
+ title: h2,
226
+ intro: [],
227
+ items: []
228
+ };
229
+ continue;
230
+ }
231
+ const h3 = extractHeading(line, 3);
232
+ if (h3) {
233
+ if (!currentSection) currentSection = createImplicitSection();
234
+ pushItem(currentSection, currentItem);
235
+ currentItem = {
236
+ title: h3,
237
+ body: []
238
+ };
239
+ continue;
240
+ }
241
+ }
242
+ if (!currentSection) currentSection = createImplicitSection();
243
+ if (currentItem) currentItem.body.push(line);
244
+ else currentSection.intro.push(line);
245
+ }
246
+ pushItem(currentSection, currentItem);
247
+ pushSection(sections, currentSection);
248
+ return sections;
249
+ }
250
+ function prepareExampleContent(body) {
251
+ if (!body || !body.trim().length) return {
252
+ descriptionMD: "",
253
+ snippet: "",
254
+ sourceCode: "",
255
+ sourceFromFence: false,
256
+ skipPreview: false,
257
+ enableConsolePanel: false,
258
+ consoleScripts: [],
259
+ companionSourceMD: "",
260
+ companionIntroMD: "",
261
+ trailingDescriptionMD: "",
262
+ mergeCompanionWithSource: false
263
+ };
264
+ const fenced = extractFirstAstroFence(body);
265
+ if (fenced) {
266
+ const { frontmatter, markup } = splitAstroFrontmatter(fenced.sourceCode);
267
+ const unsafeFrontmatter = isAstroFrontmatterPreviewUnsafe(frontmatter);
268
+ return {
269
+ descriptionMD: fenced.descriptionMD,
270
+ snippet: markup,
271
+ sourceCode: fenced.sourceCode,
272
+ sourceFromFence: true,
273
+ skipPreview: fenced.skipPreview || unsafeFrontmatter,
274
+ enableConsolePanel: fenced.enableConsolePanel,
275
+ consoleScripts: fenced.consoleScripts,
276
+ companionSourceMD: fenced.companionSourceMD,
277
+ companionIntroMD: fenced.companionIntroMD,
278
+ trailingDescriptionMD: fenced.trailingDescriptionMD,
279
+ mergeCompanionWithSource: fenced.mergeCompanionWithSource
280
+ };
281
+ }
282
+ const split = splitDescriptionAndSnippetRaw(body);
283
+ return {
284
+ descriptionMD: split.descriptionMD,
285
+ snippet: split.snippet,
286
+ sourceCode: split.snippet,
287
+ sourceFromFence: false,
288
+ skipPreview: false,
289
+ enableConsolePanel: false,
290
+ consoleScripts: [],
291
+ companionSourceMD: "",
292
+ companionIntroMD: "",
293
+ trailingDescriptionMD: "",
294
+ mergeCompanionWithSource: false
295
+ };
296
+ }
297
+ function extractTags(block) {
298
+ if (!block || !block.length) return [];
299
+ const found = /* @__PURE__ */ new Set();
300
+ const tagRegex = /<([A-Z][A-Za-z0-9_]*)\b/g;
301
+ let match;
302
+ while ((match = tagRegex.exec(block)) !== null) found.add(match[1]);
303
+ return Array.from(found);
304
+ }
305
+ function extractComponentTagsFromPreviewMarkdown(block, config = {}) {
306
+ if (!block || !block.length) return [];
307
+ const { defaultPreview = true, allowForcedPreview = true } = config;
308
+ const found = /* @__PURE__ */ new Set();
309
+ const lines = block.split("\n");
310
+ let inFence = false;
311
+ let fenceOpen = "";
312
+ let currentFenceLang = "";
313
+ let currentFenceFlags = /* @__PURE__ */ new Set();
314
+ const fenceBody = [];
315
+ for (const line of lines) {
316
+ const trimmed = line.trim();
317
+ if (trimmed.startsWith("```")) {
318
+ if (!inFence) {
319
+ inFence = true;
320
+ fenceOpen = line;
321
+ const parsed = parseFenceInfo(trimmed.slice(3).trim());
322
+ currentFenceLang = parsed.lang;
323
+ currentFenceFlags = parsed.flags;
324
+ fenceBody.length = 0;
325
+ continue;
326
+ }
327
+ if (currentFenceLang === "astro") {
328
+ const sourceCode = fenceBody.join("\n").trim();
329
+ const prepared = prepareExampleContent(`${fenceOpen}\n${sourceCode}\n${line}`);
330
+ const hasForcedPreviewFlag = currentFenceFlags.has("preview") || currentFenceFlags.has("console");
331
+ if (!prepared.skipPreview && (defaultPreview || allowForcedPreview && hasForcedPreviewFlag)) for (const tag of extractTags(prepared.snippet)) found.add(tag);
332
+ }
333
+ inFence = false;
334
+ fenceOpen = "";
335
+ currentFenceLang = "";
336
+ currentFenceFlags = /* @__PURE__ */ new Set();
337
+ fenceBody.length = 0;
338
+ continue;
339
+ }
340
+ if (inFence) {
341
+ fenceBody.push(line);
342
+ continue;
343
+ }
344
+ for (const tag of extractTags(line)) found.add(tag);
345
+ }
346
+ return Array.from(found);
347
+ }
348
+ function extractComponentTagsFromExamplesSections(sections) {
349
+ if (!sections?.length) return [];
350
+ const found = /* @__PURE__ */ new Set();
351
+ for (const section of sections) {
352
+ for (const tag of extractComponentTagsFromPreviewMarkdown(section.introMD)) found.add(tag);
353
+ for (const item of section.items) {
354
+ const prepared = prepareExampleContent(item.body);
355
+ if (prepared.skipPreview) continue;
356
+ for (const tag of extractTags(prepared.snippet)) found.add(tag);
357
+ }
358
+ }
359
+ return Array.from(found);
360
+ }
361
+
362
+ //#endregion
363
+ //#region src/docs/generate-mdx/mdx-builder.ts
364
+ /**
365
+ * Check if an import path uses the new barrel export pattern (no .astro extension)
366
+ */
367
+ function isBarrelImport(path) {
368
+ return !path.endsWith(".astro");
369
+ }
370
+ /**
371
+ * Convert a PascalCase component name to its folder path (kebab-case).
372
+ * Examples:
373
+ * - "Card" → "card"
374
+ * - "CardHeader" → "card"
375
+ * - "InputGroup" → "input-group"
376
+ * - "InputGroupAddon" → "input-group"
377
+ * - "StickySurface" → "sticky-surface"
378
+ * - "RadioGroup" → "radio-group"
379
+ * - "RadioGroupItem" → "radio-group"
380
+ */
381
+ function componentToFolder(name) {
382
+ const sortedFamilies = [
383
+ "InputGroup",
384
+ "LinkGroup",
385
+ "RadioGroup",
386
+ "ButtonGroup",
387
+ "StickySurface",
388
+ "DropdownMenu",
389
+ "NavigationMenu",
390
+ "ContextMenu",
391
+ "HoverCard",
392
+ "ToggleGroup"
393
+ ].sort((a, b) => b.length - a.length);
394
+ for (const family of sortedFamilies) if (name === family || name.startsWith(family)) return family.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
395
+ const baseMatch = name.match(/^[A-Z][a-z]*/);
396
+ return baseMatch ? baseMatch[0].toLowerCase() : name.toLowerCase();
397
+ }
398
+ function resolveComponentFolder(name, componentFolderMap) {
399
+ if (!componentFolderMap) return componentToFolder(name);
400
+ const direct = componentFolderMap[name];
401
+ if (direct && direct.length) return direct;
402
+ return componentToFolder(name);
403
+ }
404
+ /**
405
+ * Group component names by their folder path
406
+ */
407
+ function groupComponentsByFolder(components, componentFolderMap) {
408
+ const groups = /* @__PURE__ */ new Map();
409
+ for (const comp of components) {
410
+ const folder = resolveComponentFolder(comp, componentFolderMap);
411
+ if (!groups.has(folder)) groups.set(folder, []);
412
+ groups.get(folder).push(comp);
413
+ }
414
+ return groups;
415
+ }
416
+ /**
417
+ * Generate import lines for UI components using the barrel import pattern
418
+ */
419
+ function generateBarrelImports(components, componentsAlias, componentFolderMap) {
420
+ const groups = groupComponentsByFolder(components, componentFolderMap);
421
+ const lines = [];
422
+ const sortedFolders = Array.from(groups.keys()).sort();
423
+ for (const folder of sortedFolders) {
424
+ const names = groups.get(folder).sort();
425
+ if (folder.endsWith(".astro")) {
426
+ for (const name of names) lines.push(`import ${name} from '${componentsAlias}/${folder}';`);
427
+ continue;
428
+ }
429
+ lines.push(`import { ${names.join(", ")} } from '${componentsAlias}/${folder}';`);
430
+ }
431
+ return lines;
432
+ }
433
+ /**
434
+ * Generate import lines for UI components using the old default import pattern
435
+ */
436
+ function generateDefaultImports(components, componentsAlias) {
437
+ return components.slice().sort().map((name) => `import ${name} from '${componentsAlias}/${name}.astro';`);
438
+ }
439
+ function buildMdx(params) {
440
+ const { importName, importPath, title, description, descriptionBodyMDX, usageMDX, hasImport, propsList, propsTable, examples, examplesSections, componentFolderMap, autoImports, lucideIcons, primaryExampleMDX, componentSource, commandName, figmaUrl, componentsAlias, namedExports, apiMDX } = params;
441
+ const available = params.availableComponents ? new Set(params.availableComponents) : null;
442
+ const missingFromNames = (names) => names.filter((name) => available && !available.has(name) && !RESERVED_COMPONENTS.has(name) && !/Icon$/.test(name));
443
+ const missingFromSnippet = (snippet) => missingFromNames([...new Set([...snippet.matchAll(/<([A-Z][A-Za-z0-9_]*)\b/g)].map((match) => match[1]))]);
444
+ const missingNotice = (names) => {
445
+ return `> Preview unavailable. Run \`bejamas add ${[...new Set(names.map((name) => resolveComponentFolder(name, componentFolderMap)))].sort().join(" ")}\` to install the required components, then regenerate the docs.`;
446
+ };
447
+ const useBarrelPattern = isBarrelImport(importPath);
448
+ const sortedLucide = (lucideIcons ?? []).slice().sort();
449
+ const externalTopImports = [`import { Tabs as DocsTabs, TabItem as DocsTabItem } from '@astrojs/starlight/components';`, sortedLucide.length ? `import { ${sortedLucide.join(", ")} } from '@lucide/astro';` : null].filter((v) => v != null).slice().sort((a, b) => String(a).localeCompare(String(b)));
450
+ const sortedUiAuto = (autoImports ?? []).filter((name) => !available || available.has(name)).slice().sort();
451
+ const uiAutoLines = useBarrelPattern || componentFolderMap ? generateBarrelImports(sortedUiAuto, componentsAlias, componentFolderMap) : generateDefaultImports(sortedUiAuto, componentsAlias);
452
+ const exampleLines = (examples ?? []).filter((ex) => !missingFromSnippet(ex.source).length).map((ex) => `import ${ex.importName} from '${ex.importPath}';`).sort((a, b) => a.localeCompare(b));
453
+ let mainImportLine = null;
454
+ if (!hasImport) {
455
+ if (useBarrelPattern) mainImportLine = `import { ${[importName, ...namedExports ?? []].sort().join(", ")} } from '${importPath}';`;
456
+ else mainImportLine = `import ${importName} from '${importPath}';`;
457
+ }
458
+ const internalTopImports = [
459
+ mainImportLine,
460
+ ...uiAutoLines,
461
+ ...exampleLines
462
+ ].filter((v) => v != null).slice().sort((a, b) => String(a).localeCompare(String(b)));
463
+ const importLines = [
464
+ ...externalTopImports,
465
+ externalTopImports.length && internalTopImports.length ? "" : null,
466
+ ...internalTopImports
467
+ ].filter((v) => v !== null && v !== void 0);
468
+ const extractTags = (snippet) => {
469
+ const found = /* @__PURE__ */ new Set();
470
+ const tagRegex = /<([A-Z][A-Za-z0-9_]*)\b/g;
471
+ let m;
472
+ while ((m = tagRegex.exec(snippet)) !== null) found.add(m[1]);
473
+ return found;
474
+ };
475
+ const buildSnippetImportLines = (snippet) => {
476
+ if (!snippet || !snippet.length) return [];
477
+ const used = extractTags(snippet);
478
+ const usedIcons = sortedLucide.filter((n) => used.has(n));
479
+ const usedUi = (autoImports ?? []).filter((n) => used.has(n));
480
+ const includeMain = !hasImport && used.has(importName);
481
+ const usedNamedExports = useBarrelPattern && namedExports ? namedExports.filter((n) => used.has(n)) : [];
482
+ const external = [];
483
+ if (usedIcons.length) external.push(`import { ${usedIcons.join(", ")} } from '@lucide/astro';`);
484
+ const internal = [];
485
+ if (useBarrelPattern) {
486
+ if (includeMain || usedNamedExports.length > 0) {
487
+ const mainExports = [...includeMain ? [importName] : [], ...usedNamedExports].sort();
488
+ internal.push(`import { ${mainExports.join(", ")} } from '${importPath}';`);
489
+ }
490
+ if (usedUi.length > 0) internal.push(...generateBarrelImports(usedUi, componentsAlias, componentFolderMap));
491
+ } else {
492
+ if (includeMain) internal.push(`import ${importName} from '${importPath}';`);
493
+ internal.push(...componentFolderMap ? generateBarrelImports(usedUi, componentsAlias, componentFolderMap) : generateDefaultImports(usedUi, componentsAlias));
494
+ }
495
+ const externalSorted = external.slice().sort((a, b) => a.localeCompare(b));
496
+ const internalSorted = internal.slice().sort((a, b) => a.localeCompare(b));
497
+ return [
498
+ ...externalSorted,
499
+ externalSorted.length && internalSorted.length ? "" : null,
500
+ ...internalSorted
501
+ ].filter((v) => v !== null && v !== void 0);
502
+ };
503
+ let consolePreviewCounter = 0;
504
+ const wrapTextNodes = (snippet) => {
505
+ if (!snippet) return snippet;
506
+ return snippet.replace(/>([^<]+)</g, (match, inner) => {
507
+ const trimmed = inner.trim();
508
+ if (!trimmed.length) return match;
509
+ if (/^\{[\s\S]*\}$/.test(trimmed)) return match;
510
+ if (/\{[^}]*\}/.test(inner)) return match;
511
+ return `>{${JSON.stringify(inner)}}<`;
512
+ });
513
+ };
514
+ /**
515
+ * Normalize whitespace in preview content.
516
+ * This collapses ALL newlines and multiple spaces into single spaces to prevent
517
+ * MDX parsing issues. In particular, a '>' at the start of a line is interpreted
518
+ * as a block quote marker by MDX, which causes "Unexpected lazy line" errors.
519
+ */
520
+ const normalizeInlineWhitespace = (snippet) => {
521
+ if (!snippet) return snippet;
522
+ return snippet.replace(/\s+/g, " ").trim();
523
+ };
524
+ /**
525
+ * Convert <p> tags that contain component tags to <span> tags.
526
+ * This is necessary because components may render as block elements (like <div>),
527
+ * and HTML doesn't allow block elements inside <p>. The browser would automatically
528
+ * close the <p> before any block element, breaking the layout.
529
+ */
530
+ const convertParagraphsWithComponents = (snippet) => {
531
+ if (!snippet) return snippet;
532
+ return snippet.replace(/<p(\s[^>]*)?>([^]*?)<\/p>/gi, (match, attrs, content) => {
533
+ if (/<[A-Z][A-Za-z0-9]*/.test(content)) return `<span${attrs || ""} style="display:block">${content}</span>`;
534
+ return match;
535
+ });
536
+ };
537
+ const extractInlineScripts = (snippet) => {
538
+ if (!snippet) return {
539
+ markup: snippet,
540
+ scripts: []
541
+ };
542
+ const scripts = [];
543
+ return {
544
+ markup: snippet.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gi, (_, scriptBody) => {
545
+ scripts.push((scriptBody || "").trim());
546
+ return "";
547
+ }),
548
+ scripts
549
+ };
550
+ };
551
+ const toMdxPreview = (snippet, options = {}) => {
552
+ if (!snippet) return {
553
+ markup: "",
554
+ scripts: []
555
+ };
556
+ const { enableConsolePanel = false, consoleScripts = [] } = options;
557
+ const extracted = extractInlineScripts(snippet);
558
+ const resolvedConsoleScripts = enableConsolePanel && consoleScripts.length ? consoleScripts : enableConsolePanel ? extracted.scripts : [];
559
+ const withoutComments = extracted.markup.replace(/<!--([\s\S]*?)-->/g, "{/*$1*/}");
560
+ const withConvertedParagraphs = convertParagraphsWithComponents(withoutComments);
561
+ if (/<p[\s>]/i.test(withConvertedParagraphs)) return {
562
+ markup: normalizeInlineWhitespace(withConvertedParagraphs),
563
+ scripts: resolvedConsoleScripts
564
+ };
565
+ const normalized = normalizeInlineWhitespace(withConvertedParagraphs);
566
+ return {
567
+ markup: wrapTextNodes(normalized),
568
+ scripts: resolvedConsoleScripts
569
+ };
570
+ };
571
+ const buildConsoleRuntimeSource = (rootId, panelId, userScript) => {
572
+ return `(function () {
573
+ var root = document.getElementById(${JSON.stringify(rootId)});
574
+ var panel = document.getElementById(${JSON.stringify(panelId)});
575
+ if (!root || !panel) return;
576
+
577
+ var placeholder = panel.textContent || "Waiting for logs...";
578
+ var hasLogs = false;
579
+
580
+ var toText = function (value) {
581
+ if (typeof value === "string") return value;
582
+ if (value === undefined) return "undefined";
583
+ if (value === null) return "null";
584
+ try {
585
+ return JSON.stringify(value);
586
+ } catch (_error) {
587
+ return String(value);
588
+ }
589
+ };
590
+
591
+ var append = function (level, args) {
592
+ var stamp = new Date().toLocaleTimeString();
593
+ var body = Array.prototype.map.call(args, toText).join(" ");
594
+ var line = "[" + stamp + "] " + String(level).toUpperCase() + ": " + body;
595
+ var current = panel.textContent || "";
596
+ if (!hasLogs && current.trim() === placeholder.trim()) {
597
+ panel.textContent = line;
598
+ } else {
599
+ panel.textContent = current ? current + "\\n" + line : line;
600
+ }
601
+ hasLogs = true;
602
+ panel.scrollTop = panel.scrollHeight;
603
+ };
604
+
605
+ var methods = ["log", "info", "warn", "error"];
606
+ var proxyConsole = Object.create(console);
607
+ for (var i = 0; i < methods.length; i += 1) {
608
+ (function (methodName) {
609
+ var fallback = typeof console.log === "function" ? console.log.bind(console) : function () {};
610
+ var original =
611
+ typeof console[methodName] === "function"
612
+ ? console[methodName].bind(console)
613
+ : fallback;
614
+ proxyConsole[methodName] = function () {
615
+ var args = Array.prototype.slice.call(arguments);
616
+ original.apply(console, args);
617
+ append(methodName, args);
618
+ };
619
+ })(methods[i]);
620
+ }
621
+
622
+ try {
623
+ var run = new Function("console", "root", "panel", ${JSON.stringify(userScript)});
624
+ run(proxyConsole, root, panel);
625
+ } catch (error) {
626
+ var fallbackError = typeof console.error === "function" ? console.error.bind(console) : function () {};
627
+ fallbackError(error);
628
+ append("error", [error]);
629
+ }
630
+ })();`;
631
+ };
632
+ const renderConsoleScripts = (rootId, panelId, scripts) => {
633
+ if (!scripts.length) return "";
634
+ return scripts.filter((script) => script && script.trim().length).map((script) => {
635
+ const runtimeSource = buildConsoleRuntimeSource(rootId, panelId, script);
636
+ return `<script type="module" src="data:text/javascript;charset=utf-8,${encodeURIComponent(runtimeSource)}"><\/script>`;
637
+ }).join("\n");
638
+ };
639
+ const renderCompanionSourceMD = (sourceMD) => {
640
+ if (!sourceMD || !sourceMD.trim().length) return "";
641
+ const match = sourceMD.trim().match(/^```(\S*)([^\n]*)\n([\s\S]*?)\n```$/);
642
+ if (!match) return sourceMD;
643
+ const [, langRaw = "", flagsRaw = "", body = ""] = match;
644
+ const lang = langRaw.trim().toLowerCase();
645
+ if (![
646
+ "js",
647
+ "ts",
648
+ "javascript",
649
+ "typescript"
650
+ ].includes(lang)) return sourceMD;
651
+ const astroInfo = ["astro", flagsRaw.trim()].filter(Boolean).join(" ");
652
+ const scriptBody = body.replace(/\s+$/, "");
653
+ return [
654
+ `\`\`\`${astroInfo}`,
655
+ "<script>",
656
+ scriptBody,
657
+ "<\/script>",
658
+ "```"
659
+ ].join("\n");
660
+ };
661
+ const extractCompanionScriptBody = (sourceMD) => {
662
+ if (!sourceMD || !sourceMD.trim().length) return "";
663
+ const match = sourceMD.trim().match(/^```(\S*)([^\n]*)\n([\s\S]*?)\n```$/);
664
+ if (!match) return "";
665
+ const [, langRaw = "", , body = ""] = match;
666
+ const lang = langRaw.trim().toLowerCase();
667
+ if (![
668
+ "js",
669
+ "ts",
670
+ "javascript",
671
+ "typescript"
672
+ ].includes(lang)) return "";
673
+ return body.replace(/\s+$/, "");
674
+ };
675
+ const renderMergedAstroSourceMD = (fenceOpen, sourceBody, companionSourceMD) => {
676
+ const scriptBody = extractCompanionScriptBody(companionSourceMD);
677
+ if (!scriptBody.length) return [
678
+ fenceOpen,
679
+ sourceBody,
680
+ "```"
681
+ ].filter(Boolean).join("\n");
682
+ const normalizedSourceBody = sourceBody.replace(/\s+$/, "");
683
+ const parts = [fenceOpen];
684
+ if (normalizedSourceBody.length) parts.push(normalizedSourceBody, "");
685
+ parts.push("<script>", scriptBody, "<\/script>", "```");
686
+ return parts.join("\n");
687
+ };
688
+ const renderPreviewBlock = (snippet, options = {}) => {
689
+ const missing = missingFromSnippet(snippet);
690
+ if (missing.length) return missingNotice(missing);
691
+ const { enableConsolePanel = false } = options;
692
+ const preparedPreview = toMdxPreview(snippet, options);
693
+ if (!preparedPreview.markup || !preparedPreview.markup.length) return "";
694
+ if (!enableConsolePanel) return `<div class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">
695
+ ${preparedPreview.markup}
696
+ </div>`;
697
+ consolePreviewCounter += 1;
698
+ const rootId = `sl-bejamas-console-preview-${consolePreviewCounter}`;
699
+ const panelId = `${rootId}-output`;
700
+ const scriptTags = renderConsoleScripts(rootId, panelId, preparedPreview.scripts);
701
+ return [
702
+ `<div id="${rootId}" class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">`,
703
+ preparedPreview.markup,
704
+ "</div>",
705
+ `<div class="not-content sl-bejamas-console-log-shell px-4 md:px-10 py-4 border border-border border-t-0">`,
706
+ `<pre id="${panelId}" data-slot="event-log" class="sl-bejamas-console-log w-full p-3 rounded-md bg-muted text-xs font-mono text-muted-foreground min-h-[80px] max-h-[200px] overflow-y-auto">Waiting for logs...</pre>`,
707
+ scriptTags && scriptTags.length ? scriptTags : null,
708
+ "</div>"
709
+ ].filter((part) => part !== null).join("\n");
710
+ };
711
+ const renderAstroPreviewsInMarkdown = (block, config = {}) => {
712
+ if (!block || !block.length) return block;
713
+ const missingRaw = missingFromNames(extractComponentTagsFromPreviewMarkdown(block, {
714
+ defaultPreview: false,
715
+ allowForcedPreview: false
716
+ }));
717
+ if (missingRaw.length) return `${missingNotice(missingRaw)}\n\n\`\`\`\`mdx\n${block}\n\`\`\`\``;
718
+ const { defaultPreview = true, allowForcedPreview = true } = config;
719
+ const lines = block.split("\n");
720
+ const out = [];
721
+ let inFence = false;
722
+ let fenceOpen = "";
723
+ let currentFenceLang = "";
724
+ let currentFenceFlags = /* @__PURE__ */ new Set();
725
+ const fenceBody = [];
726
+ const findCompanionConsoleFence = (startIndex) => {
727
+ let index = startIndex;
728
+ while (index < lines.length) {
729
+ const trimmedLine = lines[index].trim();
730
+ if (!trimmedLine.length || !trimmedLine.startsWith("```")) {
731
+ index += 1;
732
+ continue;
733
+ }
734
+ const parsed = parseFenceInfo(trimmedLine.slice(3).trim());
735
+ if (![
736
+ "js",
737
+ "ts",
738
+ "javascript",
739
+ "typescript"
740
+ ].includes(parsed.lang)) return null;
741
+ let end = index + 1;
742
+ while (end < lines.length) {
743
+ if (lines[end].trim().startsWith("```")) return {
744
+ start: index,
745
+ end
746
+ };
747
+ end += 1;
748
+ }
749
+ return null;
750
+ }
751
+ return null;
752
+ };
753
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
754
+ const line = lines[lineIndex];
755
+ const trimmed = line.trim();
756
+ if (trimmed.startsWith("```")) {
757
+ if (!inFence) {
758
+ inFence = true;
759
+ fenceOpen = line;
760
+ const parsed = parseFenceInfo(trimmed.slice(3).trim());
761
+ currentFenceLang = parsed.lang;
762
+ currentFenceFlags = parsed.flags;
763
+ fenceBody.length = 0;
764
+ continue;
765
+ }
766
+ if (currentFenceLang === "astro") {
767
+ const sourceCode = decodeEscapedScriptTags(fenceBody.join("\n").trim());
768
+ const companionConsoleFence = currentFenceFlags.has("console") ? findCompanionConsoleFence(lineIndex + 1) : null;
769
+ const companionMarkdown = companionConsoleFence ? lines.slice(lineIndex + 1, companionConsoleFence.end + 1).join("\n") : "";
770
+ const prepared = prepareExampleContent(companionMarkdown.length ? `${fenceOpen}\n${sourceCode}\n${line}\n${companionMarkdown}` : `${fenceOpen}\n${sourceCode}\n${line}`);
771
+ const hasForcedPreviewFlag = currentFenceFlags.has("preview") || currentFenceFlags.has("console");
772
+ const shouldPreview = !prepared.skipPreview && (defaultPreview || allowForcedPreview && hasForcedPreviewFlag);
773
+ if (prepared.snippet && prepared.snippet.length && shouldPreview) {
774
+ const previewBlock = renderPreviewBlock(prepared.snippet, {
775
+ enableConsolePanel: prepared.enableConsolePanel,
776
+ consoleScripts: prepared.consoleScripts
777
+ });
778
+ if (previewBlock.length) out.push(previewBlock);
779
+ out.push("");
780
+ }
781
+ }
782
+ if (currentFenceLang === "astro") {
783
+ const normalizedFenceBody = decodeEscapedScriptTags(fenceBody.join("\n"));
784
+ const companionConsoleFence = currentFenceFlags.has("console") ? findCompanionConsoleFence(lineIndex + 1) : null;
785
+ const companionMarkdown = companionConsoleFence ? lines.slice(lineIndex + 1, companionConsoleFence.end + 1).join("\n") : "";
786
+ const prepared = companionMarkdown.length ? prepareExampleContent(`${fenceOpen}\n${normalizedFenceBody}\n${line}\n${companionMarkdown}`) : null;
787
+ if (prepared?.companionSourceMD && prepared.companionSourceMD.length && prepared.mergeCompanionWithSource) {
788
+ out.push(renderMergedAstroSourceMD(fenceOpen, normalizedFenceBody, prepared.companionSourceMD));
789
+ if (companionConsoleFence) lineIndex = companionConsoleFence.end;
790
+ } else {
791
+ out.push(fenceOpen);
792
+ if (normalizedFenceBody.length) out.push(...normalizedFenceBody.split("\n"));
793
+ out.push(line);
794
+ if (prepared?.companionSourceMD && prepared.companionSourceMD.length && companionConsoleFence) {
795
+ const companionIntro = lines.slice(lineIndex + 1, companionConsoleFence.start).join("\n");
796
+ if (companionIntro.length) out.push(companionIntro);
797
+ out.push(renderCompanionSourceMD(prepared.companionSourceMD));
798
+ lineIndex = companionConsoleFence.end;
799
+ }
800
+ }
801
+ } else {
802
+ out.push(fenceOpen);
803
+ if (fenceBody.length) out.push(...fenceBody);
804
+ out.push(line);
805
+ }
806
+ inFence = false;
807
+ fenceOpen = "";
808
+ currentFenceLang = "";
809
+ currentFenceFlags = /* @__PURE__ */ new Set();
810
+ fenceBody.length = 0;
811
+ continue;
812
+ }
813
+ if (inFence) {
814
+ fenceBody.push(line);
815
+ continue;
816
+ }
817
+ out.push(line);
818
+ }
819
+ if (inFence) {
820
+ out.push(fenceOpen);
821
+ if (currentFenceLang === "astro") {
822
+ const normalizedFenceBody = decodeEscapedScriptTags(fenceBody.join("\n"));
823
+ if (normalizedFenceBody.length) out.push(...normalizedFenceBody.split("\n"));
824
+ } else if (fenceBody.length) out.push(...fenceBody);
825
+ }
826
+ return out.join("\n").trim();
827
+ };
828
+ const renderedDescriptionBodyMDX = renderAstroPreviewsInMarkdown(descriptionBodyMDX || "");
829
+ const renderedUsageMDX = renderAstroPreviewsInMarkdown(usageMDX || "", { defaultPreview: false });
830
+ const renderedApiMDX = renderAstroPreviewsInMarkdown(apiMDX || "", {
831
+ defaultPreview: false,
832
+ allowForcedPreview: false
833
+ });
834
+ const primaryExampleSection = primaryExampleMDX && primaryExampleMDX.length ? `${renderPreviewBlock(primaryExampleMDX)}
835
+
836
+ \`\`\`astro
837
+ ${(() => {
838
+ const lines = buildSnippetImportLines(primaryExampleMDX);
839
+ return lines.length ? `---\n${lines.join("\n")}\n---\n\n` : "";
840
+ })()}${primaryExampleMDX}
841
+ \`\`\`` : null;
842
+ const renderExampleItem = (headingLevel, headingTitle, body) => {
843
+ const prepared = prepareExampleContent(body);
844
+ const blocks = [`${headingLevel} ${headingTitle}`];
845
+ if (prepared.descriptionMD && prepared.descriptionMD.length) blocks.push(renderAstroPreviewsInMarkdown(prepared.descriptionMD));
846
+ if (prepared.snippet && prepared.snippet.length && prepared.sourceCode && prepared.sourceCode.length && !prepared.skipPreview) blocks.push(renderPreviewBlock(prepared.snippet, {
847
+ enableConsolePanel: prepared.enableConsolePanel,
848
+ consoleScripts: prepared.consoleScripts
849
+ }));
850
+ if (prepared.sourceCode && prepared.sourceCode.length) {
851
+ const sourceBlock = prepared.sourceFromFence ? prepared.sourceCode : `${(() => {
852
+ const lines = buildSnippetImportLines(prepared.sourceCode);
853
+ return lines.length ? `---\n${lines.join("\n")}\n---\n\n` : "";
854
+ })()}${prepared.sourceCode}`;
855
+ if (prepared.companionSourceMD && prepared.companionSourceMD.length && prepared.mergeCompanionWithSource) blocks.push(renderMergedAstroSourceMD("```astro", sourceBlock, prepared.companionSourceMD));
856
+ else blocks.push(`\`\`\`astro
857
+ ${sourceBlock}
858
+ \`\`\``);
859
+ if (prepared.companionIntroMD && prepared.companionIntroMD.length && !prepared.mergeCompanionWithSource) blocks.push(prepared.companionIntroMD);
860
+ if (prepared.companionSourceMD && prepared.companionSourceMD.length && !prepared.mergeCompanionWithSource) blocks.push(renderCompanionSourceMD(prepared.companionSourceMD));
861
+ if (prepared.trailingDescriptionMD && prepared.trailingDescriptionMD.length) blocks.push(renderAstroPreviewsInMarkdown(prepared.trailingDescriptionMD));
862
+ }
863
+ return blocks.join("\n\n");
864
+ };
865
+ const renderedExampleSections = [];
866
+ if (examplesSections && examplesSections.length) {
867
+ let implicitExampleCounter = 1;
868
+ for (const section of examplesSections) {
869
+ const sectionParts = [];
870
+ const hasSectionTitle = !!(section.title && section.title.length);
871
+ if (hasSectionTitle) sectionParts.push(`### ${section.title}`);
872
+ if (section.introMD && section.introMD.length) sectionParts.push(renderAstroPreviewsInMarkdown(section.introMD));
873
+ if (section.items && section.items.length) {
874
+ const itemHeadingLevel = hasSectionTitle ? "####" : "###";
875
+ for (const item of section.items) sectionParts.push(renderExampleItem(itemHeadingLevel, item.title, item.body));
876
+ } else if (!hasSectionTitle && section.introMD && section.introMD.length) {
877
+ const prepared = prepareExampleContent(section.introMD);
878
+ if (prepared.snippet && prepared.snippet.length) {
879
+ sectionParts.length = 0;
880
+ sectionParts.push(renderExampleItem("###", `Example ${implicitExampleCounter}`, section.introMD));
881
+ implicitExampleCounter += 1;
882
+ }
883
+ }
884
+ if (sectionParts.length) renderedExampleSections.push(sectionParts.join("\n\n").trim());
885
+ }
886
+ }
887
+ if (examples && examples.length) for (const ex of examples) renderedExampleSections.push(`### ${ex.title}
888
+
889
+ ${missingFromSnippet(ex.source).length ? missingNotice(missingFromSnippet(ex.source)) : `<div class="not-content">
890
+ <${ex.importName} />
891
+ </div>`}
892
+
893
+ \`\`\`astro
894
+ ${ex.source}
895
+ \`\`\``);
896
+ const formatDefault = (val) => {
897
+ if (val == null) return "";
898
+ let raw = String(val).trim();
899
+ if (!raw.length) return "";
900
+ raw = raw.replace(/[\u201C\u201D\u201E\u201F]/g, "\"").replace(/[\u2018\u2019]/g, "'");
901
+ const isSingleQuoted = /^'[^']*'$/.test(raw);
902
+ const isDoubleQuoted = /^"[^"]*"$/.test(raw);
903
+ const isBacktickSimple = /^`[^`]*`$/.test(raw) && raw.indexOf("${") === -1;
904
+ let content = raw;
905
+ if (isSingleQuoted || isDoubleQuoted || isBacktickSimple) content = `"${raw.slice(1, -1)}"`;
906
+ content = content.replace(/\|/g, "\\|");
907
+ const hasTick = content.includes("`");
908
+ const hasDoubleTick = content.includes("``");
909
+ const fence = !hasTick ? "`" : !hasDoubleTick ? "``" : "```";
910
+ return `${fence}${content}${fence}`;
911
+ };
912
+ const installationSection = `## Installation
913
+
914
+ <DocsTabs syncKey="pkg">
915
+ <DocsTabItem label="bun">
916
+ \`\`\`bash
917
+ bunx bejamas add ${commandName}
918
+ \`\`\`
919
+ </DocsTabItem>
920
+ <DocsTabItem label="npm">
921
+ \`\`\`bash
922
+ npx bejamas add ${commandName}
923
+ \`\`\`
924
+ </DocsTabItem>
925
+ <DocsTabItem label="pnpm">
926
+ \`\`\`bash
927
+ pnpm dlx bejamas add ${commandName}
928
+ \`\`\`
929
+ </DocsTabItem>
930
+ <DocsTabItem label="yarn">
931
+ \`\`\`bash
932
+ yarn dlx bejamas add ${commandName}
933
+ \`\`\`
934
+ </DocsTabItem>
935
+ </DocsTabs>`;
936
+ const serializeFrontmatter = (label, value) => {
937
+ if (!value || !value.length) return null;
938
+ return `${label}: ${JSON.stringify(value)}`;
939
+ };
940
+ return [
941
+ "---",
942
+ serializeFrontmatter("title", title),
943
+ serializeFrontmatter("description", description),
944
+ serializeFrontmatter("figmaUrl", figmaUrl),
945
+ "---",
946
+ "",
947
+ ...importLines,
948
+ importLines.length ? "" : null,
949
+ renderedDescriptionBodyMDX && renderedDescriptionBodyMDX.length ? renderedDescriptionBodyMDX : null,
950
+ renderedDescriptionBodyMDX && renderedDescriptionBodyMDX.length ? "" : null,
951
+ primaryExampleSection,
952
+ primaryExampleSection ? "" : null,
953
+ installationSection,
954
+ "",
955
+ renderedUsageMDX && renderedUsageMDX.length ? `## Usage\n\n${renderedUsageMDX}` : null,
956
+ "",
957
+ propsTable && propsTable.length ? `## Props\n\n| Prop | Type | Default |\n|---|---|---|\n${propsTable.map((p) => `| <code>${p.name}</code> | \`${(p.type || "").replace(/\|/g, "\\|")}\` | ${formatDefault(p.defaultValue)} |`).join("\n")}` : propsList ? `## Props\n\n${propsList}` : null,
958
+ propsTable && propsTable.length || propsList ? "" : null,
959
+ renderedExampleSections.length ? `## Examples\n\n` + renderedExampleSections.join("\n\n") : null,
960
+ renderedExampleSections.length ? "" : null,
961
+ renderedApiMDX && renderedApiMDX.length ? `## API Reference\n\n${renderedApiMDX}` : null
962
+ ].filter((v) => v !== null && v !== void 0).join("\n").trim() + "\n";
963
+ }
964
+
965
+ //#endregion
966
+ //#region src/docs/generate-mdx/index.ts
967
+ /**
968
+ * Discover components in the components directory.
969
+ * Supports both:
970
+ * - Old pattern: flat .astro files in components/
971
+ * - New pattern: folders with index.ts barrel exports
972
+ */
973
+ async function discoverComponents(componentsDir) {
974
+ const entries = [];
975
+ const dirEntries = (await readdir(componentsDir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
976
+ for (const entry of dirEntries) if (entry.isDirectory()) {
977
+ const folderPath = join(componentsDir, entry.name);
978
+ const indexPath = join(folderPath, "index.ts");
979
+ if (existsSync(indexPath)) {
980
+ const localExports = parseBarrelExports(await readFile(indexPath, "utf-8")).filter(({ file }) => dirname(join(folderPath, file)) === folderPath && existsSync(join(folderPath, file)));
981
+ const namedExports = localExports.map(({ name }) => name);
982
+ const mainComponentName = findMainComponent(namedExports, entry.name);
983
+ if (mainComponentName) {
984
+ const mainFilePath = join(folderPath, localExports.find(({ name }) => name === mainComponentName).file);
985
+ if (existsSync(mainFilePath)) {
986
+ const subComponents = namedExports.filter((n) => n !== mainComponentName);
987
+ entries.push({
988
+ name: mainComponentName,
989
+ filePath: mainFilePath,
990
+ folderName: entry.name,
991
+ isFolder: true,
992
+ namedExports: subComponents
993
+ });
994
+ }
995
+ }
996
+ }
997
+ } else if (entry.isFile() && extname(entry.name).toLowerCase() === ".astro") {
998
+ const componentName = entry.name.replace(/\.astro$/i, "");
999
+ entries.push({
1000
+ name: componentName,
1001
+ filePath: join(componentsDir, entry.name),
1002
+ folderName: "",
1003
+ isFolder: false,
1004
+ namedExports: []
1005
+ });
1006
+ }
1007
+ const nestedNames = new Set(entries.filter((entry) => entry.isFolder).map((entry) => entry.name));
1008
+ return entries.filter((entry) => entry.isFolder || !nestedNames.has(entry.name));
1009
+ }
1010
+ function buildComponentFolderMap(components) {
1011
+ const map = {};
1012
+ for (const component of components) {
1013
+ if (!component.isFolder) {
1014
+ map[component.name] = `${component.name}.astro`;
1015
+ continue;
1016
+ }
1017
+ if (!component.folderName) continue;
1018
+ map[component.name] = component.folderName;
1019
+ for (const sub of component.namedExports) map[sub] = component.folderName;
1020
+ }
1021
+ return map;
1022
+ }
1023
+ /**
1024
+ * Parse a barrel (index.ts) file to extract named exports.
1025
+ * Handles patterns like:
1026
+ * - export { default as Card } from "./Card.astro";
1027
+ * - export { default as CardHeader } from "./CardHeader.astro";
1028
+ */
1029
+ function parseBarrelExports(content) {
1030
+ const exports = [];
1031
+ const exportRegex = /export\s*\{\s*default\s+as\s+(\w+)\s*\}\s*from\s*["'](\.\/[^"']+\.astro)["']/g;
1032
+ let match;
1033
+ while ((match = exportRegex.exec(content)) !== null) exports.push({
1034
+ name: match[1],
1035
+ file: match[2]
1036
+ });
1037
+ return exports;
1038
+ }
1039
+ /**
1040
+ * Find the main component from a list of exports.
1041
+ * The main component is typically the one that matches the folder name (PascalCase).
1042
+ */
1043
+ function findMainComponent(exports, folderName) {
1044
+ if (exports.length === 0) return null;
1045
+ const expectedName = folderName.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1046
+ const exactMatch = exports.find((e) => e === expectedName);
1047
+ if (exactMatch) return exactMatch;
1048
+ return exports[0];
1049
+ }
1050
+ async function main() {
1051
+ const DEBUG = process.env.BEJAMAS_DEBUG === "1" || process.env.BEJAMAS_DEBUG === "true";
1052
+ const cwd = process.env.BEJAMAS_DOCS_CWD && process.env.BEJAMAS_DOCS_CWD.length ? process.env.BEJAMAS_DOCS_CWD : process.cwd();
1053
+ const config = await getConfig(cwd);
1054
+ const componentsAlias = (config?.aliases?.ui || config?.aliases?.components || "@bejamas/ui/components").replace(/\/$/, "");
1055
+ const componentsDirFromConfig = config?.resolvedPaths?.ui || config?.resolvedPaths?.components;
1056
+ let uiRoot = componentsDirFromConfig ? dirname(dirname(componentsDirFromConfig)) : resolveUiRoot(cwd);
1057
+ let componentsDir = join(uiRoot, "src", "components");
1058
+ if (componentsDirFromConfig) {
1059
+ componentsDir = componentsDirFromConfig;
1060
+ uiRoot = dirname(dirname(componentsDirFromConfig));
1061
+ }
1062
+ if (!existsSync(componentsDir)) componentsDir = join(uiRoot, "src", "components");
1063
+ const outDir = resolveOutDir(cwd);
1064
+ mkdirSync(outDir, { recursive: true });
1065
+ if (DEBUG) {
1066
+ logger.info(`[docs-generator] cwd: ${cwd}`);
1067
+ logger.info(`[docs-generator] uiRoot: ${uiRoot}`);
1068
+ logger.info(`[docs-generator] componentsDir: ${componentsDir}`);
1069
+ logger.info(`[docs-generator] outDir: ${outDir}`);
1070
+ }
1071
+ const components = await discoverComponents(componentsDir);
1072
+ const componentFolderMap = buildComponentFolderMap(components);
1073
+ const availableComponents = Object.keys(componentFolderMap);
1074
+ const rewriteAliases = (source) => source.replace(/@bejamas\/ui\/components(?=[/"'])/g, componentsAlias).replace(/@bejamas\/(?:ui|registry)\/lib\/utils(?=["'])/g, config?.aliases?.utils ?? "@/lib/utils").replace(/@bejamas\/(?:ui|registry)\/lib(?=[/"'])/g, config?.aliases?.lib ?? "@/lib");
1075
+ if (DEBUG) {
1076
+ logger.info(`[docs-generator] components found: ${components.length}`);
1077
+ if (components.length) logger.info(`[docs-generator] first few: ${components.slice(0, 5).map((c) => c.name).join(", ")}`);
1078
+ }
1079
+ let generatedCount = 0;
1080
+ const total = components.length;
1081
+ const spin = spinner(`Generating docs (0/${total})`).start();
1082
+ for (const component of components) {
1083
+ const { name: pascal, filePath, folderName, isFolder, namedExports } = component;
1084
+ const astroFile = await readFile(filePath, "utf-8");
1085
+ const frontmatterCode = extractFrontmatter(astroFile);
1086
+ const sourceFile = createSourceFileFromFrontmatter(frontmatterCode);
1087
+ const meta = parseJsDocMetadata(rewriteAliases(frontmatterCode));
1088
+ const declaredProps = extractPropsFromDeclaredProps(sourceFile);
1089
+ const destructuredProps = extractPropsFromAstroProps(sourceFile);
1090
+ const defaultsMap = /* @__PURE__ */ new Map();
1091
+ for (const p of destructuredProps) if (p.name && p.hasDefault) defaultsMap.set(p.name, p.defaultValue || null);
1092
+ const propsTable = (declaredProps.length ? declaredProps : []).map((p) => ({
1093
+ name: p.name,
1094
+ type: p.type,
1095
+ required: !p.optional,
1096
+ defaultValue: defaultsMap.has(p.name) ? defaultsMap.get(p.name) : null
1097
+ }));
1098
+ const slug = slugify(pascal);
1099
+ const title = meta.title || meta.name || pascal;
1100
+ const description = meta.description || "";
1101
+ const descriptionBodyMDX = meta.descriptionBodyMDX || "";
1102
+ const figmaUrl = meta.figmaUrl || "";
1103
+ const propsList = "";
1104
+ const importName = pascal;
1105
+ const importPath = isFolder ? `${componentsAlias}/${folderName}` : `${componentsAlias}/${pascal}.astro`;
1106
+ const { text: usageMDX, hasImport: hasImportUsage } = normalizeUsageMDX(meta.usageMDX || "", pascal);
1107
+ const primaryExampleMDX = normalizeBlockMDX(meta.primaryExampleMDX || "").trim();
1108
+ const examplesMDX = normalizeBlockMDX(meta.examplesMDX || "").trim();
1109
+ const apiMDX = normalizeBlockMDX(meta.apiMDX || "").trim();
1110
+ const hasImportExamples = detectHasImportTopLevel(examplesMDX, pascal);
1111
+ const hasImportPrimary = detectHasImportTopLevel(primaryExampleMDX, pascal);
1112
+ const hasImport = hasImportUsage || hasImportExamples || hasImportPrimary;
1113
+ let exampleRelPaths = [];
1114
+ let examples = [];
1115
+ const parsedExamplesSections = parseExamplesSections(examplesMDX);
1116
+ if (parsedExamplesSections.length === 0) {
1117
+ exampleRelPaths = await discoverExamples(filePath, componentsDir);
1118
+ examples = (exampleRelPaths || []).map((rel) => {
1119
+ const posixRel = rel.split(__require("path").sep).join(__require("path").posix.sep);
1120
+ const importPathEx = `${componentsAlias}/${posixRel}`;
1121
+ const abs = join(componentsDir, rel);
1122
+ const source = rewriteAliases(__require("fs").readFileSync(abs, "utf-8"));
1123
+ const base = toIdentifier(__require("path").basename(rel, __require("path").extname(rel)));
1124
+ return {
1125
+ importName: `${pascal}${base}`,
1126
+ importPath: importPathEx,
1127
+ title: base,
1128
+ source
1129
+ };
1130
+ });
1131
+ }
1132
+ const usedInUsage = extractComponentTagsFromPreviewMarkdown(usageMDX, { defaultPreview: false }).filter((n) => n !== pascal);
1133
+ const usedInDescription = extractComponentTagsFromPreviewMarkdown(descriptionBodyMDX).filter((n) => n !== pascal);
1134
+ const usedInExamples = extractComponentTagsFromExamplesSections(parsedExamplesSections).filter((n) => n !== pascal);
1135
+ const usedInPrimary = extractComponentTagsFromMDX(primaryExampleMDX).filter((n) => n !== pascal);
1136
+ const usedInApi = extractComponentTagsFromPreviewMarkdown(apiMDX, {
1137
+ defaultPreview: false,
1138
+ allowForcedPreview: false
1139
+ }).filter((n) => n !== pascal);
1140
+ const autoSet = /* @__PURE__ */ new Set([
1141
+ ...usedInUsage,
1142
+ ...usedInDescription,
1143
+ ...usedInExamples,
1144
+ ...usedInPrimary,
1145
+ ...usedInApi
1146
+ ]);
1147
+ const usedNamedExports = isFolder ? namedExports.filter((n) => autoSet.has(n)) : [];
1148
+ if (isFolder) for (const n of namedExports) autoSet.delete(n);
1149
+ const autoImports = Array.from(autoSet).filter((name) => !RESERVED_COMPONENTS.has(name));
1150
+ const lucideIcons = autoImports.filter((n) => /Icon$/.test(n));
1151
+ const uiAutoImports = autoImports.filter((n) => !/Icon$/.test(n));
1152
+ const missingComponents = uiAutoImports.filter((name) => !availableComponents.includes(name));
1153
+ if (missingComponents.length) logger.warn(`${slug}.mdx: previews require ${missingComponents.sort().join(", ")}; keeping their source examples without executing them.`);
1154
+ const mdx = buildMdx({
1155
+ importName,
1156
+ importPath,
1157
+ title,
1158
+ description,
1159
+ usageMDX,
1160
+ hasImport,
1161
+ propsList,
1162
+ propsTable,
1163
+ examples,
1164
+ examplesSections: parsedExamplesSections,
1165
+ componentFolderMap,
1166
+ availableComponents,
1167
+ autoImports: uiAutoImports,
1168
+ lucideIcons,
1169
+ primaryExampleMDX,
1170
+ componentSource: astroFile.trim(),
1171
+ commandName: slug,
1172
+ figmaUrl,
1173
+ descriptionBodyMDX,
1174
+ componentsAlias,
1175
+ namedExports: isFolder ? usedNamedExports : void 0,
1176
+ apiMDX
1177
+ });
1178
+ const outFile = join(outDir, `${slug}.mdx`);
1179
+ mkdirSync(dirname(outFile), { recursive: true });
1180
+ await writeFile(outFile, mdx, "utf-8");
1181
+ generatedCount += 1;
1182
+ spin.text = `Generating docs (${generatedCount}/${total}) - ${title}`;
1183
+ if (DEBUG) logger.info(`Generated ${outFile}`);
1184
+ }
1185
+ spin.succeed(`Created ${generatedCount} file${generatedCount === 1 ? "" : "s"}:`);
1186
+ components.map((c) => {
1187
+ const slug = slugify(c.name);
1188
+ const outFile = join(outDir, `${slug}.mdx`);
1189
+ return relative(cwd, outFile);
1190
+ }).sort((a, b) => a.localeCompare(b)).forEach((p) => {
1191
+ logger.log(` - ${p}`);
1192
+ });
1193
+ logger.break();
1194
+ }
1195
+ async function runDocsGenerator() {
1196
+ await main();
1197
+ }
1198
+ if (process.env.BEJAMAS_SKIP_AUTO_RUN !== "1" && process.env.BEJAMAS_SKIP_AUTO_RUN !== "true") runDocsGenerator().catch((err) => {
1199
+ logger.error(String(err));
1200
+ process.exit(1);
1201
+ });
1202
+
1203
+ //#endregion
1204
+ export { runDocsGenerator };
1205
+ //# sourceMappingURL=generate-mdx-BSRQJPrF.js.map