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
|
@@ -0,0 +1,548 @@
|
|
|
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 { execa } from "execa";
|
|
8
|
+
import {
|
|
9
|
+
detectProjectStructure
|
|
10
|
+
} from "../utils/detect-structure.js";
|
|
11
|
+
import { scanMdxComponents, printScanWarnings } from "../utils/scan-mdx.js";
|
|
12
|
+
import { ping } from "../utils/telemetry.js";
|
|
13
|
+
import {
|
|
14
|
+
CSS_VARS_BLOCK_V3,
|
|
15
|
+
CSS_VARS_BLOCK_V4,
|
|
16
|
+
MDXUI_SEMANTIC_TOKENS,
|
|
17
|
+
MDXUI_V4_THEME,
|
|
18
|
+
MDXUI_TAILWIND_COLOR_SCALES,
|
|
19
|
+
TAILWIND_V3_THEME_EXTENSIONS
|
|
20
|
+
} from "../lib/css-tokens.js";
|
|
21
|
+
const FRAMEWORK_LABELS = {
|
|
22
|
+
nextjs: "Next.js",
|
|
23
|
+
astro: "Astro",
|
|
24
|
+
react: "React",
|
|
25
|
+
unknown: "Unknown"
|
|
26
|
+
};
|
|
27
|
+
const init = new Command().name("init").description("Initialize your project for docsui").action(async () => {
|
|
28
|
+
console.log(chalk.bold("\n\u2728 Welcome to docsui!\n"));
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
const structure = await detectProjectStructure(cwd);
|
|
31
|
+
console.log(
|
|
32
|
+
chalk.dim(
|
|
33
|
+
`Detected framework: ${chalk.white(FRAMEWORK_LABELS[structure.framework])}`
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
console.log(
|
|
37
|
+
chalk.dim(`Structure: ${structure.hasSrc ? "src/" : "root-level"}
|
|
38
|
+
`)
|
|
39
|
+
);
|
|
40
|
+
const config = await prompts([
|
|
41
|
+
{
|
|
42
|
+
type: "text",
|
|
43
|
+
name: "componentsDir",
|
|
44
|
+
message: "Where should we put the components?",
|
|
45
|
+
initial: structure.componentsDir
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "confirm",
|
|
49
|
+
name: "typescript",
|
|
50
|
+
message: "Are you using TypeScript?",
|
|
51
|
+
initial: structure.hasTypeScript
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: "confirm",
|
|
55
|
+
name: "tailwind",
|
|
56
|
+
message: "Are you using Tailwind CSS?",
|
|
57
|
+
initial: structure.hasTailwind
|
|
58
|
+
}
|
|
59
|
+
]);
|
|
60
|
+
const spinner = ora("Initializing project...").start();
|
|
61
|
+
try {
|
|
62
|
+
await fs.ensureDir(path.join(cwd, config.componentsDir));
|
|
63
|
+
await fs.ensureDir(path.join(cwd, structure.libDir));
|
|
64
|
+
const ext = config.typescript ? "ts" : "js";
|
|
65
|
+
const utilsContent = config.typescript ? `import { clsx, type ClassValue } from "clsx"
|
|
66
|
+
import { twMerge } from "tailwind-merge"
|
|
67
|
+
|
|
68
|
+
export function cn(...inputs: ClassValue[]) {
|
|
69
|
+
return twMerge(clsx(inputs))
|
|
70
|
+
}
|
|
71
|
+
` : `import { clsx } from "clsx"
|
|
72
|
+
import { twMerge } from "tailwind-merge"
|
|
73
|
+
|
|
74
|
+
export function cn(...inputs) {
|
|
75
|
+
return twMerge(clsx(inputs))
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
const utilsPath = path.join(cwd, structure.libDir, `utils.${ext}`);
|
|
79
|
+
if (!await fs.pathExists(utilsPath)) {
|
|
80
|
+
await fs.writeFile(utilsPath, utilsContent);
|
|
81
|
+
}
|
|
82
|
+
await setupFramework(structure.framework, cwd, spinner);
|
|
83
|
+
if (config.typescript) {
|
|
84
|
+
await setupPathAlias(
|
|
85
|
+
structure.framework,
|
|
86
|
+
structure.hasSrc,
|
|
87
|
+
cwd,
|
|
88
|
+
spinner
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
let cssFile = null;
|
|
92
|
+
let tailwindConfig = null;
|
|
93
|
+
if (config.tailwind) {
|
|
94
|
+
const twVersion = await detectTailwindVersion(cwd);
|
|
95
|
+
cssFile = await setupGlobalCSS(
|
|
96
|
+
structure.framework,
|
|
97
|
+
structure.hasSrc,
|
|
98
|
+
cwd,
|
|
99
|
+
twVersion,
|
|
100
|
+
spinner
|
|
101
|
+
);
|
|
102
|
+
tailwindConfig = await setupTailwindConfig(cwd, twVersion, spinner);
|
|
103
|
+
}
|
|
104
|
+
await setupMdxComponents(
|
|
105
|
+
config.componentsDir,
|
|
106
|
+
config.typescript,
|
|
107
|
+
cwd,
|
|
108
|
+
spinner
|
|
109
|
+
);
|
|
110
|
+
await fs.writeJSON(
|
|
111
|
+
path.join(cwd, "docsui.json"),
|
|
112
|
+
{
|
|
113
|
+
$schema: "https://docsui.dev/schema.json",
|
|
114
|
+
framework: structure.framework,
|
|
115
|
+
mdxPipeline: structure.mdxPipeline,
|
|
116
|
+
componentsDir: config.componentsDir,
|
|
117
|
+
typescript: config.typescript,
|
|
118
|
+
tailwind: config.tailwind,
|
|
119
|
+
...cssFile && { cssFile },
|
|
120
|
+
...tailwindConfig && { tailwindConfig }
|
|
121
|
+
},
|
|
122
|
+
{ spaces: 2 }
|
|
123
|
+
);
|
|
124
|
+
spinner.succeed("Project initialized!");
|
|
125
|
+
ping("init", { framework: structure.framework });
|
|
126
|
+
console.log(chalk.green("\n\u2713 docsui.json"));
|
|
127
|
+
console.log(chalk.green(`\u2713 ${config.componentsDir}/`));
|
|
128
|
+
console.log(chalk.green(`\u2713 ${structure.libDir}/utils.${ext}`));
|
|
129
|
+
console.log(
|
|
130
|
+
chalk.green(
|
|
131
|
+
`\u2713 ${config.componentsDir}/mdx-components.${config.typescript ? "tsx" : "jsx"}`
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
if (config.typescript && structure.framework !== "nextjs") {
|
|
135
|
+
console.log(
|
|
136
|
+
chalk.green("\u2713 Configured @/ path alias in tsconfig and vite.config")
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (config.tailwind) {
|
|
140
|
+
console.log(
|
|
141
|
+
chalk.green(
|
|
142
|
+
"\u2713 Added CSS variable tokens to globals.css and tailwind.config"
|
|
143
|
+
)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
printNextSteps(structure.framework, structure.mdxPipeline);
|
|
147
|
+
const pm = await fs.pathExists(path.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : await fs.pathExists(path.join(cwd, "yarn.lock")) ? "yarn" : "npm";
|
|
148
|
+
const scanResults = await scanMdxComponents(cwd);
|
|
149
|
+
printScanWarnings(scanResults, pm);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
spinner.fail("Failed to initialize project");
|
|
152
|
+
console.error(error);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
async function setupFramework(framework, cwd, spinner) {
|
|
157
|
+
if (framework !== "astro") return;
|
|
158
|
+
const pkg = await fs.readJSON(path.join(cwd, "package.json"));
|
|
159
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
160
|
+
if (!("@astrojs/react" in deps)) {
|
|
161
|
+
spinner.text = "Installing @astrojs/react...";
|
|
162
|
+
const pm = await fs.pathExists(path.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : await fs.pathExists(path.join(cwd, "yarn.lock")) ? "yarn" : "npm";
|
|
163
|
+
const addCmd = pm === "npm" ? "install" : "add";
|
|
164
|
+
await execa(
|
|
165
|
+
pm,
|
|
166
|
+
[
|
|
167
|
+
addCmd,
|
|
168
|
+
"-D",
|
|
169
|
+
"@astrojs/react",
|
|
170
|
+
"react",
|
|
171
|
+
"react-dom",
|
|
172
|
+
"@types/react",
|
|
173
|
+
"@types/react-dom"
|
|
174
|
+
],
|
|
175
|
+
{ cwd }
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
const astroConfig = await fs.pathExists(path.join(cwd, "astro.config.ts")) ? path.join(cwd, "astro.config.ts") : path.join(cwd, "astro.config.mjs");
|
|
179
|
+
if (await fs.pathExists(astroConfig)) {
|
|
180
|
+
let content = await fs.readFile(astroConfig, "utf-8");
|
|
181
|
+
if (!content.includes("@astrojs/react")) {
|
|
182
|
+
content = `import react from "@astrojs/react"
|
|
183
|
+
` + content;
|
|
184
|
+
content = content.replace(
|
|
185
|
+
/integrations:\s*\[/,
|
|
186
|
+
"integrations: [\n react(),"
|
|
187
|
+
);
|
|
188
|
+
await fs.writeFile(astroConfig, content);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async function setupPathAlias(framework, hasSrc, cwd, spinner) {
|
|
193
|
+
if (framework === "nextjs") return;
|
|
194
|
+
const aliasTarget = hasSrc ? "./src" : ".";
|
|
195
|
+
const aliasTargetTs = hasSrc ? ["./src/*"] : ["./*"];
|
|
196
|
+
const tsconfigPaths = [
|
|
197
|
+
path.join(cwd, "tsconfig.app.json"),
|
|
198
|
+
// Vite generates this
|
|
199
|
+
path.join(cwd, "tsconfig.json")
|
|
200
|
+
];
|
|
201
|
+
for (const tsconfigPath of tsconfigPaths) {
|
|
202
|
+
if (!await fs.pathExists(tsconfigPath)) continue;
|
|
203
|
+
try {
|
|
204
|
+
const tsconfig = await fs.readJSON(tsconfigPath);
|
|
205
|
+
tsconfig.compilerOptions = tsconfig.compilerOptions ?? {};
|
|
206
|
+
tsconfig.compilerOptions.baseUrl = ".";
|
|
207
|
+
tsconfig.compilerOptions.paths = {
|
|
208
|
+
...tsconfig.compilerOptions.paths ?? {},
|
|
209
|
+
"@/*": aliasTargetTs
|
|
210
|
+
};
|
|
211
|
+
await fs.writeJSON(tsconfigPath, tsconfig, { spaces: 2 });
|
|
212
|
+
spinner.text = `Patched ${path.basename(tsconfigPath)} with @/ alias`;
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const viteConfigPaths = [
|
|
217
|
+
path.join(cwd, "vite.config.ts"),
|
|
218
|
+
path.join(cwd, "vite.config.js"),
|
|
219
|
+
path.join(cwd, "vite.config.mts")
|
|
220
|
+
];
|
|
221
|
+
for (const viteConfigPath of viteConfigPaths) {
|
|
222
|
+
if (!await fs.pathExists(viteConfigPath)) continue;
|
|
223
|
+
try {
|
|
224
|
+
let content = await fs.readFile(viteConfigPath, "utf-8");
|
|
225
|
+
if (!content.includes("import path from")) {
|
|
226
|
+
content = `import path from "path"
|
|
227
|
+
` + content;
|
|
228
|
+
}
|
|
229
|
+
if (!content.includes("resolve:") && !content.includes("alias:")) {
|
|
230
|
+
content = content.replace(
|
|
231
|
+
/defineConfig\s*\(\s*\{/,
|
|
232
|
+
`defineConfig({
|
|
233
|
+
resolve: {
|
|
234
|
+
alias: {
|
|
235
|
+
"@": path.resolve(__dirname, "${aliasTarget}"),
|
|
236
|
+
},
|
|
237
|
+
},`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
await fs.writeFile(viteConfigPath, content);
|
|
241
|
+
spinner.text = `Patched ${path.basename(viteConfigPath)} with @/ alias`;
|
|
242
|
+
const pkg = await fs.readJSON(path.join(cwd, "package.json"));
|
|
243
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
244
|
+
if (!("@types/node" in deps)) {
|
|
245
|
+
spinner.text = "Installing @types/node...";
|
|
246
|
+
const pm = await fs.pathExists(path.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : await fs.pathExists(path.join(cwd, "yarn.lock")) ? "yarn" : "npm";
|
|
247
|
+
const addCmd = pm === "npm" ? "install" : "add";
|
|
248
|
+
await execa(pm, [addCmd, "-D", "@types/node"], { cwd });
|
|
249
|
+
}
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function detectTailwindVersion(cwd) {
|
|
256
|
+
try {
|
|
257
|
+
const pkg = await fs.readJSON(path.join(cwd, "package.json"));
|
|
258
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
259
|
+
const version = deps["tailwindcss"] ?? "";
|
|
260
|
+
return version.match(/^\^?4/) ? 4 : 3;
|
|
261
|
+
} catch {
|
|
262
|
+
return 3;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function setupGlobalCSS(_framework, hasSrc, cwd, twVersion, spinner) {
|
|
266
|
+
const candidatePaths = [
|
|
267
|
+
path.join(cwd, "src/app/globals.css"),
|
|
268
|
+
// Next.js App Router (src)
|
|
269
|
+
path.join(cwd, "app/globals.css"),
|
|
270
|
+
// Next.js App Router (root)
|
|
271
|
+
path.join(cwd, "src/styles/globals.css"),
|
|
272
|
+
// Astro / custom
|
|
273
|
+
path.join(cwd, "src/styles/global.css"),
|
|
274
|
+
// Astro / custom
|
|
275
|
+
path.join(cwd, "src/index.css"),
|
|
276
|
+
// Vite React
|
|
277
|
+
path.join(cwd, "src/globals.css"),
|
|
278
|
+
// custom
|
|
279
|
+
path.join(cwd, "index.css"),
|
|
280
|
+
// root-level
|
|
281
|
+
path.join(cwd, "globals.css")
|
|
282
|
+
// root-level
|
|
283
|
+
];
|
|
284
|
+
let cssPath = null;
|
|
285
|
+
for (const p of candidatePaths) {
|
|
286
|
+
if (await fs.pathExists(p)) {
|
|
287
|
+
cssPath = p;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const cssVarsBlock = twVersion === 4 ? CSS_VARS_BLOCK_V4 : CSS_VARS_BLOCK_V3;
|
|
292
|
+
const twDirectives = twVersion === 4 ? `@import "tailwindcss";
|
|
293
|
+
` : `@tailwind base;
|
|
294
|
+
@tailwind components;
|
|
295
|
+
@tailwind utilities;
|
|
296
|
+
`;
|
|
297
|
+
if (!cssPath) {
|
|
298
|
+
cssPath = hasSrc ? path.join(cwd, "src/index.css") : path.join(cwd, "index.css");
|
|
299
|
+
await fs.ensureDir(path.dirname(cssPath));
|
|
300
|
+
const v4Theme = twVersion === 4 ? MDXUI_V4_THEME : "";
|
|
301
|
+
await fs.writeFile(
|
|
302
|
+
cssPath,
|
|
303
|
+
twDirectives + cssVarsBlock + MDXUI_SEMANTIC_TOKENS + v4Theme
|
|
304
|
+
);
|
|
305
|
+
spinner.text = `Created ${path.relative(cwd, cssPath)} with CSS variable tokens`;
|
|
306
|
+
const entryFiles = [
|
|
307
|
+
"src/main.tsx",
|
|
308
|
+
"src/main.ts",
|
|
309
|
+
"src/index.tsx",
|
|
310
|
+
"src/index.ts",
|
|
311
|
+
"main.tsx",
|
|
312
|
+
"main.ts",
|
|
313
|
+
"src/entry.ts",
|
|
314
|
+
"src/entry.tsx"
|
|
315
|
+
];
|
|
316
|
+
for (const entry of entryFiles) {
|
|
317
|
+
const entryPath = path.join(cwd, entry);
|
|
318
|
+
if (!await fs.pathExists(entryPath)) continue;
|
|
319
|
+
const entryContent = await fs.readFile(entryPath, "utf-8");
|
|
320
|
+
if (!entryContent.includes(".css")) {
|
|
321
|
+
const rel = path.relative(path.dirname(entryPath), cssPath).replace(/\\/g, "/");
|
|
322
|
+
await fs.writeFile(entryPath, `import "./${rel}"
|
|
323
|
+
` + entryContent);
|
|
324
|
+
spinner.text = `Added CSS import to ${entry}`;
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
return path.relative(cwd, cssPath);
|
|
329
|
+
}
|
|
330
|
+
let content = "";
|
|
331
|
+
try {
|
|
332
|
+
content = await fs.readFile(cssPath, "utf-8");
|
|
333
|
+
} catch {
|
|
334
|
+
return path.relative(cwd, cssPath);
|
|
335
|
+
}
|
|
336
|
+
if (!content.includes("--background:") && !content.includes("--foreground:")) {
|
|
337
|
+
try {
|
|
338
|
+
await fs.appendFile(cssPath, cssVarsBlock);
|
|
339
|
+
content += cssVarsBlock;
|
|
340
|
+
spinner.text = `Added CSS variable tokens to ${path.relative(cwd, cssPath)}`;
|
|
341
|
+
} catch {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (!content.includes("--mdxui-info-bg")) {
|
|
345
|
+
try {
|
|
346
|
+
const v4Theme = twVersion === 4 ? MDXUI_V4_THEME : "";
|
|
347
|
+
await fs.appendFile(cssPath, MDXUI_SEMANTIC_TOKENS + v4Theme);
|
|
348
|
+
spinner.text = `Added docsui semantic tokens to ${path.relative(cwd, cssPath)}`;
|
|
349
|
+
} catch {
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return path.relative(cwd, cssPath);
|
|
353
|
+
}
|
|
354
|
+
function injectMdxuiColorsIntoConfig(content) {
|
|
355
|
+
const colorsIdx = content.indexOf("colors:");
|
|
356
|
+
if (colorsIdx === -1) {
|
|
357
|
+
return content.includes("extend:") ? content.replace(
|
|
358
|
+
/extend:\s*\{/,
|
|
359
|
+
`extend: {
|
|
360
|
+
colors: {
|
|
361
|
+
${MDXUI_TAILWIND_COLOR_SCALES}
|
|
362
|
+
},`
|
|
363
|
+
) : content;
|
|
364
|
+
}
|
|
365
|
+
const openBrace = content.indexOf("{", colorsIdx);
|
|
366
|
+
if (openBrace === -1) return content;
|
|
367
|
+
let depth = 0;
|
|
368
|
+
let closeIdx = -1;
|
|
369
|
+
for (let i = openBrace; i < content.length; i++) {
|
|
370
|
+
if (content[i] === "{") depth++;
|
|
371
|
+
else if (content[i] === "}") {
|
|
372
|
+
if (--depth === 0) {
|
|
373
|
+
closeIdx = i;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (closeIdx === -1) return content;
|
|
379
|
+
return content.slice(0, closeIdx) + `
|
|
380
|
+
${MDXUI_TAILWIND_COLOR_SCALES}
|
|
381
|
+
` + content.slice(closeIdx);
|
|
382
|
+
}
|
|
383
|
+
async function setupTailwindConfig(cwd, twVersion, spinner) {
|
|
384
|
+
if (twVersion === 4) return null;
|
|
385
|
+
const configPaths = [
|
|
386
|
+
path.join(cwd, "tailwind.config.ts"),
|
|
387
|
+
path.join(cwd, "tailwind.config.js"),
|
|
388
|
+
path.join(cwd, "tailwind.config.mjs"),
|
|
389
|
+
path.join(cwd, "tailwind.config.cjs")
|
|
390
|
+
];
|
|
391
|
+
let configPath = null;
|
|
392
|
+
for (const p of configPaths) {
|
|
393
|
+
if (await fs.pathExists(p)) {
|
|
394
|
+
configPath = p;
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (!configPath) return null;
|
|
399
|
+
try {
|
|
400
|
+
let content = await fs.readFile(configPath, "utf-8");
|
|
401
|
+
const hasShadcn = content.includes("hsl(var(--background))") || content.includes('"--background"');
|
|
402
|
+
const hasMdxui = content.includes("--mdxui-info-border");
|
|
403
|
+
if (!hasShadcn) {
|
|
404
|
+
if (content.includes("extend:")) {
|
|
405
|
+
content = content.replace(
|
|
406
|
+
/extend:\s*\{/,
|
|
407
|
+
`extend: {
|
|
408
|
+
${TAILWIND_V3_THEME_EXTENSIONS}`
|
|
409
|
+
);
|
|
410
|
+
} else if (content.includes("theme:")) {
|
|
411
|
+
content = content.replace(
|
|
412
|
+
/theme:\s*\{/,
|
|
413
|
+
`theme: {
|
|
414
|
+
extend: {
|
|
415
|
+
${TAILWIND_V3_THEME_EXTENSIONS}
|
|
416
|
+
},`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
await fs.writeFile(configPath, content);
|
|
420
|
+
spinner.text = `Patched ${path.basename(configPath)} with CSS variable theme colors`;
|
|
421
|
+
} else if (!hasMdxui) {
|
|
422
|
+
content = injectMdxuiColorsIntoConfig(content);
|
|
423
|
+
await fs.writeFile(configPath, content);
|
|
424
|
+
spinner.text = `Added docsui semantic colors to ${path.basename(configPath)}`;
|
|
425
|
+
}
|
|
426
|
+
} catch {
|
|
427
|
+
}
|
|
428
|
+
return path.relative(cwd, configPath);
|
|
429
|
+
}
|
|
430
|
+
async function setupMdxComponents(componentsDir, typescript, cwd, spinner) {
|
|
431
|
+
const ext = typescript ? "tsx" : "jsx";
|
|
432
|
+
const mdxPath = path.join(cwd, componentsDir, `mdx-components.${ext}`);
|
|
433
|
+
if (await fs.pathExists(mdxPath)) return;
|
|
434
|
+
const content = `export const mdxComponents = {
|
|
435
|
+
}
|
|
436
|
+
`;
|
|
437
|
+
await fs.ensureDir(path.dirname(mdxPath));
|
|
438
|
+
await fs.writeFile(mdxPath, content);
|
|
439
|
+
spinner.text = `Created ${path.relative(cwd, mdxPath)}`;
|
|
440
|
+
}
|
|
441
|
+
const REMARK_PLUGIN_SNIPPETS = {
|
|
442
|
+
contentlayer: `
|
|
443
|
+
// contentlayer.config.ts
|
|
444
|
+
import remarkMdxUi from "@docsui-io/remark-plugin"
|
|
445
|
+
|
|
446
|
+
export default makeSource({
|
|
447
|
+
mdxOptions: {
|
|
448
|
+
remarkPlugins: [
|
|
449
|
+
[remarkMdxUi, { callout: true, table: true, steps: true, mermaid: true }],
|
|
450
|
+
],
|
|
451
|
+
},
|
|
452
|
+
})`,
|
|
453
|
+
"next-mdx-remote": `
|
|
454
|
+
// In your compileMdx / serialize call:
|
|
455
|
+
import remarkMdxUi from "@docsui-io/remark-plugin"
|
|
456
|
+
|
|
457
|
+
const result = await serialize(source, {
|
|
458
|
+
mdxOptions: {
|
|
459
|
+
remarkPlugins: [
|
|
460
|
+
[remarkMdxUi, { callout: true, table: true, steps: true, mermaid: true }],
|
|
461
|
+
],
|
|
462
|
+
},
|
|
463
|
+
})`,
|
|
464
|
+
"next-mdx": `
|
|
465
|
+
// next.config.ts
|
|
466
|
+
import remarkMdxUi from "@docsui-io/remark-plugin"
|
|
467
|
+
|
|
468
|
+
const withMDX = createMDX({
|
|
469
|
+
options: {
|
|
470
|
+
remarkPlugins: [
|
|
471
|
+
[remarkMdxUi, { callout: true, table: true, steps: true, mermaid: true }],
|
|
472
|
+
],
|
|
473
|
+
},
|
|
474
|
+
})`,
|
|
475
|
+
"astro-mdx": `
|
|
476
|
+
// astro.config.ts
|
|
477
|
+
import remarkMdxUi from "@docsui-io/remark-plugin"
|
|
478
|
+
|
|
479
|
+
export default defineConfig({
|
|
480
|
+
markdown: {
|
|
481
|
+
remarkPlugins: [
|
|
482
|
+
[remarkMdxUi, { callout: true, table: true, steps: true, mermaid: true }],
|
|
483
|
+
],
|
|
484
|
+
},
|
|
485
|
+
})`,
|
|
486
|
+
"mdx-rollup": `
|
|
487
|
+
// vite.config.ts
|
|
488
|
+
import remarkMdxUi from "@docsui-io/remark-plugin"
|
|
489
|
+
|
|
490
|
+
export default defineConfig({
|
|
491
|
+
plugins: [
|
|
492
|
+
mdx({
|
|
493
|
+
remarkPlugins: [
|
|
494
|
+
[remarkMdxUi, { callout: true, table: true, steps: true, mermaid: true }],
|
|
495
|
+
],
|
|
496
|
+
}),
|
|
497
|
+
],
|
|
498
|
+
})`,
|
|
499
|
+
unknown: null
|
|
500
|
+
};
|
|
501
|
+
function printNextSteps(framework, mdxPipeline) {
|
|
502
|
+
console.log(chalk.bold("\n\u{1F389} You're all set!\n"));
|
|
503
|
+
console.log("Next steps:");
|
|
504
|
+
console.log(chalk.cyan(" npx docsui-cli@latest add callout"));
|
|
505
|
+
console.log(chalk.cyan(" npx docsui-cli@latest list"));
|
|
506
|
+
const snippet = REMARK_PLUGIN_SNIPPETS[mdxPipeline];
|
|
507
|
+
if (snippet) {
|
|
508
|
+
console.log(
|
|
509
|
+
chalk.bold(
|
|
510
|
+
"\n\u{1F4E6} Wire up the remark plugin so markdown auto-upgrades to components:"
|
|
511
|
+
)
|
|
512
|
+
);
|
|
513
|
+
console.log(
|
|
514
|
+
chalk.dim(" Install: npm install docsui-cli-remark-plugin")
|
|
515
|
+
);
|
|
516
|
+
console.log(chalk.white(snippet));
|
|
517
|
+
} else {
|
|
518
|
+
console.log(
|
|
519
|
+
chalk.dim(
|
|
520
|
+
"\n\u{1F4E6} To enable auto-upgrade of markdown \u2192 components, add the remark plugin:"
|
|
521
|
+
)
|
|
522
|
+
);
|
|
523
|
+
console.log(chalk.dim(" npm install docsui-cli-remark-plugin"));
|
|
524
|
+
console.log(
|
|
525
|
+
chalk.dim(
|
|
526
|
+
" Then add remarkMdxUi to your MDX pipeline's remarkPlugins array."
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
if (framework === "astro") {
|
|
531
|
+
console.log(
|
|
532
|
+
chalk.dim("\nFor interactive components in Astro, use client:load:")
|
|
533
|
+
);
|
|
534
|
+
console.log(chalk.white(" <Callout client:load>Hello</Callout>"));
|
|
535
|
+
}
|
|
536
|
+
if (framework !== "unknown") {
|
|
537
|
+
console.log(
|
|
538
|
+
chalk.dim(
|
|
539
|
+
"\nFor math/chemistry support, import KaTeX CSS in your root layout:"
|
|
540
|
+
)
|
|
541
|
+
);
|
|
542
|
+
console.log(chalk.white(' import "katex/dist/katex.min.css"'));
|
|
543
|
+
}
|
|
544
|
+
console.log();
|
|
545
|
+
}
|
|
546
|
+
export {
|
|
547
|
+
init
|
|
548
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { getConfig } from "../utils/get-config.js";
|
|
6
|
+
import { REGISTRY } from "../lib/component-registry.js";
|
|
7
|
+
const UTILITY_NAMES = /* @__PURE__ */ new Set(["utils"]);
|
|
8
|
+
function buildRegistryFromSource() {
|
|
9
|
+
return {
|
|
10
|
+
components: Object.entries(REGISTRY).map(([name, entry]) => ({
|
|
11
|
+
name,
|
|
12
|
+
type: UTILITY_NAMES.has(name) ? "utility" : "mdx",
|
|
13
|
+
description: entry.description,
|
|
14
|
+
files: entry.files
|
|
15
|
+
}))
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function loadRegistry() {
|
|
19
|
+
return buildRegistryFromSource();
|
|
20
|
+
}
|
|
21
|
+
const list = new Command().name("list").description("List all available components").option("-i, --installed", "show only installed components").action(async (opts) => {
|
|
22
|
+
console.log();
|
|
23
|
+
if (opts.installed) {
|
|
24
|
+
const config = await getConfig();
|
|
25
|
+
if (!config) {
|
|
26
|
+
console.log(chalk.red("\u2717 No docsui.json found."));
|
|
27
|
+
console.log(chalk.yellow(" Run: npx docsui-cli@latest init\n"));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const cwd = process.cwd();
|
|
31
|
+
const dir = path.join(cwd, config.componentsDir);
|
|
32
|
+
if (!await fs.pathExists(dir)) {
|
|
33
|
+
console.log(chalk.yellow("No components installed yet."));
|
|
34
|
+
console.log(chalk.dim(" Run: npx docsui-cli@latest add\n"));
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
const files = await fs.readdir(dir);
|
|
38
|
+
const installed = files.filter((f) => f.endsWith(".tsx") && f !== "mdx-components.tsx").map((f) => f.replace(".tsx", ""));
|
|
39
|
+
if (installed.length === 0) {
|
|
40
|
+
console.log(chalk.yellow("No components installed yet."));
|
|
41
|
+
console.log(chalk.dim(" Run: npx docsui-cli@latest add\n"));
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
console.log(chalk.bold(`Installed components (${installed.length}):
|
|
45
|
+
`));
|
|
46
|
+
for (const name of installed.sort()) {
|
|
47
|
+
console.log(chalk.green(` \u2713 ${name}`));
|
|
48
|
+
}
|
|
49
|
+
console.log();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
console.log(chalk.bold("Available components:\n"));
|
|
53
|
+
const registry = await loadRegistry();
|
|
54
|
+
const mdxComponents = registry.components.filter((c) => c.type === "mdx");
|
|
55
|
+
const utilityComponents = registry.components.filter(
|
|
56
|
+
(c) => c.type === "utility"
|
|
57
|
+
);
|
|
58
|
+
if (mdxComponents.length > 0) {
|
|
59
|
+
console.log(chalk.bold(" MDX Components:"));
|
|
60
|
+
for (const component of mdxComponents) {
|
|
61
|
+
console.log(
|
|
62
|
+
chalk.cyan(` ${component.name.padEnd(18)}`),
|
|
63
|
+
component.description
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
console.log();
|
|
67
|
+
}
|
|
68
|
+
if (utilityComponents.length > 0) {
|
|
69
|
+
console.log(chalk.bold(" Utilities:"));
|
|
70
|
+
for (const component of utilityComponents) {
|
|
71
|
+
console.log(
|
|
72
|
+
chalk.cyan(` ${component.name.padEnd(18)}`),
|
|
73
|
+
component.description
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
78
|
+
console.log(chalk.dim("Usage:"));
|
|
79
|
+
console.log(chalk.dim(" npx docsui-cli@latest add <component>"));
|
|
80
|
+
console.log();
|
|
81
|
+
});
|
|
82
|
+
export {
|
|
83
|
+
list
|
|
84
|
+
};
|