docsui-cli 0.0.59
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 +279 -0
- package/dist/commands/add.d.ts +5 -0
- package/dist/commands/add.js +254 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.js +250 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +548 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.js +84 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.js +1562 -0
- package/dist/commands/new.d.ts +5 -0
- package/dist/commands/new.js +113 -0
- package/dist/commands/remove.d.ts +5 -0
- package/dist/commands/remove.js +134 -0
- package/dist/commands/save.d.ts +5 -0
- package/dist/commands/save.js +60 -0
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.js +247 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +88 -0
- package/dist/latex-to-primitives.d.ts +59 -0
- package/dist/latex-to-primitives.js +1019 -0
- package/dist/lib/component-registry.d.ts +33 -0
- package/dist/lib/component-registry.js +843 -0
- package/dist/lib/css-tokens.d.ts +8 -0
- package/dist/lib/css-tokens.js +294 -0
- package/dist/metadata.d.ts +1 -0
- package/dist/metadata.js +4 -0
- package/dist/symbol-map.d.ts +30 -0
- package/dist/symbol-map.js +1607 -0
- package/dist/utils/detect-structure.d.ts +16 -0
- package/dist/utils/detect-structure.js +58 -0
- package/dist/utils/fetch-component.d.ts +13 -0
- package/dist/utils/fetch-component.js +81 -0
- package/dist/utils/get-config.d.ts +14 -0
- package/dist/utils/get-config.js +19 -0
- package/dist/utils/install-deps.d.ts +3 -0
- package/dist/utils/install-deps.js +23 -0
- package/dist/utils/save-mdx-page.d.ts +35 -0
- package/dist/utils/save-mdx-page.js +44 -0
- package/dist/utils/scan-mdx.d.ts +20 -0
- package/dist/utils/scan-mdx.js +106 -0
- package/dist/utils/telemetry.d.ts +3 -0
- package/dist/utils/telemetry.js +42 -0
- package/dist/utils/write-component.d.ts +7 -0
- package/dist/utils/write-component.js +25 -0
- package/dist/webview-bundle.js +3478 -0
- package/package.json +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# docsui
|
|
2
|
+
|
|
3
|
+
Copy-paste MDX components for documentation sites. 52+ components including 150+ math primitives, data structures, tables, diagrams, and more — built with React, TypeScript, and Tailwind CSS. CLI, MCP server, and remark plugin included.
|
|
4
|
+
|
|
5
|
+
Follows the [shadcn/ui](https://ui.shadcn.com) philosophy — components are added directly to your project, not installed as a dependency. You own the code.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Initialize your project
|
|
11
|
+
npx docsui-cli@latest init
|
|
12
|
+
|
|
13
|
+
# Add components
|
|
14
|
+
npx docsui-cli@latest add callout tabs code-block
|
|
15
|
+
|
|
16
|
+
# Or use the interactive menu
|
|
17
|
+
npx docsui-cli@latest
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Commands
|
|
21
|
+
|
|
22
|
+
### `init`
|
|
23
|
+
|
|
24
|
+
Sets up mdx-ui in your project:
|
|
25
|
+
|
|
26
|
+
- Creates `docsui.json` config
|
|
27
|
+
- Injects shadcn-compatible CSS variables into your `globals.css`
|
|
28
|
+
- Patches `tailwind.config` with color tokens (Tailwind v3) or adds `@theme inline` block (Tailwind v4)
|
|
29
|
+
- Injects Shiki dual-theme CSS for syntax highlighting
|
|
30
|
+
- Works with Next.js, Vite React, Astro, and any other framework
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx docsui-cli@latest init
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `add`
|
|
37
|
+
|
|
38
|
+
Adds one or more components to your project:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Add specific components
|
|
42
|
+
npx docsui-cli@latest add callout
|
|
43
|
+
|
|
44
|
+
# Add multiple at once
|
|
45
|
+
npx docsui-cli@latest add tabs steps accordion
|
|
46
|
+
|
|
47
|
+
# Interactive picker (no args)
|
|
48
|
+
npx docsui-cli@latest add
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Each component is written to your `componentsDir`, its npm dependencies are installed, and your `mdx-components.tsx` is patched with the correct static imports.
|
|
52
|
+
|
|
53
|
+
### `update`
|
|
54
|
+
|
|
55
|
+
Re-fetches and overwrites installed components with the latest versions:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Update all installed components
|
|
59
|
+
npx docsui-cli@latest update
|
|
60
|
+
|
|
61
|
+
# Update specific components
|
|
62
|
+
npx docsui-cli@latest update callout tabs
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `list`
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Show all available components
|
|
69
|
+
npx docsui-cli@latest list
|
|
70
|
+
|
|
71
|
+
# Show only installed components
|
|
72
|
+
npx docsui-cli@latest list --installed
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Available Components (52)
|
|
76
|
+
|
|
77
|
+
### Content & Layout
|
|
78
|
+
|
|
79
|
+
| Component | Description |
|
|
80
|
+
| ----------------- | ------------------------------------------------------------------- |
|
|
81
|
+
| `alert` | Semantic `role="alert"` boxes — info, warning, destructive, success |
|
|
82
|
+
| `accordion` | Collapsible sections — single or multiple open |
|
|
83
|
+
| `annotation` | Inline text annotation — click to reveal explanation popover |
|
|
84
|
+
| `badge` | Status labels with 7 variants |
|
|
85
|
+
| `blockquote` | Styled blockquote |
|
|
86
|
+
| `callout` | Alert boxes — info, warning, danger, success |
|
|
87
|
+
| `card` | Card + header/title/description/content/footer + LinkCard |
|
|
88
|
+
| `changelog` | Versioned entries with timeline and typed change badges |
|
|
89
|
+
| `definition` | Formal term definition in structured format |
|
|
90
|
+
| `emphasis` | Bold (`<strong>`) and italic (`<em>`) |
|
|
91
|
+
| `glossary` | Context-based inline glossary with popovers via `<Term>` |
|
|
92
|
+
| `heading` | H1–H6 with auto-generated anchor links |
|
|
93
|
+
| `headings` | Markdown h1–h6 overrides with anchor links |
|
|
94
|
+
| `highlight` | Marker-style inline highlight — yellow, blue, green, pink, purple |
|
|
95
|
+
| `horizontal-rule` | Divider — default, dashed, dotted, gradient |
|
|
96
|
+
| `illustration` | Single diagram or responsive 1–3 column image grid |
|
|
97
|
+
| `image` | Image with optional caption |
|
|
98
|
+
| `inline-code` | Inline code snippets |
|
|
99
|
+
| `invariant` | Formal invariant statement with optional complexity annotation |
|
|
100
|
+
| `kbd` | Keyboard shortcut display (`Ctrl+K`, `⌘+S`) |
|
|
101
|
+
| `link` | Styled anchor — auto-detects external URLs, adds open-in-new-tab |
|
|
102
|
+
| `list` | Ordered and unordered lists |
|
|
103
|
+
| `paragraph` | Paragraph with Lead, Intro, Large, Small, Muted variants |
|
|
104
|
+
| `preview` | Tabbed component preview — rendered output + source with copy |
|
|
105
|
+
| `reveal` | Animated click-to-reveal panel for answers, hints, and spoilers |
|
|
106
|
+
| `security-note` | Security callout with severity levels — info, warning, critical |
|
|
107
|
+
| `spoiler` | Collapsible `<details>`/`<summary>` disclosure |
|
|
108
|
+
| `steps` | Numbered step-by-step guide |
|
|
109
|
+
| `tabs` | Tabbed content with keyboard navigation |
|
|
110
|
+
|
|
111
|
+
### Code & Terminal
|
|
112
|
+
|
|
113
|
+
| Component | Description |
|
|
114
|
+
| ------------ | --------------------------------------------------------------- |
|
|
115
|
+
| `code-block` | Syntax-highlighted code blocks via rehype-pretty-code |
|
|
116
|
+
| `code-group` | Tabbed code blocks — npm/pnpm/yarn or multi-language examples |
|
|
117
|
+
| `diff-block` | Code diff block — highlights `+` additions and `-` removals |
|
|
118
|
+
| `terminal` | macOS-style terminal window with traffic lights and typed lines |
|
|
119
|
+
|
|
120
|
+
### Math
|
|
121
|
+
|
|
122
|
+
| Component | Description |
|
|
123
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
124
|
+
| `math` | LaTeX math via KaTeX — block and inline |
|
|
125
|
+
| `math-primitives` | 150+ JSX math primitives — Frac, Pow, Integral, Sum, Greek, trig, logic and more. No LaTeX engine required. |
|
|
126
|
+
| `math-equation` | Display equation containers — centered block with optional equation number |
|
|
127
|
+
| `math-solution` | Step-by-step solution blocks — SolutionStep, SolutionAnswer, SolutionNote |
|
|
128
|
+
|
|
129
|
+
### Data & Tables
|
|
130
|
+
|
|
131
|
+
| Component | Description |
|
|
132
|
+
| ------------------ | --------------------------------------------------------------------- |
|
|
133
|
+
| `complexity-table` | Time and space complexity table for algorithm operations |
|
|
134
|
+
| `data-table` | Dynamic table — accepts columns and data as props |
|
|
135
|
+
| `data-type-table` | AI/ML numeric data type and tensor specification table |
|
|
136
|
+
| `hardware-spec` | Hardware interface spec card — type, version, speed, voltage, pins |
|
|
137
|
+
| `pin-table` | Hardware pinout table — signal name, direction, voltage, alt function |
|
|
138
|
+
| `privacy-table` | Personal data documentation — purpose, legal basis, retention |
|
|
139
|
+
| `register-map` | Hardware register/OTP fuse map — address, bits, access type, reset |
|
|
140
|
+
| `table` | Table with header, body, footer, caption |
|
|
141
|
+
|
|
142
|
+
### Diagrams & Visualization
|
|
143
|
+
|
|
144
|
+
| Component | Description |
|
|
145
|
+
| --------- | ---------------------------------------------------------------------------------- |
|
|
146
|
+
| `ds` | Data structure visualizations — Array, LinkedList, Stack, Queue, Tree, Graph, etc. |
|
|
147
|
+
| `ds-tree` | SVG tree visualizer — BST, AVL, Heap, N-ary with traversal animations |
|
|
148
|
+
| `mermaid` | Mermaid diagrams — flowcharts, sequences, etc. |
|
|
149
|
+
|
|
150
|
+
### SEO & Metadata
|
|
151
|
+
|
|
152
|
+
| Component | Description |
|
|
153
|
+
| --------- | ---------------------------------------------------------- |
|
|
154
|
+
| `json-ld` | JSON-LD structured data — Article, BreadcrumbList, FAQPage |
|
|
155
|
+
|
|
156
|
+
### Navigation
|
|
157
|
+
|
|
158
|
+
| Component | Description |
|
|
159
|
+
| ----------- | ------------------------------------ |
|
|
160
|
+
| `file-tree` | Simple string-based file/folder tree |
|
|
161
|
+
| `tree` | Interactive file/folder tree |
|
|
162
|
+
|
|
163
|
+
### Certification & Compliance
|
|
164
|
+
|
|
165
|
+
| Component | Description |
|
|
166
|
+
| --------------------- | ------------------------------------------------------------ |
|
|
167
|
+
| `certification-badge` | ISO, TISAX, SOC 2 certification chips with status indicators |
|
|
168
|
+
|
|
169
|
+
### Media
|
|
170
|
+
|
|
171
|
+
| Component | Description |
|
|
172
|
+
| --------- | ----------------------------------------------------------------- |
|
|
173
|
+
| `video` | Video embed — auto-detects YouTube, Vimeo, and HTML5 with caption |
|
|
174
|
+
|
|
175
|
+
### Utilities
|
|
176
|
+
|
|
177
|
+
| Component | Description |
|
|
178
|
+
| ---------------- | -------------------------------------- |
|
|
179
|
+
| `mdx-components` | Auto-wired MDX component mapper |
|
|
180
|
+
| `utils` | `cn()` utility (clsx + tailwind-merge) |
|
|
181
|
+
|
|
182
|
+
## Math Primitives
|
|
183
|
+
|
|
184
|
+
`math-primitives` is the single math system in mdx-ui — 150+ JSX components with no LaTeX engine required:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx docsui-cli@latest add math-primitives
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```mdx
|
|
191
|
+
{/* Fraction */}
|
|
192
|
+
|
|
193
|
+
<Frac num="a" den="b" />
|
|
194
|
+
|
|
195
|
+
{/* Integral with bounds */}
|
|
196
|
+
|
|
197
|
+
<Integral from="a" to="b">
|
|
198
|
+
f(x) dx
|
|
199
|
+
</Integral>
|
|
200
|
+
|
|
201
|
+
{/* Compose multiple elements */}
|
|
202
|
+
|
|
203
|
+
<Frac
|
|
204
|
+
num={
|
|
205
|
+
<Expr>
|
|
206
|
+
x <Pow exp="2">dx</Pow>
|
|
207
|
+
</Expr>
|
|
208
|
+
}
|
|
209
|
+
den={<Pow exp="2">dy</Pow>}
|
|
210
|
+
/>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Key components: `Expr` (composition), `Frac`, `Pow`, `Sub`, `Sqrt`, `Integral`, `Sum`, `Prod`, `Lim`, `Deriv`, `PDeriv`, `Brace`, `Paren`, full Greek alphabet, trig functions, logic symbols, and more.
|
|
214
|
+
|
|
215
|
+
## Syntax Highlighting
|
|
216
|
+
|
|
217
|
+
`code-block` integrates with `rehype-pretty-code` and Shiki for accurate, dual-theme syntax highlighting.
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
npm install rehype-pretty-code shiki
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
// next.config.ts
|
|
225
|
+
import rehypePrettyCode from "rehype-pretty-code";
|
|
226
|
+
|
|
227
|
+
const withMDX = createMDX({
|
|
228
|
+
options: {
|
|
229
|
+
rehypePlugins: [
|
|
230
|
+
[
|
|
231
|
+
rehypePrettyCode,
|
|
232
|
+
{
|
|
233
|
+
theme: { light: "github-light-default", dark: "github-dark-default" },
|
|
234
|
+
defaultColor: false,
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Map `pre` to `CodeBlock` in your MDX components:
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
import { CodeBlock } from "@/components/mdx/code-block";
|
|
246
|
+
const components = { pre: CodeBlock };
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The required CSS is automatically injected by `npx docsui-cli@latest init`.
|
|
250
|
+
|
|
251
|
+
## Framework Support
|
|
252
|
+
|
|
253
|
+
| Framework | Status |
|
|
254
|
+
| ---------------------- | ------ |
|
|
255
|
+
| Next.js (App Router) | ✓ |
|
|
256
|
+
| Next.js (Pages Router) | ✓ |
|
|
257
|
+
| Vite React | ✓ |
|
|
258
|
+
| Astro | ✓ |
|
|
259
|
+
| Tailwind v3 | ✓ |
|
|
260
|
+
| Tailwind v4 | ✓ |
|
|
261
|
+
|
|
262
|
+
## Example `docsui.json`
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"componentsDir": "src/components/mdx",
|
|
267
|
+
"framework": "nextjs"
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Links
|
|
272
|
+
|
|
273
|
+
- **Documentation**: [GitHub](https://github.com/suryaravikumar-space/mdx-ui)
|
|
274
|
+
- **Issues**: [GitHub Issues](https://github.com/suryaravikumar-space/mdx-ui/issues)
|
|
275
|
+
- **npm**: [docsui](https://www.npmjs.com/package/docsui-cli)
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import prompts from "prompts";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import ora from "ora";
|
|
5
|
+
import fs from "fs-extra";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { getConfig } from "../utils/get-config.js";
|
|
8
|
+
import {
|
|
9
|
+
fetchComponent
|
|
10
|
+
} from "../utils/fetch-component.js";
|
|
11
|
+
import { installDependencies } from "../utils/install-deps.js";
|
|
12
|
+
import { COMPONENT_MDX_MAP, REGISTRY } from "../lib/component-registry.js";
|
|
13
|
+
import { scanMdxComponents, printScanWarnings } from "../utils/scan-mdx.js";
|
|
14
|
+
import { ping } from "../utils/telemetry.js";
|
|
15
|
+
async function findMdxComponentsFile(componentsDir, cwd) {
|
|
16
|
+
const candidates = [
|
|
17
|
+
path.join(cwd, componentsDir, "mdx-components.tsx"),
|
|
18
|
+
path.join(cwd, componentsDir, "mdx-components.jsx"),
|
|
19
|
+
// common fallbacks in case init used a different dir than the actual file
|
|
20
|
+
path.join(cwd, "src/components/mdx-components.tsx"),
|
|
21
|
+
path.join(cwd, "src/components/mdx-components.jsx"),
|
|
22
|
+
path.join(cwd, "components/mdx-components.tsx"),
|
|
23
|
+
path.join(cwd, "components/mdx-components.jsx"),
|
|
24
|
+
path.join(cwd, "src/mdx-components.tsx"),
|
|
25
|
+
path.join(cwd, "src/mdx-components.jsx")
|
|
26
|
+
];
|
|
27
|
+
for (const p of candidates) {
|
|
28
|
+
if (await fs.pathExists(p)) return p;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
async function patchMdxComponents(componentName, componentsDir, cwd) {
|
|
33
|
+
const mapping = COMPONENT_MDX_MAP[componentName];
|
|
34
|
+
if (!mapping) return { patched: false, mdxPath: "" };
|
|
35
|
+
if (mapping.imports.length === 0 && Object.keys(mapping.elementMappings).length === 0)
|
|
36
|
+
return { patched: false, mdxPath: "" };
|
|
37
|
+
let mdxPath = await findMdxComponentsFile(componentsDir, cwd);
|
|
38
|
+
if (!mdxPath) {
|
|
39
|
+
mdxPath = path.join(cwd, componentsDir, "mdx-components.tsx");
|
|
40
|
+
await fs.ensureDir(path.dirname(mdxPath));
|
|
41
|
+
await fs.writeFile(mdxPath, `export const mdxComponents = {
|
|
42
|
+
}
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
let content = await fs.readFile(mdxPath, "utf-8");
|
|
46
|
+
if (!content.includes(mapping.importFile)) {
|
|
47
|
+
const importLine = `import { ${mapping.imports.join(", ")} } from "${mapping.importFile}"
|
|
48
|
+
`;
|
|
49
|
+
const exportIdx = content.indexOf("export const mdxComponents");
|
|
50
|
+
if (exportIdx !== -1) {
|
|
51
|
+
content = content.slice(0, exportIdx) + importLine + content.slice(exportIdx);
|
|
52
|
+
} else {
|
|
53
|
+
content = importLine + content;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const ANCHOR = "export const mdxComponents";
|
|
57
|
+
const anchorIdx = content.indexOf(ANCHOR);
|
|
58
|
+
const openBrace = anchorIdx !== -1 ? content.indexOf("{", anchorIdx) : -1;
|
|
59
|
+
let closeIdx = -1;
|
|
60
|
+
if (openBrace !== -1) {
|
|
61
|
+
let depth = 0;
|
|
62
|
+
for (let i = openBrace; i < content.length; i++) {
|
|
63
|
+
if (content[i] === "{") depth++;
|
|
64
|
+
else if (content[i] === "}") {
|
|
65
|
+
depth--;
|
|
66
|
+
if (depth === 0) {
|
|
67
|
+
closeIdx = i;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const objectBody = openBrace !== -1 && closeIdx !== -1 ? content.slice(openBrace + 1, closeIdx) : "";
|
|
74
|
+
const additions = [];
|
|
75
|
+
for (const [element, component] of Object.entries(mapping.elementMappings)) {
|
|
76
|
+
if (!objectBody.includes(`${element}:`) && !objectBody.includes(`${element} :`)) {
|
|
77
|
+
additions.push(` ${element}: ${component},`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (const exportName of mapping.imports) {
|
|
81
|
+
const alreadyMapped = Object.values(mapping.elementMappings).includes(
|
|
82
|
+
exportName
|
|
83
|
+
);
|
|
84
|
+
if (!alreadyMapped && !objectBody.includes(`${exportName},`) && !objectBody.includes(`${exportName}:`)) {
|
|
85
|
+
additions.push(` ${exportName},`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (additions.length > 0 && closeIdx !== -1) {
|
|
89
|
+
content = content.slice(0, closeIdx) + additions.join("\n") + "\n" + content.slice(closeIdx);
|
|
90
|
+
}
|
|
91
|
+
await fs.writeFile(mdxPath, content);
|
|
92
|
+
return { patched: true, mdxPath };
|
|
93
|
+
}
|
|
94
|
+
function loadRegistry() {
|
|
95
|
+
return {
|
|
96
|
+
components: Object.entries(REGISTRY).filter(([name]) => name !== "utils").map(([name, entry]) => ({
|
|
97
|
+
name,
|
|
98
|
+
type: "mdx",
|
|
99
|
+
description: entry.description,
|
|
100
|
+
files: entry.files
|
|
101
|
+
}))
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const add = new Command().name("add").description("Add components to your project").argument("[components...]", "components to add").option(
|
|
105
|
+
"-o, --overwrite",
|
|
106
|
+
"overwrite existing files without prompting",
|
|
107
|
+
false
|
|
108
|
+
).option("-a, --all", "add all available components", false).action(
|
|
109
|
+
async (components, opts) => {
|
|
110
|
+
console.log();
|
|
111
|
+
const config = await getConfig();
|
|
112
|
+
if (!config) {
|
|
113
|
+
console.log(chalk.red("\u2717 No docsui.json found"));
|
|
114
|
+
console.log(chalk.yellow("Run 'npx docsui-cli@latest init' first"));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
if (opts.all) {
|
|
118
|
+
const registry = loadRegistry();
|
|
119
|
+
components = registry.components.filter((c) => c.type === "mdx").map((c) => c.name);
|
|
120
|
+
console.log(
|
|
121
|
+
chalk.dim(`Adding all ${components.length} components...
|
|
122
|
+
`)
|
|
123
|
+
);
|
|
124
|
+
} else if (components.length === 0) {
|
|
125
|
+
const registry = loadRegistry();
|
|
126
|
+
const mdxComponents = registry.components.filter(
|
|
127
|
+
(c) => c.type === "mdx"
|
|
128
|
+
);
|
|
129
|
+
const { selected } = await prompts({
|
|
130
|
+
type: "multiselect",
|
|
131
|
+
name: "selected",
|
|
132
|
+
message: "Which components would you like to add?",
|
|
133
|
+
choices: mdxComponents.map((c) => ({
|
|
134
|
+
title: c.name.split("-").map(
|
|
135
|
+
(word) => word.charAt(0).toUpperCase() + word.slice(1)
|
|
136
|
+
).join(" "),
|
|
137
|
+
value: c.name,
|
|
138
|
+
description: c.description
|
|
139
|
+
}))
|
|
140
|
+
});
|
|
141
|
+
components = selected;
|
|
142
|
+
}
|
|
143
|
+
if (!components || components.length === 0) {
|
|
144
|
+
console.log(chalk.yellow("No components selected"));
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
const spinner = ora("Fetching components...").start();
|
|
148
|
+
try {
|
|
149
|
+
const componentsData = [];
|
|
150
|
+
const processedComponents = /* @__PURE__ */ new Set();
|
|
151
|
+
async function fetchComponentRecursive(componentName) {
|
|
152
|
+
if (processedComponents.has(componentName)) return;
|
|
153
|
+
processedComponents.add(componentName);
|
|
154
|
+
const data = await fetchComponent(componentName);
|
|
155
|
+
if (data.registryDependencies && data.registryDependencies.length > 0) {
|
|
156
|
+
for (const depName of data.registryDependencies) {
|
|
157
|
+
await fetchComponentRecursive(depName);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
componentsData.push(data);
|
|
161
|
+
}
|
|
162
|
+
for (const component of components) {
|
|
163
|
+
await fetchComponentRecursive(component);
|
|
164
|
+
}
|
|
165
|
+
spinner.text = "Installing dependencies...";
|
|
166
|
+
const allDeps = /* @__PURE__ */ new Set();
|
|
167
|
+
for (const data of componentsData) {
|
|
168
|
+
data.dependencies?.forEach((dep) => allDeps.add(dep));
|
|
169
|
+
}
|
|
170
|
+
if (allDeps.size > 0) {
|
|
171
|
+
await installDependencies(Array.from(allDeps));
|
|
172
|
+
}
|
|
173
|
+
spinner.stop();
|
|
174
|
+
const cwd = process.cwd();
|
|
175
|
+
const framework = config.framework ?? "unknown";
|
|
176
|
+
const written = [];
|
|
177
|
+
const skipped = [];
|
|
178
|
+
for (const data of componentsData) {
|
|
179
|
+
for (const file of data.files) {
|
|
180
|
+
const libRoot = config.componentsDir.startsWith("src/") ? path.join(cwd, "src") : cwd;
|
|
181
|
+
const filePath = file.path.startsWith("lib/") ? path.join(libRoot, file.path) : path.join(cwd, config.componentsDir, file.path);
|
|
182
|
+
if (!path.resolve(filePath).startsWith(path.resolve(cwd) + path.sep)) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`Refusing to write outside project root: ${file.path}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
let incoming = file.content;
|
|
188
|
+
if (framework === "react") {
|
|
189
|
+
incoming = incoming.replace(/^["']use client["']\n\n?/m, "");
|
|
190
|
+
}
|
|
191
|
+
const exists = await fs.pathExists(filePath);
|
|
192
|
+
if (exists) {
|
|
193
|
+
const existing = await fs.readFile(filePath, "utf-8");
|
|
194
|
+
if (existing.trim() === incoming.trim()) {
|
|
195
|
+
skipped.push(file.path);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (!opts.overwrite) {
|
|
199
|
+
const { overwrite } = await prompts({
|
|
200
|
+
type: "confirm",
|
|
201
|
+
name: "overwrite",
|
|
202
|
+
message: `${chalk.yellow(file.path)} already exists and has local changes. Overwrite?`,
|
|
203
|
+
initial: false
|
|
204
|
+
});
|
|
205
|
+
if (!overwrite) {
|
|
206
|
+
skipped.push(file.path);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
212
|
+
await fs.writeFile(filePath, incoming, "utf-8");
|
|
213
|
+
written.push(file.path);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
let patchedFile = "";
|
|
217
|
+
for (const data of componentsData) {
|
|
218
|
+
const result = await patchMdxComponents(
|
|
219
|
+
data.name,
|
|
220
|
+
config.componentsDir,
|
|
221
|
+
cwd
|
|
222
|
+
);
|
|
223
|
+
if (result.patched && !patchedFile) {
|
|
224
|
+
patchedFile = path.relative(cwd, result.mdxPath);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
console.log();
|
|
228
|
+
for (const component of components) {
|
|
229
|
+
console.log(chalk.green(`\u2713 ${component}`));
|
|
230
|
+
}
|
|
231
|
+
if (skipped.length > 0) {
|
|
232
|
+
console.log(chalk.dim(` skipped: ${skipped.join(", ")}`));
|
|
233
|
+
}
|
|
234
|
+
if (patchedFile) {
|
|
235
|
+
console.log(chalk.dim(` updated: ${patchedFile}`));
|
|
236
|
+
}
|
|
237
|
+
console.log();
|
|
238
|
+
console.log(chalk.bold("Done! \u{1F389}"));
|
|
239
|
+
console.log();
|
|
240
|
+
ping("add", { components });
|
|
241
|
+
const pm = await fs.pathExists(path.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : await fs.pathExists(path.join(cwd, "yarn.lock")) ? "yarn" : "npm";
|
|
242
|
+
const scanResults = await scanMdxComponents(cwd);
|
|
243
|
+
printScanWarnings(scanResults, pm);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
spinner.fail("Failed to add components");
|
|
246
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
247
|
+
console.error(chalk.red(msg));
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
export {
|
|
253
|
+
add
|
|
254
|
+
};
|