doccupine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +526 -0
  3. package/dist/templates/click-outside.d.ts +1 -0
  4. package/dist/templates/click-outside.js +28 -0
  5. package/dist/templates/code.d.ts +1 -0
  6. package/dist/templates/code.js +192 -0
  7. package/dist/templates/components/ClickOutside.d.ts +1 -0
  8. package/dist/templates/components/ClickOutside.js +27 -0
  9. package/dist/templates/components/Docs.d.ts +1 -0
  10. package/dist/templates/components/Docs.js +52 -0
  11. package/dist/templates/components/SideBar.d.ts +1 -0
  12. package/dist/templates/components/SideBar.js +80 -0
  13. package/dist/templates/components/layout/Code.d.ts +1 -0
  14. package/dist/templates/components/layout/Code.js +192 -0
  15. package/dist/templates/components/layout/DocsComponents.d.ts +1 -0
  16. package/dist/templates/components/layout/DocsComponents.js +444 -0
  17. package/dist/templates/components/layout/Footer.d.ts +1 -0
  18. package/dist/templates/components/layout/Footer.js +8 -0
  19. package/dist/templates/components/layout/Header.d.ts +1 -0
  20. package/dist/templates/components/layout/Header.js +280 -0
  21. package/dist/templates/components/layout/Icon.d.ts +1 -0
  22. package/dist/templates/components/layout/Icon.js +19 -0
  23. package/dist/templates/components/layout/Pictograms.d.ts +1 -0
  24. package/dist/templates/components/layout/Pictograms.js +66 -0
  25. package/dist/templates/components/layout/SharedStyles.d.ts +1 -0
  26. package/dist/templates/components/layout/SharedStyles.js +791 -0
  27. package/dist/templates/components/layout/ThemeToggle.d.ts +1 -0
  28. package/dist/templates/components/layout/ThemeToggle.js +123 -0
  29. package/dist/templates/components/layout/Typography.d.ts +1 -0
  30. package/dist/templates/components/layout/Typography.js +51 -0
  31. package/dist/templates/docs-components.d.ts +1 -0
  32. package/dist/templates/docs-components.js +441 -0
  33. package/dist/templates/docs.d.ts +1 -0
  34. package/dist/templates/docs.js +48 -0
  35. package/dist/templates/footer.d.ts +1 -0
  36. package/dist/templates/footer.js +9 -0
  37. package/dist/templates/header.d.ts +1 -0
  38. package/dist/templates/header.js +275 -0
  39. package/dist/templates/home.d.ts +1 -0
  40. package/dist/templates/home.js +80 -0
  41. package/dist/templates/icon.d.ts +1 -0
  42. package/dist/templates/icon.js +20 -0
  43. package/dist/templates/layout.d.ts +1 -0
  44. package/dist/templates/layout.js +66 -0
  45. package/dist/templates/not-found.d.ts +1 -0
  46. package/dist/templates/not-found.js +22 -0
  47. package/dist/templates/pictograms.d.ts +1 -0
  48. package/dist/templates/pictograms.js +67 -0
  49. package/dist/templates/shared-styles.d.ts +1 -0
  50. package/dist/templates/shared-styles.js +792 -0
  51. package/dist/templates/theme-toggle.d.ts +1 -0
  52. package/dist/templates/theme-toggle.js +111 -0
  53. package/dist/templates/theme.d.ts +1 -0
  54. package/dist/templates/theme.js +291 -0
  55. package/dist/templates/typography.d.ts +1 -0
  56. package/dist/templates/typography.js +52 -0
  57. package/dist/templates/utils/orderNavItems.d.ts +1 -0
  58. package/dist/templates/utils/orderNavItems.js +45 -0
  59. package/package.json +44 -0
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,526 @@
1
+ import { program } from "commander";
2
+ import chokidar from "chokidar";
3
+ import fs from "fs-extra";
4
+ import path from "path";
5
+ import matter from "gray-matter";
6
+ import chalk from "chalk";
7
+ import prompts from "prompts";
8
+ import { homeTemplate } from "./templates/home.js";
9
+ import { notFoundTemplate } from "./templates/not-found.js";
10
+ import { layoutTemplate } from "./templates/layout.js";
11
+ import { themeTemplate } from "./templates/theme.js";
12
+ import { iconTemplate } from "./templates/components/layout/Icon.js";
13
+ import { pictogramsTemplate } from "./templates/components/layout/Pictograms.js";
14
+ import { typographyTemplate } from "./templates/components/layout/Typography.js";
15
+ import { headerTemplate } from "./templates/components/layout/Header.js";
16
+ import { footerTemplate } from "./templates/components/layout/Footer.js";
17
+ import { themeToggleTemplate } from "./templates/components/layout/ThemeToggle.js";
18
+ import { sharedStyledTemplate } from "./templates/components/layout/SharedStyles.js";
19
+ import { codeTemplate } from "./templates/components/layout/Code.js";
20
+ import { docsComponentsTemplate } from "./templates/components/layout/DocsComponents.js";
21
+ import { clickOutsideTemplate } from "./templates/components/ClickOutside.js";
22
+ import { docsTemplate } from "./templates/components/Docs.js";
23
+ import { orderNavItemsTemplate } from "./templates/utils/orderNavItems.js";
24
+ import { sideBarTemplate } from "./templates/components/SideBar.js";
25
+ class MDXToNextJSGenerator {
26
+ watchDir;
27
+ outputDir;
28
+ watcher = null;
29
+ constructor(watchDir, outputDir) {
30
+ this.watchDir = path.resolve(watchDir);
31
+ this.outputDir = path.resolve(outputDir);
32
+ }
33
+ async init() {
34
+ console.log(chalk.blue("šŸš€ Initializing MDX to Next.js generator..."));
35
+ // Ensure directories exist
36
+ await fs.ensureDir(this.watchDir);
37
+ await fs.ensureDir(this.outputDir);
38
+ // Create initial Next.js structure
39
+ await this.createNextJSStructure();
40
+ // Process existing files
41
+ await this.processAllMDXFiles();
42
+ console.log(chalk.green("āœ… Initial setup complete!"));
43
+ console.log(chalk.cyan("šŸ’” To start the Next.js dev server:"));
44
+ console.log(chalk.white(` cd ${path.relative(process.cwd(), this.outputDir)}`));
45
+ console.log(chalk.white(" npm install && npm run dev"));
46
+ }
47
+ async createNextJSStructure() {
48
+ const structure = {
49
+ "package.json": this.generatePackageJson(),
50
+ "next.config.ts": this.generateNextConfig(),
51
+ "tsconfig.json": this.generateTSConfig(),
52
+ "app/layout.tsx": await this.generateRootLayout(),
53
+ "app/page.tsx": this.generateHomePage(),
54
+ "app/not-found.tsx": this.generateNotFoundPage(),
55
+ "app/theme.ts": this.generateTheme(),
56
+ "components/layout/Icon.tsx": this.generateIcon(),
57
+ "components/layout/Pictograms.tsx": this.generatePictograms(),
58
+ "components/layout/Typography.ts": this.generateTypography(),
59
+ "components/layout/Header.tsx": this.generateHeader(),
60
+ "components/layout/Footer.tsx": this.generateFooter(),
61
+ "components/layout/ThemeToggle.tsx": this.generateThemeToggle(),
62
+ "components/layout/SharedStyled.ts": this.generateSharedStyled(),
63
+ "components/layout/Code.tsx": this.generateCode(),
64
+ "components/layout/DocsComponents.tsx": this.generateDocsComponents(),
65
+ "components/ClickOutside.ts": this.generateClickOutside(),
66
+ "components/Docs.tsx": this.generateDocs(),
67
+ "components/SideBar.tsx": this.generateSideBar(),
68
+ "utils/orderNavItems.ts": this.generateOrderNavItems(),
69
+ };
70
+ for (const [filePath, content] of Object.entries(structure)) {
71
+ const fullPath = path.join(this.outputDir, filePath);
72
+ await fs.ensureDir(path.dirname(fullPath));
73
+ await fs.writeFile(fullPath, String(content), "utf8");
74
+ }
75
+ }
76
+ async startWatching() {
77
+ console.log(chalk.yellow(`šŸ‘€ Watching for changes in: ${this.watchDir}`));
78
+ this.watcher = chokidar.watch("**/*.mdx", {
79
+ cwd: this.watchDir,
80
+ persistent: true,
81
+ ignoreInitial: false,
82
+ });
83
+ this.watcher
84
+ .on("add", (filePath) => this.handleFileChange("added", filePath))
85
+ .on("change", (filePath) => this.handleFileChange("changed", filePath))
86
+ .on("unlink", (filePath) => this.handleFileDelete(filePath));
87
+ }
88
+ async handleFileChange(action, filePath) {
89
+ console.log(chalk.cyan(`šŸ“ File ${action}: ${filePath}`));
90
+ const fullPath = path.join(this.watchDir, filePath);
91
+ try {
92
+ const content = await fs.readFile(fullPath, "utf8");
93
+ const { data: frontmatter, content: mdxContent } = matter(content);
94
+ if (filePath === "index.mdx" || filePath === "./index.mdx") {
95
+ console.log(chalk.blue("šŸ  Updating homepage with index.mdx content"));
96
+ await this.updatePagesIndex();
97
+ await this.updateRootLayout();
98
+ }
99
+ else {
100
+ const mdxFile = {
101
+ path: filePath,
102
+ content: mdxContent,
103
+ frontmatter,
104
+ slug: this.generateSlug(filePath),
105
+ };
106
+ await this.generatePageFromMDX(mdxFile);
107
+ await this.updatePagesIndex();
108
+ await this.updateRootLayout();
109
+ }
110
+ console.log(chalk.green(`āœ… Generated page for: ${filePath}`));
111
+ }
112
+ catch (error) {
113
+ console.error(chalk.red(`āŒ Error processing ${filePath}:`), error);
114
+ }
115
+ }
116
+ async handleFileDelete(filePath) {
117
+ console.log(chalk.red(`šŸ—‘ļø File deleted: ${filePath}`));
118
+ try {
119
+ if (filePath === "index.mdx" || filePath === "./index.mdx") {
120
+ console.log(chalk.blue("šŸ  Updating homepage - index.mdx deleted"));
121
+ await this.updatePagesIndex();
122
+ await this.updateRootLayout();
123
+ }
124
+ else {
125
+ const slug = this.generateSlug(filePath);
126
+ const pagePath = path.join(this.outputDir, "app", slug);
127
+ await fs.remove(pagePath);
128
+ await this.updatePagesIndex();
129
+ await this.updateRootLayout();
130
+ }
131
+ console.log(chalk.green(`āœ… Removed page for: ${filePath}`));
132
+ }
133
+ catch (error) {
134
+ console.error(chalk.red(`āŒ Error removing page for ${filePath}:`), error);
135
+ }
136
+ }
137
+ async processAllMDXFiles() {
138
+ const files = await this.getAllMDXFiles();
139
+ for (const file of files) {
140
+ await this.handleFileChange("processed", file);
141
+ }
142
+ }
143
+ async getAllMDXFiles() {
144
+ const files = [];
145
+ async function scanDir(dir, relativePath = "") {
146
+ const entries = await fs.readdir(dir, { withFileTypes: true });
147
+ for (const entry of entries) {
148
+ const fullPath = path.join(dir, entry.name);
149
+ const relPath = path.join(relativePath, entry.name);
150
+ if (entry.isDirectory()) {
151
+ await scanDir(fullPath, relPath);
152
+ }
153
+ else if (entry.name.endsWith(".mdx")) {
154
+ files.push(relPath);
155
+ }
156
+ }
157
+ }
158
+ await scanDir(this.watchDir);
159
+ return files;
160
+ }
161
+ generateSlug(filePath) {
162
+ if (filePath === "index.mdx" || filePath === "./index.mdx") {
163
+ return "";
164
+ }
165
+ return filePath
166
+ .replace(/\.mdx$/, "")
167
+ .replace(/\\/g, "/")
168
+ .replace(/[^a-zA-Z0-9\/\-_]/g, "-")
169
+ .toLowerCase();
170
+ }
171
+ async generatePageFromMDX(mdxFile) {
172
+ const files = await this.getAllMDXFiles();
173
+ const pages = [];
174
+ for (const file of files) {
175
+ const fullPath = path.join(this.watchDir, file);
176
+ const content = await fs.readFile(fullPath, "utf8");
177
+ const { data: frontmatter } = matter(content);
178
+ pages.push({
179
+ slug: this.generateSlug(file),
180
+ title: frontmatter.title || "Untitled",
181
+ description: frontmatter.description || "",
182
+ date: frontmatter.date || null,
183
+ category: frontmatter.category || "",
184
+ path: file,
185
+ categoryOrder: frontmatter.categoryOrder || 0,
186
+ order: frontmatter.order || 0,
187
+ });
188
+ }
189
+ const pageContent = `import { Metadata } from "next";
190
+ import { Docs } from "@/components/Docs";
191
+
192
+ const content = \`${mdxFile.content.replace(/`/g, "\\`")}\`;
193
+
194
+ export const metadata: Metadata = {
195
+ title: '${mdxFile.frontmatter.title || "Generated with Doccupine"}',
196
+ description: '${mdxFile.frontmatter.description || "Automatically generated from MDX files using Doccupine"}',
197
+ };
198
+
199
+ export default function Page() {
200
+ return (
201
+ <Docs content={content} />
202
+ );
203
+ }`;
204
+ const pagePath = path.join(this.outputDir, "app", mdxFile.slug, "page.tsx");
205
+ await fs.ensureDir(path.dirname(pagePath));
206
+ await fs.writeFile(pagePath, pageContent, "utf8");
207
+ }
208
+ async updatePagesIndex() {
209
+ const files = await this.getAllMDXFiles();
210
+ let indexMDX = null;
211
+ for (const file of files) {
212
+ const fullPath = path.join(this.watchDir, file);
213
+ const content = await fs.readFile(fullPath, "utf8");
214
+ const { data: frontmatter, content: mdxContent } = matter(content);
215
+ if (file === "index.mdx" || file === "./index.mdx") {
216
+ indexMDX = {
217
+ content: mdxContent,
218
+ frontmatter,
219
+ title: frontmatter.title || "Welcome",
220
+ category: frontmatter.category || "",
221
+ description: frontmatter.description || "",
222
+ categoryOrder: frontmatter.categoryOrder || 0,
223
+ order: frontmatter.order || 0,
224
+ };
225
+ }
226
+ }
227
+ const indexContent = `import { Metadata } from "next";
228
+ import { Docs } from "@/components/Docs";
229
+
230
+ ${indexMDX ? `const indexContent = \`${indexMDX.content.replace(/`/g, "\\`")}\`;` : `const indexContent = null;`}
231
+
232
+ ${indexMDX
233
+ ? `export const metadata: Metadata = {
234
+ title: '${indexMDX.title}',
235
+ description: '${indexMDX.description}',
236
+ };`
237
+ : `export const metadata: Metadata = {
238
+ title: 'Generated with Doccupine',
239
+ description: 'Automatically generated from MDX files using Doccupine',
240
+ };`}
241
+
242
+ export default function Home() {
243
+ return (
244
+ <Docs content={indexContent} />
245
+ );
246
+ }`;
247
+ await fs.writeFile(path.join(this.outputDir, "app", "page.tsx"), indexContent, "utf8");
248
+ }
249
+ // Configuration file generators
250
+ generatePackageJson() {
251
+ return JSON.stringify({
252
+ name: "doccupine",
253
+ version: "0.1.0",
254
+ private: true,
255
+ scripts: {
256
+ dev: "next dev",
257
+ build: "next build",
258
+ start: "next start",
259
+ lint: "next lint",
260
+ },
261
+ dependencies: {
262
+ next: "15.5.2",
263
+ react: "19.1.1",
264
+ "react-dom": "19.1.1",
265
+ },
266
+ devDependencies: {
267
+ "@types/node": "^24",
268
+ "@types/react": "^19",
269
+ "@types/react-dom": "^19",
270
+ "cherry-styled-components": "^0.1.0-43",
271
+ eslint: "^9",
272
+ "eslint-config-next": "15.5.2",
273
+ "lucide-react": "^0.542.0",
274
+ polished: "^4.3.1",
275
+ prettier: "^3.6.2",
276
+ "react-markdown": "^10.1.0",
277
+ "rehype-highlight": "^7.0.2",
278
+ "rehype-parse": "^9.0.1",
279
+ "rehype-stringify": "^10.0.1",
280
+ "remark-gfm": "^4.0.1",
281
+ "styled-components": "^6.1.19",
282
+ typescript: "^5",
283
+ },
284
+ }, null, 2);
285
+ }
286
+ generateNextConfig() {
287
+ return `import type { NextConfig } from "next";
288
+
289
+ const nextConfig: NextConfig = {
290
+ compiler: {
291
+ styledComponents: true,
292
+ },
293
+ transpilePackages: ["lucide-react"],
294
+ };
295
+
296
+ export default nextConfig;
297
+ `;
298
+ }
299
+ generateTSConfig() {
300
+ return JSON.stringify({
301
+ compilerOptions: {
302
+ target: "es5",
303
+ lib: ["dom", "dom.iterable", "es6"],
304
+ allowJs: true,
305
+ skipLibCheck: true,
306
+ strict: true,
307
+ noEmit: true,
308
+ esModuleInterop: true,
309
+ module: "esnext",
310
+ moduleResolution: "bundler",
311
+ resolveJsonModule: true,
312
+ isolatedModules: true,
313
+ jsx: "preserve",
314
+ incremental: true,
315
+ plugins: [{ name: "next" }],
316
+ baseUrl: ".",
317
+ paths: {
318
+ "@/*": ["./*"],
319
+ },
320
+ },
321
+ include: [
322
+ "next-env.d.ts",
323
+ "**/*.ts",
324
+ "**/*.tsx",
325
+ ".next/types/**/*.ts",
326
+ ],
327
+ exclude: ["node_modules"],
328
+ }, null, 2);
329
+ }
330
+ async generateRootLayout() {
331
+ const files = await this.getAllMDXFiles();
332
+ const pages = [];
333
+ for (const file of files) {
334
+ const fullPath = path.join(this.watchDir, file);
335
+ const content = await fs.readFile(fullPath, "utf8");
336
+ const { data: frontmatter } = matter(content);
337
+ pages.push({
338
+ slug: this.generateSlug(file),
339
+ title: frontmatter.title || "Untitled",
340
+ description: frontmatter.description || "",
341
+ date: frontmatter.date || null,
342
+ category: frontmatter.category || "",
343
+ path: file,
344
+ categoryOrder: frontmatter.categoryOrder || 0,
345
+ order: frontmatter.order || 0,
346
+ });
347
+ }
348
+ return layoutTemplate(pages);
349
+ }
350
+ async updateRootLayout() {
351
+ const layoutContent = await this.generateRootLayout();
352
+ await fs.writeFile(path.join(this.outputDir, "app", "layout.tsx"), layoutContent, "utf8");
353
+ }
354
+ generateHomePage() {
355
+ return homeTemplate;
356
+ }
357
+ generateNotFoundPage() {
358
+ return notFoundTemplate;
359
+ }
360
+ generateTheme() {
361
+ return themeTemplate;
362
+ }
363
+ generateIcon() {
364
+ return iconTemplate;
365
+ }
366
+ generatePictograms() {
367
+ return pictogramsTemplate;
368
+ }
369
+ generateClickOutside() {
370
+ return clickOutsideTemplate;
371
+ }
372
+ generateTypography() {
373
+ return typographyTemplate;
374
+ }
375
+ generateHeader() {
376
+ return headerTemplate;
377
+ }
378
+ generateFooter() {
379
+ return footerTemplate;
380
+ }
381
+ generateThemeToggle() {
382
+ return themeToggleTemplate;
383
+ }
384
+ generateSharedStyled() {
385
+ return sharedStyledTemplate;
386
+ }
387
+ generateCode() {
388
+ return codeTemplate;
389
+ }
390
+ generateDocsComponents() {
391
+ return docsComponentsTemplate;
392
+ }
393
+ generateDocs() {
394
+ return docsTemplate;
395
+ }
396
+ generateSideBar() {
397
+ return sideBarTemplate;
398
+ }
399
+ generateOrderNavItems() {
400
+ return orderNavItemsTemplate;
401
+ }
402
+ stop() {
403
+ if (this.watcher) {
404
+ this.watcher.close();
405
+ console.log(chalk.yellow("šŸ‘‹ Stopped watching for changes"));
406
+ }
407
+ }
408
+ }
409
+ // CLI Commands
410
+ program
411
+ .name("doccupine")
412
+ .description("Watch MDX files and generate Next.js documentation pages automatically")
413
+ .version("0.0.1");
414
+ program
415
+ .command("watch", { isDefault: true })
416
+ .description("Watch a directory for MDX changes and generate Next.js app")
417
+ .option("--port <port>", "Port for Next.js dev server", "3000")
418
+ .option("--verbose", "Show verbose output")
419
+ .action(async (options) => {
420
+ const questions = [
421
+ {
422
+ type: "text",
423
+ name: "watchDir",
424
+ message: "Enter directory to watch for MDX files:",
425
+ initial: "docs",
426
+ },
427
+ {
428
+ type: "text",
429
+ name: "outputDir",
430
+ message: "Enter output directory for Next.js app:",
431
+ initial: "nextjs-app",
432
+ },
433
+ ];
434
+ const { watchDir: watchDirInput, outputDir: outputDirInput } = (await prompts(questions));
435
+ const watchDir = path.resolve(process.cwd(), watchDirInput);
436
+ const outputDir = path.resolve(process.cwd(), outputDirInput);
437
+ const generator = new MDXToNextJSGenerator(watchDir, outputDir);
438
+ await generator.init();
439
+ let devServer = null;
440
+ console.log(chalk.blue("šŸ“¦ Installing dependencies..."));
441
+ const { spawn } = await import("child_process");
442
+ // Install dependencies first
443
+ const install = spawn("npm", ["install"], {
444
+ cwd: outputDir,
445
+ stdio: "pipe",
446
+ });
447
+ await new Promise((resolve, reject) => {
448
+ install.on("close", (code) => {
449
+ if (code === 0) {
450
+ console.log(chalk.green("āœ… Dependencies installed"));
451
+ resolve(void 0);
452
+ }
453
+ else {
454
+ reject(new Error(`npm install failed with code ${code}`));
455
+ }
456
+ });
457
+ install.on("error", reject);
458
+ });
459
+ // Start dev server
460
+ console.log(chalk.blue(`šŸš€ Starting Next.js dev server on port ${options.port}...`));
461
+ devServer = spawn("npm", ["run", "dev", "--", "--port", options.port], {
462
+ cwd: outputDir,
463
+ stdio: ["ignore", "pipe", "pipe"],
464
+ });
465
+ devServer.stdout.on("data", (data) => {
466
+ const output = data.toString();
467
+ if (output.includes("Ready") || output.includes("started")) {
468
+ console.log(chalk.green(`🌐 Next.js ready at http://localhost:${options.port}`));
469
+ }
470
+ // Filter out noisy Next.js logs, show important ones
471
+ if (output.includes("compiled") ||
472
+ output.includes("error") ||
473
+ output.includes("Ready")) {
474
+ process.stdout.write(chalk.gray("[Next.js] ") + output);
475
+ }
476
+ });
477
+ if (options.verbose) {
478
+ devServer.stderr.on("data", (data) => {
479
+ process.stderr.write(chalk.red("[Next.js Error] ") + data.toString());
480
+ });
481
+ }
482
+ devServer.on("error", (error) => {
483
+ console.error(chalk.red("āŒ Error starting dev server:"), error);
484
+ });
485
+ await generator.startWatching();
486
+ // Handle graceful shutdown
487
+ process.on("SIGINT", () => {
488
+ console.log(chalk.yellow("\nšŸ›‘ Shutting down..."));
489
+ generator.stop();
490
+ if (devServer) {
491
+ devServer.kill();
492
+ }
493
+ process.exit(0);
494
+ });
495
+ console.log(chalk.green("šŸŽ‰ Generator is running! Press Ctrl+C to stop."));
496
+ if (options.dev) {
497
+ console.log(chalk.cyan(`šŸ“ Edit your MDX files in: ${watchDir}`));
498
+ console.log(chalk.cyan(`🌐 View changes at: http://localhost:${options.port}`));
499
+ }
500
+ });
501
+ program
502
+ .command("build")
503
+ .description("One-time build of Next.js app from MDX files")
504
+ .action(async () => {
505
+ const questions = [
506
+ {
507
+ type: "text",
508
+ name: "watchDir",
509
+ message: "Enter directory to watch for MDX files:",
510
+ initial: "docs",
511
+ },
512
+ {
513
+ type: "text",
514
+ name: "outputDir",
515
+ message: "Enter output directory for Next.js app:",
516
+ initial: "nextjs-app",
517
+ },
518
+ ];
519
+ const { watchDir: watchDirInput, outputDir: outputDirInput } = (await prompts(questions));
520
+ const watchDir = path.resolve(process.cwd(), watchDirInput);
521
+ const outputDir = path.resolve(process.cwd(), outputDirInput);
522
+ const generator = new MDXToNextJSGenerator(watchDir, outputDir);
523
+ await generator.init();
524
+ console.log(chalk.green("šŸŽ‰ Build complete!"));
525
+ });
526
+ program.parse();
@@ -0,0 +1 @@
1
+ export declare const clickOutsideTemplate = "\nimport { RefObject, useEffect } from \"react\";\n\nexport function useOnClickOutside(\n refs: RefObject<HTMLElement | null>[],\n cb: () => void,\n) {\n useEffect(() => {\n function handleClickOutside(event: MouseEvent) {\n if (\n refs &&\n refs\n .map(\n (ref) =>\n ref && ref.current && ref.current.contains(event.target as Node),\n )\n .every((i) => i === false)\n ) {\n cb();\n }\n }\n document.addEventListener(\"mousedown\", handleClickOutside);\n return () => {\n document.removeEventListener(\"mousedown\", handleClickOutside);\n };\n }, [refs, cb]);\n}\n";
@@ -0,0 +1,28 @@
1
+ export const clickOutsideTemplate = `
2
+ import { RefObject, useEffect } from "react";
3
+
4
+ export function useOnClickOutside(
5
+ refs: RefObject<HTMLElement | null>[],
6
+ cb: () => void,
7
+ ) {
8
+ useEffect(() => {
9
+ function handleClickOutside(event: MouseEvent) {
10
+ if (
11
+ refs &&
12
+ refs
13
+ .map(
14
+ (ref) =>
15
+ ref && ref.current && ref.current.contains(event.target as Node),
16
+ )
17
+ .every((i) => i === false)
18
+ ) {
19
+ cb();
20
+ }
21
+ }
22
+ document.addEventListener("mousedown", handleClickOutside);
23
+ return () => {
24
+ document.removeEventListener("mousedown", handleClickOutside);
25
+ };
26
+ }, [refs, cb]);
27
+ }
28
+ `;
@@ -0,0 +1 @@
1
+ export declare const codeTemplate = "\"use client\";\n\"use client\";\nimport styled from \"styled-components\";\nimport { Theme, styledCode } from \"cherry-styled-components/src/lib\";\nimport { rgba } from \"polished\";\nimport { unified } from \"unified\";\nimport rehypeParse from \"rehype-parse\";\nimport rehypeHighlight from \"rehype-highlight\";\nimport rehypeStringify from \"rehype-stringify\";\nimport { editableContent } from \"@/app/components/layout/SharedStyled\";\n\ninterface CodeProps extends React.HTMLAttributes<HTMLDivElement> {\n code: string;\n language?: string;\n theme?: Theme;\n}\n\nconst CodeWrapper = styled.span<{ theme: Theme }>`\n position: relative;\n z-index: 2;\n display: block;\n width: 100%;\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n border: solid 1px\n ${({ theme }) =>\n theme.isDark ? rgba(theme.colors.dark, 0.2) : rgba(theme.colors.dark, 0)};\n`;\n\nconst TopBar = styled.div<{ theme: Theme }>`\n background: #0d1117;\n border-top-left-radius: ${({ theme }) => theme.spacing.radius.lg};\n border-top-right-radius: ${({ theme }) => theme.spacing.radius.lg};\n border-bottom: solid 1px ${rgba(\"#ffffff\", 0.1)};\n height: 33px;\n width: 100%;\n display: flex;\n justify-content: flex-start;\n gap: 5px;\n padding: 10px;\n`;\n\nconst Dot = styled.span<{ theme: Theme }>`\n margin: auto 0;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: ${rgba(\"#ffffff\", 0.1)};\n`;\n\nconst Body = styled.div<{ theme: Theme }>`\n background: #0d1117;\n border-bottom-left-radius: ${({ theme }) => theme.spacing.radius.lg};\n border-bottom-right-radius: ${({ theme }) => theme.spacing.radius.lg};\n color: #ffffff;\n padding: 20px;\n font-family: ${({ theme }) => theme.fonts.mono};\n text-align: left;\n overflow-x: auto;\n overflow-y: auto;\n max-height: calc(100svh - 400px);\n ${({ theme }) => styledCode(theme)};\n\n &[contenteditable=\"true\"] {\n ${editableContent};\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n\n & .hljs {\n color: #c9d1d9;\n background: #0d1117;\n }\n\n & .hljs-doctag,\n & .hljs-keyword,\n & .hljs-meta .hljs-keyword,\n & .hljs-template-tag,\n & .hljs-template-variable,\n & .hljs-type,\n & .hljs-variable.language_ {\n color: #ff7b72;\n }\n\n & .hljs-title,\n & .hljs-title.class_,\n & .hljs-title.class_.inherited__,\n & .hljs-title.function_ {\n color: #d2a8ff;\n }\n\n & .hljs-attr,\n & .hljs-attribute,\n & .hljs-literal,\n & .hljs-meta,\n & .hljs-number,\n & .hljs-operator,\n & .hljs-selector-attr,\n & .hljs-selector-class,\n & .hljs-selector-id,\n & .hljs-variable {\n color: #79c0ff;\n }\n\n & .hljs-meta .hljs-string,\n & .hljs-regexp,\n & .hljs-string {\n color: #a5d6ff;\n }\n\n & .hljs-built_in,\n & .hljs-symbol {\n color: #ffa657;\n }\n\n & .hljs-code,\n & .hljs-comment,\n & .hljs-formula {\n color: #8b949e;\n }\n\n & .hljs-name,\n & .hljs-quote,\n & .hljs-selector-pseudo,\n & .hljs-selector-tag {\n color: #7ee787;\n }\n\n & .hljs-subst {\n color: #c9d1d9;\n }\n\n & .hljs-section {\n color: #1f6feb;\n font-weight: 700;\n }\n\n & .hljs-bullet {\n color: #f2cc60;\n }\n\n & .hljs-emphasis {\n color: #c9d1d9;\n font-style: italic;\n }\n\n & .hljs-strong {\n color: #c9d1d9;\n font-weight: 700;\n }\n\n & .hljs-addition {\n color: #aff5b4;\n background-color: #033a16;\n }\n\n & .hljs-deletion {\n color: #ffdcd7;\n background-color: #67060c;\n }\n`;\n\nconst highlightCode = (code: string, language: string): string => {\n const result = unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeHighlight, {\n detect: true,\n ignoreMissing: true,\n })\n .use(rehypeStringify)\n .processSync(\n `<pre><code class=\"language-${language}\">${code}</code></pre>`,\n );\n\n return String(result);\n};\n\nfunction Code({ code, language = \"javascript\", ...props }: CodeProps) {\n const highlightedCode = highlightCode(code, language);\n return (\n <CodeWrapper>\n <TopBar>\n <Dot />\n <Dot />\n <Dot />\n </TopBar>\n <Body dangerouslySetInnerHTML={{ __html: highlightedCode }} {...props} />\n </CodeWrapper>\n );\n}\n\nexport { Code };\n";