bejamas 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,13 +25,13 @@ npx bejamas add [component]
25
25
  ### Example
26
26
 
27
27
  ```bash
28
- npx shadcn add button
28
+ npx bejamas add button
29
29
  ```
30
30
 
31
31
  You can also run the command without any arguments to view a list of all available components:
32
32
 
33
33
  ```bash
34
- npx shadcn add
34
+ npx bejamas add
35
35
  ```
36
36
 
37
37
  ## Documentation
@@ -1,272 +1,12 @@
1
- import { getConfig, logger, spinner } from "./get-config-CJUwQ2Xb.js";
1
+ import { RESERVED_COMPONENTS, createSourceFileFromFrontmatter, detectHasImportTopLevel, discoverExamples, extractComponentTagsFromMDX, extractFrontmatter, extractPropsFromAstroProps, extractPropsFromDeclaredProps, getConfig, logger, normalizeBlockMDX, normalizeUsageMDX, parseJsDocMetadata, resolveOutDir, resolveUiRoot, slugify, spinner, toIdentifier } from "./utils-DfvCox_O.js";
2
2
  import { createRequire } from "node:module";
3
- import { createRequire as createRequire$1 } from "module";
4
- import path, { dirname, extname, join, posix, relative } from "path";
3
+ import { dirname, extname, join, relative } from "path";
5
4
  import { existsSync, mkdirSync } from "fs";
6
5
  import { readdir, writeFile } from "fs/promises";
7
- import { Project, SyntaxKind } from "ts-morph";
8
6
 
9
7
  //#region rolldown:runtime
10
8
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
11
9
 
12
- //#endregion
13
- //#region src/docs/generate-mdx/utils.ts
14
- const RESERVED_COMPONENTS = new Set([
15
- "Fragment",
16
- "CodePackageManagers",
17
- "DocsTabs",
18
- "DocsTabItem",
19
- "DocsCodePackageManagers",
20
- "Tabs",
21
- "TabItem"
22
- ]);
23
- function slugify(input) {
24
- return input.replace(/\.(astro|md|mdx|tsx|ts|jsx|js)$/i, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/\s+/g, "-").replace(/_+/g, "-").toLowerCase();
25
- }
26
- function extractFrontmatter(source) {
27
- const match = source.match(/^---\n([\s\S]*?)\n---/);
28
- return match && match[1] || "";
29
- }
30
- function toIdentifier(name) {
31
- const base = name.replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9]+/g, " ").trim().replace(/\b\w/g, (c) => c.toUpperCase()).replace(/\s+/g, "");
32
- return /^[A-Za-z_]/.test(base) ? base : `Ex${base}`;
33
- }
34
- function parseJsDocMetadata(frontmatterCode) {
35
- const jsDocMatch = frontmatterCode.match(/\/\*\*([\s\S]*?)\*\//);
36
- if (!jsDocMatch) return {};
37
- const lines = jsDocMatch[1].split("\n").map((l) => l.replace(/^\s*\*\s?/, ""));
38
- const meta = {};
39
- let inUsage = false;
40
- let inExamples = false;
41
- let inPrimaryExample = false;
42
- let captureDescriptionBody = false;
43
- const usageLines = [];
44
- const examplesLines = [];
45
- const primaryExampleLines = [];
46
- const descriptionBodyLines = [];
47
- for (const rawLine of lines) {
48
- const line = rawLine;
49
- if (inUsage) if (line.trim().startsWith("@")) inUsage = false;
50
- else {
51
- usageLines.push(line);
52
- continue;
53
- }
54
- if (inPrimaryExample) if (line.trim().startsWith("@")) inPrimaryExample = false;
55
- else {
56
- primaryExampleLines.push(line);
57
- continue;
58
- }
59
- if (inExamples) if (line.trim().startsWith("@")) inExamples = false;
60
- else {
61
- examplesLines.push(line);
62
- continue;
63
- }
64
- if (captureDescriptionBody) if (line.trim().startsWith("@")) captureDescriptionBody = false;
65
- else {
66
- descriptionBodyLines.push(line);
67
- continue;
68
- }
69
- if (line.trim().startsWith("@component")) meta.name = line.replace("@component", "").trim();
70
- else if (line.trim().startsWith("@title")) meta.title = line.replace("@title", "").trim();
71
- else if (line.trim().startsWith("@description")) {
72
- meta.description = line.replace("@description", "").trim();
73
- captureDescriptionBody = true;
74
- continue;
75
- } else if (line.trim().startsWith("@figmaUrl")) meta.figmaUrl = line.replace("@figmaUrl", "").trim();
76
- else if (line.trim().startsWith("@figma")) meta.figmaUrl = line.replace("@figma", "").trim();
77
- else if (line.trim().startsWith("@usage")) {
78
- inUsage = true;
79
- continue;
80
- } else if (line.trim().startsWith("@examples")) {
81
- inExamples = true;
82
- continue;
83
- } else if (line.trim().startsWith("@preview")) {
84
- inPrimaryExample = true;
85
- continue;
86
- } else if (line.trim().startsWith("@example")) {
87
- inPrimaryExample = true;
88
- continue;
89
- }
90
- }
91
- if (usageLines.length) meta.usageMDX = usageLines.join("\n").trim();
92
- if (examplesLines.length) meta.examplesMDX = examplesLines.join("\n").trim();
93
- if (primaryExampleLines.length) meta.primaryExampleMDX = primaryExampleLines.join("\n").trim();
94
- if (descriptionBodyLines.length) meta.descriptionBodyMDX = descriptionBodyLines.join("\n").trim();
95
- return meta;
96
- }
97
- function extractPropsFromAstroProps(sourceFile) {
98
- function unwrapAstroProps(node) {
99
- let current = node;
100
- for (let i = 0; i < 10; i += 1) {
101
- const kind = current.getKind();
102
- if (kind === SyntaxKind.PropertyAccessExpression) {
103
- const expr = current.getExpression();
104
- if (expr && expr.getText() === "Astro" && current.getName() === "props") return current;
105
- return null;
106
- }
107
- if (kind === SyntaxKind.AsExpression || kind === SyntaxKind.TypeAssertion || kind === SyntaxKind.SatisfiesExpression || kind === SyntaxKind.NonNullExpression || kind === SyntaxKind.ParenthesizedExpression) {
108
- const next = current.getExpression && current.getExpression();
109
- if (!next) return null;
110
- current = next;
111
- continue;
112
- }
113
- return null;
114
- }
115
- return null;
116
- }
117
- const target = sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration).find((decl) => {
118
- const init = decl.getInitializer();
119
- if (!init) return false;
120
- return !!unwrapAstroProps(init);
121
- });
122
- if (!target) return [];
123
- const nameNode = target.getNameNode();
124
- if (!nameNode || nameNode.getKind() !== SyntaxKind.ObjectBindingPattern) return [];
125
- return nameNode.asKindOrThrow(SyntaxKind.ObjectBindingPattern).getElements().map((el) => {
126
- if (!!el.getDotDotDotToken()) return {
127
- isRest: true,
128
- hasDefault: false,
129
- alias: el.getName()
130
- };
131
- const propertyNameNode = el.getPropertyNameNode();
132
- const name = el.getName();
133
- const propName = propertyNameNode ? propertyNameNode.getText() : name;
134
- const initializer = el.getInitializer();
135
- let defaultValue;
136
- if (initializer) defaultValue = initializer.getText();
137
- return {
138
- name: propName,
139
- hasDefault: initializer != null,
140
- defaultValue
141
- };
142
- });
143
- }
144
- function extractPropsFromDeclaredProps(sourceFile) {
145
- function normalizeTypeText(text) {
146
- if (!text) return "";
147
- return text.replace(/\s+/g, " ").replace(/;\s*$/, "").trim();
148
- }
149
- const iface = sourceFile.getInterface("Props");
150
- if (iface) return iface.getProperties().map((prop) => {
151
- const name = prop.getName();
152
- const typeNode = prop.getTypeNode();
153
- const rawType = typeNode ? typeNode.getText() : prop.getType().getText();
154
- const typeText = normalizeTypeText(rawType);
155
- const optional = prop.hasQuestionToken();
156
- return {
157
- name,
158
- type: typeText,
159
- optional
160
- };
161
- });
162
- const typeAlias = sourceFile.getTypeAlias("Props");
163
- if (typeAlias) {
164
- const typeNode = typeAlias.getTypeNode();
165
- if (typeNode && typeNode.getKind() === SyntaxKind.TypeLiteral) return typeNode.asKindOrThrow(SyntaxKind.TypeLiteral).getProperties().map((prop) => {
166
- const name = prop.getName();
167
- const tn = prop.getTypeNode();
168
- const rawType = tn ? tn.getText() : prop.getType().getText();
169
- const typeText = normalizeTypeText(rawType);
170
- const optional = prop.hasQuestionToken();
171
- return {
172
- name,
173
- type: typeText,
174
- optional
175
- };
176
- });
177
- }
178
- return [];
179
- }
180
- function resolveUiRoot(cwd) {
181
- const require$1 = createRequire$1(import.meta.url);
182
- const envRoot = process.env.BEJAMAS_UI_ROOT;
183
- if (envRoot && existsSync(path.join(envRoot, "package.json"))) return envRoot;
184
- try {
185
- const pkgPath = require$1.resolve("@bejamas/ui/package.json", { paths: [cwd] });
186
- return path.dirname(pkgPath);
187
- } catch {}
188
- let current = cwd;
189
- for (let i = 0; i < 6; i += 1) {
190
- const candidate = path.join(current, "packages", "ui", "package.json");
191
- if (existsSync(candidate)) return path.dirname(candidate);
192
- const parent = path.dirname(current);
193
- if (parent === current) break;
194
- current = parent;
195
- }
196
- try {
197
- const anyEntry = require$1.resolve("@bejamas/ui/*", { paths: [cwd] });
198
- return path.resolve(anyEntry, "..", "..");
199
- } catch {}
200
- throw new Error("Unable to locate @bejamas/ui in the workspace");
201
- }
202
- function resolveOutDir(cwd) {
203
- const envOut = process.env.BEJAMAS_DOCS_OUT_DIR;
204
- if (envOut && envOut.length) return path.isAbsolute(envOut) ? envOut : path.resolve(cwd, envOut);
205
- return path.resolve(cwd, "../../apps/web/src/content/docs/components");
206
- }
207
- function detectHasImportTopLevel(block, pascalName) {
208
- if (!block) return false;
209
- let inFence = false;
210
- const importLineRegex = /* @__PURE__ */ new RegExp(`^\\s*import\\s+.*\\bfrom\\s+['"][^'"]+\\b${pascalName}\\.astro['"]`);
211
- for (const line of block.split("\n")) {
212
- if (line.trim().startsWith("```")) {
213
- inFence = !inFence;
214
- continue;
215
- }
216
- if (!inFence && importLineRegex.test(line)) return true;
217
- }
218
- return false;
219
- }
220
- function normalizeBlockMDX(block) {
221
- if (!block) return "";
222
- return block.replace(/from\s+['"]@\/ui\/components\//g, "from '@bejamas/ui/components/");
223
- }
224
- function normalizeUsageMDX(usageMDX, pascalName) {
225
- const normalized = normalizeBlockMDX(usageMDX);
226
- const hasImport = detectHasImportTopLevel(normalized, pascalName);
227
- return {
228
- text: normalized.trim(),
229
- hasImport
230
- };
231
- }
232
- function extractComponentTagsFromMDX(block) {
233
- if (!block) return [];
234
- let inFence = false;
235
- const found = /* @__PURE__ */ new Set();
236
- const tagRegex = /<([A-Z][A-Za-z0-9_]*)\b/g;
237
- for (const line of block.split("\n")) {
238
- if (line.trim().startsWith("```")) {
239
- inFence = !inFence;
240
- continue;
241
- }
242
- if (inFence) continue;
243
- let match;
244
- while ((match = tagRegex.exec(line)) !== null) {
245
- const name = match[1];
246
- found.add(name);
247
- }
248
- }
249
- return Array.from(found);
250
- }
251
- async function discoverExamples(componentFilePath, componentsDir) {
252
- const fileBase = path.basename(componentFilePath, path.extname(componentFilePath));
253
- const kebabBase = slugify(fileBase);
254
- const candidates = [join(dirname(componentFilePath), `${fileBase}.examples`), join(dirname(componentFilePath), `${kebabBase}.examples`)];
255
- const found = [];
256
- for (const dir of candidates) try {
257
- const items = await readdir(dir, { withFileTypes: true });
258
- for (const it of items) if (it.isFile() && extname(it.name).toLowerCase() === ".astro") {
259
- const abs = join(dir, it.name);
260
- const relFromComponents = path.relative(componentsDir, abs).split(path.sep).join(posix.sep);
261
- found.push(relFromComponents);
262
- }
263
- } catch {}
264
- return found;
265
- }
266
- function createSourceFileFromFrontmatter(frontmatterCode) {
267
- return new Project({ useInMemoryFileSystem: true }).createSourceFile("Component.ts", frontmatterCode, { overwrite: true });
268
- }
269
-
270
10
  //#endregion
271
11
  //#region src/docs/generate-mdx/mdx-builder.ts
272
12
  function buildMdx(params) {
@@ -661,4 +401,4 @@ if (process.env.BEJAMAS_SKIP_AUTO_RUN !== "1" && process.env.BEJAMAS_SKIP_AUTO_R
661
401
 
662
402
  //#endregion
663
403
  export { runDocsGenerator };
664
- //# sourceMappingURL=generate-mdx-CR4bXlml.js.map
404
+ //# sourceMappingURL=generate-mdx-5r1bKyim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-mdx-5r1bKyim.js","names":["m: RegExpExecArray | null","external: string[]","internal: string[]","exampleSections: string[]","exampleRelPaths: string[]","examples: Array<{\n importName: string;\n importPath: string;\n title: string;\n source: string;\n }>","importPath","blocks: Array<{ title: string; body: string[] }>","current: { title: string; body: string[] }"],"sources":["../src/docs/generate-mdx/mdx-builder.ts","../src/docs/generate-mdx/index.ts"],"sourcesContent":["export function buildMdx(params: {\n importName: string;\n importPath: string;\n title: string;\n description: string;\n descriptionBodyMDX?: string;\n figmaUrl?: string;\n usageMDX: string;\n hasImport: boolean;\n propsList: string;\n propsTable?: Array<{\n name: string;\n type?: string;\n required?: boolean;\n defaultValue?: string | null;\n description?: string | null;\n }>;\n examples: Array<{\n importName: string;\n importPath: string;\n title: string;\n source: string;\n }>;\n examplesBlocks: Array<{ title: string; body: string }>;\n autoImports: string[];\n lucideIcons: string[];\n primaryExampleMDX: string;\n componentSource: string;\n commandName: string;\n componentsAlias: string;\n}): string {\n const {\n importName,\n importPath,\n title,\n description,\n descriptionBodyMDX,\n usageMDX,\n hasImport,\n propsList,\n propsTable,\n examples,\n examplesBlocks,\n autoImports,\n lucideIcons,\n primaryExampleMDX,\n componentSource,\n commandName,\n figmaUrl,\n componentsAlias,\n } = params;\n\n const sortedLucide = (lucideIcons ?? []).slice().sort();\n const lucideTopLine = sortedLucide.length\n ? `import { ${sortedLucide.join(\", \")} } from '@lucide/astro';`\n : null;\n const externalTopImports = [\n `import { Tabs as DocsTabs, TabItem as DocsTabItem } from '@astrojs/starlight/components';`,\n lucideTopLine,\n ]\n .filter((v) => v != null)\n .slice()\n .sort((a, b) => String(a).localeCompare(String(b)));\n const sortedUiAuto = (autoImports ?? []).slice().sort();\n const uiAutoLines = sortedUiAuto.map(\n (name) => `import ${name} from '${componentsAlias}/${name}.astro';`,\n );\n const exampleLines = (examples ?? [])\n .map((ex) => `import ${ex.importName} from '${ex.importPath}';`)\n .sort((a, b) => a.localeCompare(b));\n const internalTopImports = [\n !hasImport ? `import ${importName} from '${importPath}';` : null,\n ...uiAutoLines,\n ...exampleLines,\n ]\n .filter((v) => v != null)\n .slice()\n .sort((a, b) => String(a).localeCompare(String(b)));\n const importLines = [\n ...externalTopImports,\n externalTopImports.length && internalTopImports.length ? \"\" : null,\n ...internalTopImports,\n ].filter((v) => v !== null && v !== undefined);\n\n // Helper: build per-snippet imports so code fences are minimal and copy-pasteable\n const extractTags = (snippet: string): Set<string> => {\n const found = new Set<string>();\n const tagRegex = /<([A-Z][A-Za-z0-9_]*)\\b/g;\n let m: RegExpExecArray | null;\n while ((m = tagRegex.exec(snippet)) !== null) {\n found.add(m[1]);\n }\n return found;\n };\n\n const buildSnippetImportLines = (snippet: string): string[] => {\n if (!snippet || !snippet.length) return [];\n const used = extractTags(snippet);\n const usedIcons = sortedLucide.filter((n) => used.has(n));\n const usedUi = (autoImports ?? [])\n .filter((n) => used.has(n))\n .slice()\n .sort();\n const includeMain = !hasImport && used.has(importName);\n\n const external: string[] = [];\n if (usedIcons.length) {\n external.push(`import { ${usedIcons.join(\", \")} } from '@lucide/astro';`);\n }\n const internal: string[] = [];\n if (includeMain)\n internal.push(`import ${importName} from '${importPath}';`);\n internal.push(\n ...usedUi.map((name) => `import ${name} from '${componentsAlias}/${name}.astro';`),\n );\n\n const externalSorted = external.slice().sort((a, b) => a.localeCompare(b));\n const internalSorted = internal.slice().sort((a, b) => a.localeCompare(b));\n return [\n ...externalSorted,\n externalSorted.length && internalSorted.length ? \"\" : null,\n ...internalSorted,\n ].filter((v) => v !== null && v !== undefined) as string[];\n };\n\n const wrapTextNodes = (snippet: string): string => {\n if (!snippet) return snippet;\n return snippet.replace(/>([^<]+)</g, (match, inner) => {\n const trimmed = inner.trim();\n if (!trimmed.length) return match;\n if (/^\\{[\\s\\S]*\\}$/.test(trimmed)) return match;\n return `>{${JSON.stringify(inner)}}<`;\n });\n };\n\n const toMdxPreview = (snippet: string): string => {\n if (!snippet) return snippet;\n // Convert HTML comments to MDX comment blocks for preview sections\n const withoutComments = snippet.replace(/<!--([\\s\\S]*?)-->/g, \"{/*$1*/}\");\n return wrapTextNodes(withoutComments);\n };\n\n // Split an example body into leading markdown description (paragraphs)\n // and the Astro/HTML snippet that should be rendered in Preview/Source tabs\n const splitDescriptionAndSnippet = (\n body: string,\n ): { descriptionMD: string; snippet: string } => {\n if (!body || !body.trim().length) return { descriptionMD: \"\", snippet: \"\" };\n const lines = body.split(\"\\n\");\n let snippetStartIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n const ln = lines[i];\n // Skip empty lines before first meaningful content\n if (!ln.trim().length) continue;\n // Heuristic: consider this line the start of the snippet if it looks like Astro/HTML/JSX\n // e.g. starts with '<' or '{'\n if (/^\\s*[<{]/.test(ln)) {\n snippetStartIdx = i;\n break;\n }\n // Otherwise it's part of markdown description; keep looking until we hit markup\n }\n if (snippetStartIdx <= 0) {\n // No clear description or snippet starts at the very beginning → treat all as snippet\n return { descriptionMD: \"\", snippet: body.trim() };\n }\n const descriptionMD = lines.slice(0, snippetStartIdx).join(\"\\n\").trim();\n const snippet = lines.slice(snippetStartIdx).join(\"\\n\").trim();\n return { descriptionMD, snippet };\n };\n\n const primaryExampleSection =\n primaryExampleMDX && primaryExampleMDX.length\n ? `<DocsTabs>\n <DocsTabItem label=\"Preview\">\n <div class=\"not-content sl-bejamas-component-preview flex justify-center px-10 py-12 border border-border rounded-md min-h-[450px] items-center [&_input]:max-w-xs\">\n${toMdxPreview(primaryExampleMDX)}\n </div>\n </DocsTabItem>\n <DocsTabItem label=\"Source\">\n\n\\`\\`\\`astro\n${(() => {\n const lines = buildSnippetImportLines(primaryExampleMDX);\n return lines.length ? `---\\n${lines.join(\"\\n\")}\\n---\\n\\n` : \"\";\n})()}${primaryExampleMDX}\n\\`\\`\\`\n </DocsTabItem>\n</DocsTabs>`\n : null;\n\n const exampleSections: string[] = [];\n if (examplesBlocks && examplesBlocks.length) {\n for (const blk of examplesBlocks) {\n const { descriptionMD, snippet } = splitDescriptionAndSnippet(blk.body);\n const previewBody = toMdxPreview(snippet);\n\n // If there's no snippet, render only header + description\n if (!snippet || !snippet.length) {\n exampleSections.push(\n `### ${blk.title}\n\n${descriptionMD}`.trim(),\n );\n continue;\n }\n\n exampleSections.push(\n `### ${blk.title}\n\n${descriptionMD ? `${descriptionMD}\\n\\n` : \"\"}<DocsTabs>\n <DocsTabItem label=\"Preview\">\n <div class=\"not-content sl-bejamas-component-preview flex justify-center px-10 py-12 border border-border rounded-md min-h-[450px] items-center [&_input]:max-w-xs\">\n${previewBody}\n </div>\n </DocsTabItem>\n <DocsTabItem label=\"Source\">\n\n\\`\\`\\`astro\n${(() => {\n const lines = buildSnippetImportLines(snippet);\n return lines.length ? `---\\n${lines.join(\"\\n\")}\\n---\\n\\n` : \"\";\n})()}${snippet}\n\\`\\`\\`\n </DocsTabItem>\n</DocsTabs>`,\n );\n }\n }\n if (examples && examples.length) {\n for (const ex of examples) {\n exampleSections.push(\n `### ${ex.title}\n\n<DocsTabs>\n <DocsTabItem label=\"Preview\">\n <div class=\"not-content\">\n <${ex.importName} />\n </div>\n </DocsTabItem>\n <DocsTabItem label=\"Source\">\n\n\\`\\`\\`astro\n${ex.source}\n\\`\\`\\`\n </DocsTabItem>\n</DocsTabs>`,\n );\n }\n }\n\n const formatDefault = (val: unknown): string => {\n if (val == null) return \"\";\n let raw = String(val).trim();\n if (!raw.length) return \"\";\n // Normalize curly quotes to ASCII\n raw = raw\n .replace(/[\\u201C\\u201D\\u201E\\u201F]/g, '\"')\n .replace(/[\\u2018\\u2019]/g, \"'\");\n const isSingleQuoted = /^'[^']*'$/.test(raw);\n const isDoubleQuoted = /^\"[^\"]*\"$/.test(raw);\n const isBacktickSimple = /^`[^`]*`$/.test(raw) && raw.indexOf(\"${\") === -1;\n\n let content = raw;\n if (isSingleQuoted || isDoubleQuoted || isBacktickSimple) {\n const inner = raw.slice(1, -1);\n // Re-quote with standard double quotes\n content = `\"${inner}\"`;\n }\n // Escape table pipes\n content = content.replace(/\\|/g, \"\\\\|\");\n // Choose a backtick fence that doesn't appear in content\n const hasTick = content.includes(\"`\");\n const hasDoubleTick = content.includes(\"``\");\n const fence = !hasTick ? \"`\" : !hasDoubleTick ? \"``\" : \"```\";\n return `${fence}${content}${fence}`;\n };\n\n const installationSection = `## Installation\n<DocsTabs syncKey=\"installation\">\n <DocsTabItem label=\"CLI\">\n\n<DocsTabs syncKey=\"pkg\">\n <DocsTabItem label=\"bun\">\n\n\\`\\`\\`bash\n bunx bejamas add ${commandName}\n\\`\\`\\`\n\n </DocsTabItem>\n <DocsTabItem label=\"npm\">\n\n\\`\\`\\`bash\n npx bejamas add ${commandName}\n\\`\\`\\`\n\n </DocsTabItem>\n <DocsTabItem label=\"pnpm\">\n\n\\`\\`\\`bash\n pnpm dlx bejamas add ${commandName}\n\\`\\`\\`\n\n </DocsTabItem>\n <DocsTabItem label=\"yarn\">\n\n\\`\\`\\`bash\n yarn dlx bejamas add ${commandName}\n\\`\\`\\`\n\n </DocsTabItem>\n</DocsTabs>\n</DocsTabItem>\n<DocsTabItem label=\"Manual\">\n\n\\`\\`\\`astro\n${componentSource}\n\\`\\`\\`\n </DocsTabItem>\n</DocsTabs>`;\n\n const serializeFrontmatter = (\n label: string,\n value?: string,\n ): string | null => {\n if (!value || !value.length) return null;\n return `${label}: ${JSON.stringify(value)}`;\n };\n\n const lines = [\n \"---\",\n serializeFrontmatter(\"title\", title),\n serializeFrontmatter(\"description\", description),\n serializeFrontmatter(\"figmaUrl\", figmaUrl),\n \"---\",\n \"\",\n ...importLines,\n importLines.length ? \"\" : null,\n descriptionBodyMDX && descriptionBodyMDX.length ? descriptionBodyMDX : null,\n descriptionBodyMDX && descriptionBodyMDX.length ? \"\" : null,\n primaryExampleSection,\n primaryExampleSection ? \"\" : null,\n installationSection,\n \"\",\n usageMDX && usageMDX.length ? `## Usage\\n\\n${usageMDX}` : null,\n \"\",\n propsTable && propsTable.length\n ? `## Props\\n\\n| Prop | Type | Default |\\n|---|---|---|\\n${propsTable\n .map(\n (p) =>\n `| <code>${p.name}</code> | \\`${(p.type || \"\").replace(/\\|/g, \"\\\\|\")}\\` | ${formatDefault(p.defaultValue)} |`,\n )\n .join(\"\\n\")}`\n : propsList\n ? `## Props\\n\\n${propsList}`\n : null,\n (propsTable && propsTable.length) || propsList ? \"\" : null,\n exampleSections.length\n ? `## Examples\\n\\n` + exampleSections.join(\"\\n\\n\")\n : null,\n ].filter((v) => v !== null && v !== undefined);\n\n return lines.join(\"\\n\").trim() + \"\\n\";\n}\n","import { mkdirSync, existsSync } from \"fs\";\nimport { readdir, writeFile } from \"fs/promises\";\nimport { join, extname, dirname, relative } from \"path\";\nimport {\n RESERVED_COMPONENTS,\n slugify,\n extractFrontmatter,\n toIdentifier,\n parseJsDocMetadata,\n extractPropsFromAstroProps,\n extractPropsFromDeclaredProps,\n resolveUiRoot,\n resolveOutDir,\n normalizeUsageMDX,\n normalizeBlockMDX,\n detectHasImportTopLevel,\n discoverExamples,\n extractComponentTagsFromMDX,\n createSourceFileFromFrontmatter,\n} from \"./utils\";\nimport { buildMdx } from \"./mdx-builder\";\nimport { logger } from \"@/src/utils/logger\";\nimport { spinner } from \"@/src/utils/spinner\";\nimport { getConfig } from \"@/src/utils/get-config\";\n\nasync function main() {\n const DEBUG =\n process.env.BEJAMAS_DEBUG === \"1\" || process.env.BEJAMAS_DEBUG === \"true\";\n const cwd =\n process.env.BEJAMAS_DOCS_CWD && process.env.BEJAMAS_DOCS_CWD.length\n ? (process.env.BEJAMAS_DOCS_CWD as string)\n : process.cwd();\n const config = await getConfig(cwd);\n const componentsAlias = (\n config?.aliases?.ui ||\n config?.aliases?.components ||\n \"@bejamas/ui/components\"\n ).replace(/\\/$/, \"\");\n const componentsDirFromConfig =\n config?.resolvedPaths?.ui || config?.resolvedPaths?.components;\n let uiRoot = resolveUiRoot(cwd);\n let componentsDir = join(uiRoot, \"src\", \"components\");\n if (componentsDirFromConfig) {\n componentsDir = componentsDirFromConfig;\n uiRoot = dirname(dirname(componentsDirFromConfig));\n }\n if (!existsSync(componentsDir)) {\n // Fallback to ui package components if the configured path is missing.\n componentsDir = join(uiRoot, \"src\", \"components\");\n }\n const outDir = resolveOutDir(cwd);\n mkdirSync(outDir, { recursive: true });\n\n if (DEBUG) {\n logger.info(`[docs-generator] cwd: ${cwd}`);\n logger.info(`[docs-generator] uiRoot: ${uiRoot}`);\n logger.info(`[docs-generator] componentsDir: ${componentsDir}`);\n logger.info(`[docs-generator] outDir: ${outDir}`);\n }\n\n const entriesDir = await readdir(componentsDir, { withFileTypes: true });\n const files = entriesDir.filter(\n (e) => e.isFile() && extname(e.name).toLowerCase() === \".astro\",\n );\n if (DEBUG) {\n logger.info(`[docs-generator] components found: ${files.length}`);\n if (files.length)\n logger.info(\n `[docs-generator] first few: ${files\n .slice(0, 5)\n .map((f) => f.name)\n .join(\", \")}`,\n );\n }\n\n let generatedCount = 0;\n const total = files.length;\n const spin = spinner(`Generating docs (0/${total})`).start();\n for (const f of files) {\n const filePath = join(componentsDir, f.name);\n const astroFile = await (\n await import(\"fs/promises\")\n ).readFile(filePath, \"utf-8\");\n const frontmatterCode = extractFrontmatter(astroFile);\n const sourceFile = createSourceFileFromFrontmatter(frontmatterCode);\n const meta = parseJsDocMetadata(frontmatterCode);\n const declaredProps = extractPropsFromDeclaredProps(sourceFile);\n const destructuredProps = extractPropsFromAstroProps(sourceFile);\n\n // Build props table preferring declared types; merge defaults from destructuring\n const defaultsMap = new Map<string, string | null>();\n for (const p of destructuredProps) {\n if (p.name && p.hasDefault) {\n defaultsMap.set(p.name, p.defaultValue || null);\n }\n }\n\n const propsTable = (declaredProps.length ? declaredProps : []).map((p) => ({\n name: p.name,\n type: p.type,\n required: !p.optional,\n defaultValue: defaultsMap.has(p.name) ? defaultsMap.get(p.name)! : null,\n }));\n\n const slug = `${slugify(f.name)}`;\n const pascal = f.name.replace(/\\.(astro)$/i, \"\");\n const title = meta.title || meta.name || pascal;\n const description = meta.description || \"\";\n const descriptionBodyMDX = (meta as any).descriptionBodyMDX || \"\";\n const figmaUrl = (meta as any).figmaUrl || \"\";\n // Do not display props if there is no declared Props\n const propsList = \"\";\n\n const importName = pascal;\n const importPath = `${componentsAlias}/${pascal}.astro`;\n const { text: usageMDX, hasImport: hasImportUsage } = normalizeUsageMDX(\n meta.usageMDX || \"\",\n pascal,\n );\n const primaryExampleMDX = normalizeBlockMDX(\n meta.primaryExampleMDX || \"\",\n ).trim();\n const examplesMDX = normalizeBlockMDX(meta.examplesMDX || \"\").trim();\n const hasImportExamples = detectHasImportTopLevel(examplesMDX, pascal);\n const hasImportPrimary = detectHasImportTopLevel(primaryExampleMDX, pascal);\n const hasImport = hasImportUsage || hasImportExamples || hasImportPrimary;\n\n let exampleRelPaths: string[] = [];\n let examples: Array<{\n importName: string;\n importPath: string;\n title: string;\n source: string;\n }> = [];\n const examplesBlocksRaw = examplesMDX;\n const examplesBlocks = parseExamplesBlocks(examplesBlocksRaw);\n if (examplesBlocks.length === 0) {\n exampleRelPaths = await discoverExamples(filePath, componentsDir);\n examples = (exampleRelPaths || []).map((rel) => {\n const posixRel = rel\n .split(require(\"path\").sep)\n .join(require(\"path\").posix.sep);\n const importPath = `${componentsAlias}/${posixRel}`;\n const abs = join(componentsDir, rel);\n const source = require(\"fs\").readFileSync(abs, \"utf-8\");\n const base = toIdentifier(\n require(\"path\").basename(rel, require(\"path\").extname(rel)),\n );\n const importName = `${pascal}${base}`;\n const title = base;\n return { importName, importPath, title, source };\n });\n }\n\n const usedInUsage = extractComponentTagsFromMDX(usageMDX).filter(\n (n) => n !== pascal,\n );\n const usedInExamples = extractComponentTagsFromMDX(examplesMDX).filter(\n (n) => n !== pascal,\n );\n const usedInPrimary = extractComponentTagsFromMDX(primaryExampleMDX).filter(\n (n) => n !== pascal,\n );\n const autoSet = new Set<string>([\n ...usedInUsage,\n ...usedInExamples,\n ...usedInPrimary,\n ]);\n const autoImports = Array.from(autoSet)\n .filter((name) => !RESERVED_COMPONENTS.has(name))\n .filter((name) => true);\n\n const lucideIcons = autoImports.filter((n) => /Icon$/.test(n));\n const uiAutoImports = autoImports.filter((n) => !/Icon$/.test(n));\n\n const mdx = buildMdx({\n importName,\n importPath,\n title,\n description,\n usageMDX,\n hasImport,\n propsList,\n propsTable,\n examples,\n examplesBlocks: parseExamplesBlocks(examplesBlocksRaw),\n autoImports: uiAutoImports,\n lucideIcons,\n primaryExampleMDX,\n componentSource: astroFile.trim(),\n commandName: slug,\n figmaUrl,\n descriptionBodyMDX,\n componentsAlias,\n });\n const outFile = join(outDir, `${slug}.mdx`);\n mkdirSync(dirname(outFile), { recursive: true });\n await writeFile(outFile, mdx, \"utf-8\");\n generatedCount += 1;\n spin.text = `Generating docs (${generatedCount}/${total}) - ${title}`;\n if (DEBUG) logger.info(`Generated ${outFile}`);\n }\n spin.succeed(\n `Created ${generatedCount} file${generatedCount === 1 ? \"\" : \"s\"}:`,\n );\n // log all files with relative paths, sorted alphabetically\n const relPaths = files\n .map((f) => {\n const slug = `${slugify(f.name)}`;\n const outFile = join(outDir, `${slug}.mdx`);\n return relative(cwd, outFile);\n })\n .sort((a, b) => a.localeCompare(b));\n relPaths.forEach((p) => {\n logger.log(` - ${p}`);\n });\n logger.break();\n}\n\nexport function parseExamplesBlocks(\n examplesMDX: string,\n): Array<{ title: string; body: string }> {\n if (!examplesMDX) return [];\n const lines = examplesMDX.split(\"\\n\");\n const blocks: Array<{ title: string; body: string[] }> = [];\n let current: { title: string; body: string[] } = { title: \"\", body: [] };\n for (const line of lines) {\n const heading = line.match(/^###\\s+(.+)$/);\n if (heading) {\n if (current.title || current.body.length) blocks.push(current);\n current = { title: heading[1].trim(), body: [] };\n continue;\n }\n current.body.push(line);\n }\n if (current.title || current.body.length) blocks.push(current);\n return blocks.map((b, idx) => ({\n title: b.title || `Example ${idx + 1}`,\n body: b.body.join(\"\\n\").trim(),\n }));\n}\n\nexport async function runDocsGenerator(): Promise<void> {\n await main();\n}\n\nif (\n process.env.BEJAMAS_SKIP_AUTO_RUN !== \"1\" &&\n process.env.BEJAMAS_SKIP_AUTO_RUN !== \"true\"\n) {\n runDocsGenerator().catch((err) => {\n logger.error(String(err));\n process.exit(1);\n });\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { BASE_COLORS, getConfig, getProjectInfo, highlighter, logger, spinner } from "./get-config-CJUwQ2Xb.js";
2
+ import { BASE_COLORS, extractFrontmatter, getConfig, getProjectInfo, highlighter, logger, parseJsDocMetadata, resolveUiRoot, spinner } from "./utils-DfvCox_O.js";
3
3
  import { Command } from "commander";
4
4
  import { createRequire } from "module";
5
5
  import path from "path";
@@ -8,12 +8,12 @@ import os from "os";
8
8
  import dotenv from "dotenv";
9
9
  import { detect } from "@antfu/ni";
10
10
  import { z } from "zod";
11
+ import { bold, cyan, dim, green, magenta, red, white, yellow } from "kleur/colors";
11
12
  import { execa } from "execa";
12
13
  import prompts from "prompts";
13
14
  import fg from "fast-glob";
14
- import path$1, { dirname as dirname$1, isAbsolute, relative as relative$1, resolve } from "node:path";
15
- import { existsSync, readFileSync } from "node:fs";
16
- import { fileURLToPath } from "node:url";
15
+ import path$1, { extname as extname$1, isAbsolute, join as join$1, relative as relative$1, resolve } from "node:path";
16
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
17
17
  import fs from "node:fs/promises";
18
18
 
19
19
  //#region src/utils/errors.ts
@@ -375,9 +375,10 @@ async function runInit(options) {
375
375
  }
376
376
 
377
377
  //#endregion
378
- //#region src/commands/docs.ts
379
- const __filename = fileURLToPath(import.meta.url);
380
- dirname$1(__filename);
378
+ //#region src/utils/tsconfig-utils.ts
379
+ /**
380
+ * Read and parse tsconfig.json from the project root
381
+ */
381
382
  function readTsConfig(projectRoot) {
382
383
  try {
383
384
  const tsconfigPath = resolve(projectRoot, "tsconfig.json");
@@ -388,6 +389,9 @@ function readTsConfig(projectRoot) {
388
389
  return null;
389
390
  }
390
391
  }
392
+ /**
393
+ * Resolve an alias path (like @/components) using tsconfig.json paths
394
+ */
391
395
  function resolveAliasPathUsingTsConfig(inputPath, projectRoot) {
392
396
  const cfg = readTsConfig(projectRoot);
393
397
  if (!cfg || !cfg.compilerOptions) return null;
@@ -406,6 +410,9 @@ function resolveAliasPathUsingTsConfig(inputPath, projectRoot) {
406
410
  }
407
411
  return null;
408
412
  }
413
+
414
+ //#endregion
415
+ //#region src/commands/docs.ts
409
416
  async function generateDocs({ cwd, outDir, verbose }) {
410
417
  const DEBUG = process.env.BEJAMAS_DEBUG === "1" || process.env.BEJAMAS_DEBUG === "true" || verbose;
411
418
  try {
@@ -520,7 +527,7 @@ async function generateDocs({ cwd, outDir, verbose }) {
520
527
  if (process.env.BEJAMAS_DOCS_CWD) logger.info(`Docs CWD: ${process.env.BEJAMAS_DOCS_CWD}`);
521
528
  if (process.env.BEJAMAS_DOCS_OUT_DIR) logger.info(`Docs out: ${process.env.BEJAMAS_DOCS_OUT_DIR}`);
522
529
  }
523
- const mod = await import("./generate-mdx-CR4bXlml.js");
530
+ const mod = await import("./generate-mdx-5r1bKyim.js");
524
531
  if (typeof mod.runDocsGenerator === "function") await mod.runDocsGenerator();
525
532
  else throw new Error("Failed to load docs generator. Export 'runDocsGenerator' not found.");
526
533
  } catch (err) {
@@ -536,6 +543,178 @@ const docs = new Command().name("docs:build").alias("docs").description("generat
536
543
  });
537
544
  });
538
545
 
546
+ //#endregion
547
+ //#region src/commands/docs-check.ts
548
+ const REQUIRED_FIELDS = [
549
+ "name",
550
+ "title",
551
+ "description"
552
+ ];
553
+ const RECOMMENDED_FIELDS = [
554
+ "primaryExampleMDX",
555
+ "usageMDX",
556
+ "figmaUrl"
557
+ ];
558
+ const FIELD_LABELS = {
559
+ name: "@component",
560
+ title: "@title",
561
+ description: "@description",
562
+ primaryExampleMDX: "@preview",
563
+ usageMDX: "@usage",
564
+ figmaUrl: "@figmaUrl"
565
+ };
566
+ function checkComponentDocs(filePath, fileName) {
567
+ const content = readFileSync(filePath, "utf-8");
568
+ const frontmatter = extractFrontmatter(content);
569
+ const meta = parseJsDocMetadata(frontmatter);
570
+ const componentName = fileName.replace(/\.astro$/i, "");
571
+ const missingRequired = [];
572
+ const missingRecommended = [];
573
+ for (const field of REQUIRED_FIELDS) {
574
+ const value = meta[field];
575
+ if (!value || typeof value === "string" && !value.trim()) missingRequired.push(FIELD_LABELS[field] || field);
576
+ }
577
+ for (const field of RECOMMENDED_FIELDS) {
578
+ const value = meta[field];
579
+ if (!value || typeof value === "string" && !value.trim()) missingRecommended.push(FIELD_LABELS[field] || field);
580
+ }
581
+ let status;
582
+ if (missingRequired.length > 0) status = "missing";
583
+ else if (missingRecommended.length > 0) status = "incomplete";
584
+ else status = "complete";
585
+ return {
586
+ name: componentName,
587
+ file: fileName,
588
+ status,
589
+ missingRequired,
590
+ missingRecommended
591
+ };
592
+ }
593
+ async function checkDocs({ cwd, json }) {
594
+ try {
595
+ const shellCwd = process.cwd();
596
+ let projectRoot = shellCwd;
597
+ let probe = shellCwd;
598
+ for (let i = 0; i < 6 && probe; i += 1) {
599
+ const candidate = resolve(probe, "components.json");
600
+ if (existsSync(candidate)) {
601
+ projectRoot = probe;
602
+ try {
603
+ const raw = readFileSync(candidate, "utf-8");
604
+ const config = JSON.parse(raw);
605
+ if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.aliases?.ui) {
606
+ const mapped = String(config.aliases.ui);
607
+ let uiAbs = null;
608
+ if (mapped.startsWith("./") || mapped.startsWith("../") || isAbsolute(mapped)) uiAbs = resolve(projectRoot, mapped);
609
+ else uiAbs = resolveAliasPathUsingTsConfig(mapped, projectRoot);
610
+ if (!uiAbs && mapped.startsWith("@/")) uiAbs = resolve(projectRoot, "src", mapped.slice(2));
611
+ if (uiAbs) process.env.BEJAMAS_UI_ROOT = uiAbs;
612
+ }
613
+ if (!cwd && !process.env.BEJAMAS_UI_ROOT && config?.tailwind?.css) {
614
+ const cssRaw = String(config.tailwind.css);
615
+ let cssAbs = null;
616
+ if (cssRaw.startsWith("./") || cssRaw.startsWith("../") || isAbsolute(cssRaw)) cssAbs = resolve(projectRoot, cssRaw);
617
+ else cssAbs = resolveAliasPathUsingTsConfig(cssRaw, projectRoot);
618
+ if (!cssAbs && cssRaw.startsWith("@/")) cssAbs = resolve(projectRoot, "src", cssRaw.slice(2));
619
+ if (cssAbs) {
620
+ const uiRootFromCss = resolve(cssAbs, "..", "..", "..");
621
+ process.env.BEJAMAS_UI_ROOT = uiRootFromCss;
622
+ }
623
+ }
624
+ } catch {}
625
+ break;
626
+ }
627
+ const parent = resolve(probe, "..");
628
+ probe = parent === probe ? null : parent;
629
+ }
630
+ if (cwd && cwd.length) process.env.BEJAMAS_UI_ROOT = resolve(cwd);
631
+ let uiRoot;
632
+ try {
633
+ uiRoot = resolveUiRoot(shellCwd);
634
+ } catch {
635
+ logger.error("Unable to locate @bejamas/ui. Use --cwd to specify the UI package path.");
636
+ process.exit(1);
637
+ }
638
+ const componentsDir = join$1(uiRoot, "src", "components");
639
+ if (!existsSync(componentsDir)) {
640
+ logger.error(`Components directory not found: ${componentsDir}\n\nExpected structure: <uiRoot>/src/components/*.astro\nUse --cwd to specify a different UI package root.`);
641
+ process.exit(1);
642
+ }
643
+ const files = readdirSync(componentsDir, { withFileTypes: true }).filter((e) => e.isFile() && extname$1(e.name).toLowerCase() === ".astro").map((e) => e.name).sort();
644
+ if (files.length === 0) {
645
+ logger.warn("No .astro component files found.");
646
+ process.exit(0);
647
+ }
648
+ const results = [];
649
+ for (const file of files) {
650
+ const filePath = join$1(componentsDir, file);
651
+ const status = checkComponentDocs(filePath, file);
652
+ results.push(status);
653
+ }
654
+ const complete = results.filter((r) => r.status === "complete");
655
+ const incomplete = results.filter((r) => r.status === "incomplete");
656
+ const missing = results.filter((r) => r.status === "missing");
657
+ const checkResult = {
658
+ total: results.length,
659
+ complete,
660
+ incomplete,
661
+ missing
662
+ };
663
+ if (json) {
664
+ console.log(JSON.stringify(checkResult, null, 2));
665
+ if (missing.length > 0) process.exit(1);
666
+ return;
667
+ }
668
+ const termWidth = Math.min(80, process.stdout.columns || 80);
669
+ const headerLine = "━".repeat(termWidth);
670
+ logger.break();
671
+ console.log(dim("┌" + "─".repeat(termWidth - 2) + "┐"));
672
+ console.log(dim("│") + bold(cyan(" docs:check")) + dim(" — Component Documentation Status") + " ".repeat(Math.max(0, termWidth - 47)) + dim("│"));
673
+ console.log(dim("└" + "─".repeat(termWidth - 2) + "┘"));
674
+ logger.break();
675
+ const formatTag = (tag) => magenta(tag);
676
+ const formatComponentName = (name) => bold(white(name));
677
+ if (complete.length > 0) {
678
+ console.log(green(`✓ Complete (${complete.length} component${complete.length === 1 ? "" : "s"}):`));
679
+ const names = complete.map((c) => formatComponentName(c.name)).join(dim(", "));
680
+ console.log(` ${names}`);
681
+ logger.break();
682
+ }
683
+ if (incomplete.length > 0) {
684
+ console.log(yellow(`⚠ Incomplete (${incomplete.length} component${incomplete.length === 1 ? "" : "s"}):`));
685
+ for (const comp of incomplete) {
686
+ const missingFields = comp.missingRecommended.map(formatTag).join(dim(", "));
687
+ console.log(` ${formatComponentName(comp.name)} ${dim("-")} ${dim("missing:")} ${missingFields}`);
688
+ }
689
+ logger.break();
690
+ }
691
+ if (missing.length > 0) {
692
+ console.log(red(`✗ Missing Docs (${missing.length} component${missing.length === 1 ? "" : "s"}):`));
693
+ for (const comp of missing) {
694
+ const missingFields = comp.missingRequired.map(formatTag).join(dim(", "));
695
+ console.log(` ${formatComponentName(comp.name)} ${dim("-")} ${dim("missing:")} ${missingFields}`);
696
+ }
697
+ logger.break();
698
+ }
699
+ console.log(dim(headerLine));
700
+ const completeText = green(`${complete.length}/${results.length} complete`);
701
+ const incompleteText = incomplete.length > 0 ? yellow(`${incomplete.length} incomplete`) : dim(`${incomplete.length} incomplete`);
702
+ const missingText = missing.length > 0 ? red(`${missing.length} missing docs`) : dim(`${missing.length} missing docs`);
703
+ console.log(`${bold("Summary:")} ${completeText} ${dim("|")} ${incompleteText} ${dim("|")} ${missingText}`);
704
+ logger.break();
705
+ if (missing.length > 0) process.exit(1);
706
+ } catch (err) {
707
+ logger.error(err?.message || String(err));
708
+ process.exit(1);
709
+ }
710
+ }
711
+ const docsCheck = new Command().name("docs:check").description("check documentation status for all components").option("-c, --cwd <cwd>", "path to UI working directory").option("--json", "output results as JSON").action(async (opts) => {
712
+ await checkDocs({
713
+ cwd: opts.cwd,
714
+ json: Boolean(opts.json)
715
+ });
716
+ });
717
+
539
718
  //#endregion
540
719
  //#region src/utils/astro-imports.ts
541
720
  function updateImportAliases(moduleSpecifier, config, isRemote = false) {
@@ -708,6 +887,7 @@ const program = new Command().name("bejamas").description("bejamas/ui cli").conf
708
887
  program.addCommand(init);
709
888
  program.addCommand(add);
710
889
  program.addCommand(docs);
890
+ program.addCommand(docsCheck);
711
891
  program.parse(process.argv);
712
892
  var src_default = program;
713
893