specra 0.1.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/LICENSE.MD +21 -0
- package/README.md +157 -0
- package/dist/app/api/mdx-watch/route.d.mts +12 -0
- package/dist/app/api/mdx-watch/route.d.ts +12 -0
- package/dist/app/api/mdx-watch/route.js +98 -0
- package/dist/app/api/mdx-watch/route.js.map +1 -0
- package/dist/app/api/mdx-watch/route.mjs +71 -0
- package/dist/app/api/mdx-watch/route.mjs.map +1 -0
- package/dist/app/docs-page.d.mts +32 -0
- package/dist/app/docs-page.d.ts +32 -0
- package/dist/app/docs-page.js +4072 -0
- package/dist/app/docs-page.js.map +1 -0
- package/dist/app/docs-page.mjs +14 -0
- package/dist/app/docs-page.mjs.map +1 -0
- package/dist/app/layout.css +297 -0
- package/dist/app/layout.css.map +1 -0
- package/dist/app/layout.d.mts +19 -0
- package/dist/app/layout.d.ts +19 -0
- package/dist/app/layout.js +112 -0
- package/dist/app/layout.js.map +1 -0
- package/dist/app/layout.mjs +13 -0
- package/dist/app/layout.mjs.map +1 -0
- package/dist/chunk-DR4EPLMT.mjs +1013 -0
- package/dist/chunk-DR4EPLMT.mjs.map +1 -0
- package/dist/chunk-INL2EC72.mjs +170 -0
- package/dist/chunk-INL2EC72.mjs.map +1 -0
- package/dist/chunk-IZFGEAD6.mjs +61 -0
- package/dist/chunk-IZFGEAD6.mjs.map +1 -0
- package/dist/chunk-KTRWWAGL.mjs +50 -0
- package/dist/chunk-KTRWWAGL.mjs.map +1 -0
- package/dist/chunk-MZJHJ6BV.mjs +21 -0
- package/dist/chunk-MZJHJ6BV.mjs.map +1 -0
- package/dist/chunk-NXRIAL7T.mjs +3119 -0
- package/dist/chunk-NXRIAL7T.mjs.map +1 -0
- package/dist/components/index.d.mts +822 -0
- package/dist/components/index.d.ts +822 -0
- package/dist/components/index.js +3738 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +3627 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/index.css +297 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +545 -0
- package/dist/index.d.ts +545 -0
- package/dist/index.js +4648 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +347 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/index.d.mts +798 -0
- package/dist/lib/index.d.ts +798 -0
- package/dist/lib/index.js +1301 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/index.mjs +89 -0
- package/dist/lib/index.mjs.map +1 -0
- package/package.json +119 -0
- package/src/app/api/mdx-watch/route.ts +86 -0
- package/src/app/docs-page.tsx +212 -0
- package/src/app/layout.tsx +74 -0
- package/src/components/docs/accordion.tsx +53 -0
- package/src/components/docs/api/api-endpoint.tsx +59 -0
- package/src/components/docs/api/api-params.tsx +43 -0
- package/src/components/docs/api/api-playground.tsx +233 -0
- package/src/components/docs/api/api-reference.tsx +291 -0
- package/src/components/docs/api/api-response.tsx +48 -0
- package/src/components/docs/api/index.ts +5 -0
- package/src/components/docs/badge.tsx +22 -0
- package/src/components/docs/breadcrumb.tsx +51 -0
- package/src/components/docs/callout.tsx +109 -0
- package/src/components/docs/card.tsx +84 -0
- package/src/components/docs/category-index.tsx +112 -0
- package/src/components/docs/code-block.tsx +129 -0
- package/src/components/docs/columns.tsx +45 -0
- package/src/components/docs/componentTextProps.ts +85 -0
- package/src/components/docs/dev-mode-badge.tsx +35 -0
- package/src/components/docs/doc-layout-wrapper.tsx +54 -0
- package/src/components/docs/doc-layout.tsx +111 -0
- package/src/components/docs/doc-loading.tsx +15 -0
- package/src/components/docs/doc-metadata.tsx +55 -0
- package/src/components/docs/doc-navigation.tsx +62 -0
- package/src/components/docs/doc-tags.tsx +25 -0
- package/src/components/docs/draft-badge.tsx +10 -0
- package/src/components/docs/footer.tsx +47 -0
- package/src/components/docs/frame.tsx +22 -0
- package/src/components/docs/header.tsx +122 -0
- package/src/components/docs/hot-reload-indicator.tsx +77 -0
- package/src/components/docs/icon.tsx +70 -0
- package/src/components/docs/image-card.tsx +95 -0
- package/src/components/docs/image.tsx +73 -0
- package/src/components/docs/index.ts +48 -0
- package/src/components/docs/math.tsx +46 -0
- package/src/components/docs/mdx-components.tsx +166 -0
- package/src/components/docs/mdx-hot-reload.tsx +37 -0
- package/src/components/docs/mermaid.tsx +77 -0
- package/src/components/docs/mobile-doc-layout.tsx +115 -0
- package/src/components/docs/not-found-content.tsx +55 -0
- package/src/components/docs/search-highlight.tsx +127 -0
- package/src/components/docs/search-modal.tsx +223 -0
- package/src/components/docs/sidebar-skeleton.tsx +39 -0
- package/src/components/docs/sidebar.tsx +323 -0
- package/src/components/docs/site-banner.tsx +92 -0
- package/src/components/docs/steps.tsx +29 -0
- package/src/components/docs/tab-context.tsx +28 -0
- package/src/components/docs/tab-groups.tsx +50 -0
- package/src/components/docs/table-of-contents.tsx +104 -0
- package/src/components/docs/tabs.tsx +63 -0
- package/src/components/docs/theme-toggle.tsx +39 -0
- package/src/components/docs/tooltip.tsx +37 -0
- package/src/components/docs/version-switcher.tsx +52 -0
- package/src/components/docs/video.tsx +80 -0
- package/src/components/global/index.ts +3 -0
- package/src/components/global/version-not-found.tsx +26 -0
- package/src/components/index.ts +8 -0
- package/src/components/theme-provider.tsx +11 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/index.ts +6 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/index.ts +41 -0
- package/src/lib/api-parser.types.ts +78 -0
- package/src/lib/api.types.ts +202 -0
- package/src/lib/category.ts +71 -0
- package/src/lib/config.server.ts +170 -0
- package/src/lib/config.ts +20 -0
- package/src/lib/config.types.ts +295 -0
- package/src/lib/dev-utils.ts +75 -0
- package/src/lib/index.ts +27 -0
- package/src/lib/mdx-cache.ts +200 -0
- package/src/lib/mdx.ts +402 -0
- package/src/lib/parsers/base-parser.ts +16 -0
- package/src/lib/parsers/index.ts +69 -0
- package/src/lib/parsers/openapi-parser.ts +251 -0
- package/src/lib/parsers/postman-parser.ts +301 -0
- package/src/lib/parsers/specra-parser.ts +24 -0
- package/src/lib/redirects.ts +40 -0
- package/src/lib/remark-code-meta.ts +23 -0
- package/src/lib/sidebar-utils.ts +188 -0
- package/src/lib/toc.ts +24 -0
- package/src/lib/utils.ts +36 -0
- package/src/specra.config.json +124 -0
- package/src/styles/globals.css +427 -0
package/LICENSE.MD
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 dalmasonto, arthur-kamau
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Specra
|
|
2
|
+
|
|
3
|
+
A modern documentation library for Next.js that makes it easy to create beautiful, feature-rich documentation sites.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📝 **MDX Support** - Write documentation in Markdown with React components
|
|
8
|
+
- 📚 **Multi-Version Docs** - Support multiple documentation versions seamlessly
|
|
9
|
+
- 🔌 **API Reference Generation** - Auto-generate API docs from OpenAPI, Postman, or Specra formats
|
|
10
|
+
- 🔍 **Full-Text Search** - Integrated MeiliSearch support for fast search
|
|
11
|
+
- 🎯 **Tab Groups** - Organize content into multiple navigation tabs
|
|
12
|
+
- 🌓 **Dark Mode** - Built-in theme switching with system preference detection
|
|
13
|
+
- 🎨 **Customizable** - Highly configurable with Tailwind CSS
|
|
14
|
+
- ⚡ **Fast** - Built on Next.js with optimized performance
|
|
15
|
+
- 📱 **Responsive** - Mobile-friendly design out of the box
|
|
16
|
+
- 🔥 **Hot Reload** - Instant updates during development
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install specra
|
|
22
|
+
# or
|
|
23
|
+
yarn add specra
|
|
24
|
+
# or
|
|
25
|
+
pnpm add specra
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Create a new Next.js app (if you don't have one)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx create-next-app@latest my-docs
|
|
34
|
+
cd my-docs
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Install Specra
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install specra
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 3. Set up your app structure
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
my-docs/
|
|
47
|
+
├── app/
|
|
48
|
+
│ ├── layout.tsx # Re-export from Specra
|
|
49
|
+
│ ├── page.tsx # Your landing page
|
|
50
|
+
│ └── docs/
|
|
51
|
+
│ └── [version]/
|
|
52
|
+
│ └── [...slug]/
|
|
53
|
+
│ └── page.tsx # Re-export from Specra
|
|
54
|
+
├── docs/ # Your MDX content
|
|
55
|
+
│ └── v1.0.0/
|
|
56
|
+
│ └── getting-started.mdx
|
|
57
|
+
├── public/
|
|
58
|
+
│ └── logo.png
|
|
59
|
+
└── specra.config.json # Specra configuration
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 4. Configure app/layout.tsx
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// app/layout.tsx
|
|
66
|
+
export { default } from 'specra/app/layout'
|
|
67
|
+
export { generateMetadata } from 'specra/app/layout'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 5. Configure app/docs/[version]/[...slug]/page.tsx
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// app/docs/[version]/[...slug]/page.tsx
|
|
74
|
+
export { default } from 'specra/app/docs-page'
|
|
75
|
+
export {
|
|
76
|
+
generateStaticParams,
|
|
77
|
+
generateMetadata
|
|
78
|
+
} from 'specra/app/docs-page'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 6. Create specra.config.json
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"site": {
|
|
86
|
+
"title": "My Documentation",
|
|
87
|
+
"description": "Awesome docs built with Specra",
|
|
88
|
+
"url": "https://docs.example.com",
|
|
89
|
+
"logo": "/logo.png"
|
|
90
|
+
},
|
|
91
|
+
"theme": {
|
|
92
|
+
"defaultMode": "system",
|
|
93
|
+
"primaryColor": "#0070f3"
|
|
94
|
+
},
|
|
95
|
+
"navigation": {
|
|
96
|
+
"sidebar": true,
|
|
97
|
+
"breadcrumbs": true
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 7. Create your first doc
|
|
103
|
+
|
|
104
|
+
```mdx
|
|
105
|
+
---
|
|
106
|
+
title: Getting Started
|
|
107
|
+
description: Learn how to get started with our platform
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
# Getting Started
|
|
111
|
+
|
|
112
|
+
Welcome to the documentation!
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 8. Import globals.css
|
|
116
|
+
|
|
117
|
+
Add to your `app/globals.css`:
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
@import 'specra/styles';
|
|
121
|
+
|
|
122
|
+
/* Your custom styles */
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 9. Run development server
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm run dev
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Visit `http://localhost:3000/docs/v1.0.0/getting-started` to see your docs!
|
|
132
|
+
|
|
133
|
+
## Configuration
|
|
134
|
+
|
|
135
|
+
See [Configuration Guide](https://docs.specra.dev/configuration) for full documentation.
|
|
136
|
+
|
|
137
|
+
## Upgrading
|
|
138
|
+
|
|
139
|
+
Simply update the package to get latest features and bug fixes:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npm update specra
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Your content and configuration stay the same - only the SDK updates!
|
|
146
|
+
|
|
147
|
+
## Why Specra?
|
|
148
|
+
|
|
149
|
+
Specra is designed to be the easiest way to create documentation for your projects. It handles all the complex parts (versioning, search, API references) while letting you focus on writing great content.
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT
|
|
154
|
+
|
|
155
|
+
## Authors
|
|
156
|
+
|
|
157
|
+
dalmasonto, arthur-kamau
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
|
|
3
|
+
declare const dynamic = "force-dynamic";
|
|
4
|
+
declare const runtime = "nodejs";
|
|
5
|
+
/**
|
|
6
|
+
* API route for watching MDX file changes in development
|
|
7
|
+
* Provides Server-Sent Events (SSE) stream for hot reloading
|
|
8
|
+
* Only available in development mode
|
|
9
|
+
*/
|
|
10
|
+
declare function GET(request: NextRequest): Promise<Response>;
|
|
11
|
+
|
|
12
|
+
export { GET, dynamic, runtime };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NextRequest } from 'next/server';
|
|
2
|
+
|
|
3
|
+
declare const dynamic = "force-dynamic";
|
|
4
|
+
declare const runtime = "nodejs";
|
|
5
|
+
/**
|
|
6
|
+
* API route for watching MDX file changes in development
|
|
7
|
+
* Provides Server-Sent Events (SSE) stream for hot reloading
|
|
8
|
+
* Only available in development mode
|
|
9
|
+
*/
|
|
10
|
+
declare function GET(request: NextRequest): Promise<Response>;
|
|
11
|
+
|
|
12
|
+
export { GET, dynamic, runtime };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/app/api/mdx-watch/route.ts
|
|
21
|
+
var route_exports = {};
|
|
22
|
+
__export(route_exports, {
|
|
23
|
+
GET: () => GET,
|
|
24
|
+
dynamic: () => dynamic,
|
|
25
|
+
runtime: () => runtime
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(route_exports);
|
|
28
|
+
var import_fs = require("fs");
|
|
29
|
+
var import_path = require("path");
|
|
30
|
+
var dynamic = "force-dynamic";
|
|
31
|
+
var runtime = "nodejs";
|
|
32
|
+
async function GET(request) {
|
|
33
|
+
if (process.env.NODE_ENV !== "development") {
|
|
34
|
+
return new Response("Not available in production", { status: 404 });
|
|
35
|
+
}
|
|
36
|
+
const encoder = new TextEncoder();
|
|
37
|
+
const stream = new ReadableStream({
|
|
38
|
+
start(controller) {
|
|
39
|
+
const connectMsg = `data: ${JSON.stringify({ type: "connected" })}
|
|
40
|
+
|
|
41
|
+
`;
|
|
42
|
+
controller.enqueue(encoder.encode(connectMsg));
|
|
43
|
+
const docsPath = (0, import_path.join)(process.cwd(), "docs");
|
|
44
|
+
const watcher = (0, import_fs.watch)(
|
|
45
|
+
docsPath,
|
|
46
|
+
{ recursive: true },
|
|
47
|
+
(eventType, filename) => {
|
|
48
|
+
if (!filename) return;
|
|
49
|
+
if (filename.endsWith(".mdx") || filename.endsWith(".json")) {
|
|
50
|
+
console.log(`[MDX Watch] ${eventType}: ${filename}`);
|
|
51
|
+
const message = `data: ${JSON.stringify({
|
|
52
|
+
type: "change",
|
|
53
|
+
file: filename,
|
|
54
|
+
eventType
|
|
55
|
+
})}
|
|
56
|
+
|
|
57
|
+
`;
|
|
58
|
+
try {
|
|
59
|
+
controller.enqueue(encoder.encode(message));
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("[MDX Watch] Error sending message:", error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
request.signal.addEventListener("abort", () => {
|
|
67
|
+
console.log("[MDX Watch] Client disconnected");
|
|
68
|
+
watcher.close();
|
|
69
|
+
try {
|
|
70
|
+
controller.close();
|
|
71
|
+
} catch (error) {
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
watcher.on("error", (error) => {
|
|
75
|
+
console.error("[MDX Watch] Watcher error:", error);
|
|
76
|
+
watcher.close();
|
|
77
|
+
try {
|
|
78
|
+
controller.close();
|
|
79
|
+
} catch (e) {
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return new Response(stream, {
|
|
85
|
+
headers: {
|
|
86
|
+
"Content-Type": "text/event-stream",
|
|
87
|
+
"Cache-Control": "no-cache, no-transform",
|
|
88
|
+
"Connection": "keep-alive"
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
93
|
+
0 && (module.exports = {
|
|
94
|
+
GET,
|
|
95
|
+
dynamic,
|
|
96
|
+
runtime
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/app/api/mdx-watch/route.ts"],"sourcesContent":["import { NextRequest } from 'next/server'\nimport { watch } from 'fs'\nimport { join } from 'path'\n\nexport const dynamic = 'force-dynamic'\nexport const runtime = 'nodejs'\n\n/**\n * API route for watching MDX file changes in development\n * Provides Server-Sent Events (SSE) stream for hot reloading\n * Only available in development mode\n */\nexport async function GET(request: NextRequest) {\n // Only allow in development mode\n if (process.env.NODE_ENV !== 'development') {\n return new Response('Not available in production', { status: 404 })\n }\n\n const encoder = new TextEncoder()\n\n const stream = new ReadableStream({\n start(controller) {\n // Send initial connection message\n const connectMsg = `data: ${JSON.stringify({ type: 'connected' })}\\n\\n`\n controller.enqueue(encoder.encode(connectMsg))\n\n const docsPath = join(process.cwd(), 'docs')\n\n // Watch the docs directory recursively\n const watcher = watch(\n docsPath,\n { recursive: true },\n (eventType, filename) => {\n if (!filename) return\n\n // Only watch for .mdx and .json files (MDX files and category configs)\n if (filename.endsWith('.mdx') || filename.endsWith('.json')) {\n console.log(`[MDX Watch] ${eventType}: ${filename}`)\n\n const message = `data: ${JSON.stringify({\n type: 'change',\n file: filename,\n eventType\n })}\\n\\n`\n\n try {\n controller.enqueue(encoder.encode(message))\n } catch (error) {\n console.error('[MDX Watch] Error sending message:', error)\n }\n }\n }\n )\n\n // Handle client disconnect\n request.signal.addEventListener('abort', () => {\n console.log('[MDX Watch] Client disconnected')\n watcher.close()\n try {\n controller.close()\n } catch (error) {\n // Controller might already be closed\n }\n })\n\n // Handle errors\n watcher.on('error', (error) => {\n console.error('[MDX Watch] Watcher error:', error)\n watcher.close()\n try {\n controller.close()\n } catch (e) {\n // Controller might already be closed\n }\n })\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n 'Connection': 'keep-alive',\n },\n })\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,gBAAsB;AACtB,kBAAqB;AAEd,IAAM,UAAU;AAChB,IAAM,UAAU;AAOvB,eAAsB,IAAI,SAAsB;AAE9C,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,WAAO,IAAI,SAAS,+BAA+B,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpE;AAEA,QAAM,UAAU,IAAI,YAAY;AAEhC,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,MAAM,YAAY;AAEhB,YAAM,aAAa,SAAS,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC,CAAC;AAAA;AAAA;AACjE,iBAAW,QAAQ,QAAQ,OAAO,UAAU,CAAC;AAE7C,YAAM,eAAW,kBAAK,QAAQ,IAAI,GAAG,MAAM;AAG3C,YAAM,cAAU;AAAA,QACd;AAAA,QACA,EAAE,WAAW,KAAK;AAAA,QAClB,CAAC,WAAW,aAAa;AACvB,cAAI,CAAC,SAAU;AAGf,cAAI,SAAS,SAAS,MAAM,KAAK,SAAS,SAAS,OAAO,GAAG;AAC3D,oBAAQ,IAAI,eAAe,SAAS,KAAK,QAAQ,EAAE;AAEnD,kBAAM,UAAU,SAAS,KAAK,UAAU;AAAA,cACtC,MAAM;AAAA,cACN,MAAM;AAAA,cACN;AAAA,YACF,CAAC,CAAC;AAAA;AAAA;AAEF,gBAAI;AACF,yBAAW,QAAQ,QAAQ,OAAO,OAAO,CAAC;AAAA,YAC5C,SAAS,OAAO;AACd,sBAAQ,MAAM,sCAAsC,KAAK;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,cAAQ,OAAO,iBAAiB,SAAS,MAAM;AAC7C,gBAAQ,IAAI,iCAAiC;AAC7C,gBAAQ,MAAM;AACd,YAAI;AACF,qBAAW,MAAM;AAAA,QACnB,SAAS,OAAO;AAAA,QAEhB;AAAA,MACF,CAAC;AAGD,cAAQ,GAAG,SAAS,CAAC,UAAU;AAC7B,gBAAQ,MAAM,8BAA8B,KAAK;AACjD,gBAAQ,MAAM;AACd,YAAI;AACF,qBAAW,MAAM;AAAA,QACnB,SAAS,GAAG;AAAA,QAEZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO,IAAI,SAAS,QAAQ;AAAA,IAC1B,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// src/app/api/mdx-watch/route.ts
|
|
2
|
+
import { watch } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
var dynamic = "force-dynamic";
|
|
5
|
+
var runtime = "nodejs";
|
|
6
|
+
async function GET(request) {
|
|
7
|
+
if (process.env.NODE_ENV !== "development") {
|
|
8
|
+
return new Response("Not available in production", { status: 404 });
|
|
9
|
+
}
|
|
10
|
+
const encoder = new TextEncoder();
|
|
11
|
+
const stream = new ReadableStream({
|
|
12
|
+
start(controller) {
|
|
13
|
+
const connectMsg = `data: ${JSON.stringify({ type: "connected" })}
|
|
14
|
+
|
|
15
|
+
`;
|
|
16
|
+
controller.enqueue(encoder.encode(connectMsg));
|
|
17
|
+
const docsPath = join(process.cwd(), "docs");
|
|
18
|
+
const watcher = watch(
|
|
19
|
+
docsPath,
|
|
20
|
+
{ recursive: true },
|
|
21
|
+
(eventType, filename) => {
|
|
22
|
+
if (!filename) return;
|
|
23
|
+
if (filename.endsWith(".mdx") || filename.endsWith(".json")) {
|
|
24
|
+
console.log(`[MDX Watch] ${eventType}: ${filename}`);
|
|
25
|
+
const message = `data: ${JSON.stringify({
|
|
26
|
+
type: "change",
|
|
27
|
+
file: filename,
|
|
28
|
+
eventType
|
|
29
|
+
})}
|
|
30
|
+
|
|
31
|
+
`;
|
|
32
|
+
try {
|
|
33
|
+
controller.enqueue(encoder.encode(message));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error("[MDX Watch] Error sending message:", error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
request.signal.addEventListener("abort", () => {
|
|
41
|
+
console.log("[MDX Watch] Client disconnected");
|
|
42
|
+
watcher.close();
|
|
43
|
+
try {
|
|
44
|
+
controller.close();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
watcher.on("error", (error) => {
|
|
49
|
+
console.error("[MDX Watch] Watcher error:", error);
|
|
50
|
+
watcher.close();
|
|
51
|
+
try {
|
|
52
|
+
controller.close();
|
|
53
|
+
} catch (e) {
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return new Response(stream, {
|
|
59
|
+
headers: {
|
|
60
|
+
"Content-Type": "text/event-stream",
|
|
61
|
+
"Cache-Control": "no-cache, no-transform",
|
|
62
|
+
"Connection": "keep-alive"
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
GET,
|
|
68
|
+
dynamic,
|
|
69
|
+
runtime
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=route.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/app/api/mdx-watch/route.ts"],"sourcesContent":["import { NextRequest } from 'next/server'\nimport { watch } from 'fs'\nimport { join } from 'path'\n\nexport const dynamic = 'force-dynamic'\nexport const runtime = 'nodejs'\n\n/**\n * API route for watching MDX file changes in development\n * Provides Server-Sent Events (SSE) stream for hot reloading\n * Only available in development mode\n */\nexport async function GET(request: NextRequest) {\n // Only allow in development mode\n if (process.env.NODE_ENV !== 'development') {\n return new Response('Not available in production', { status: 404 })\n }\n\n const encoder = new TextEncoder()\n\n const stream = new ReadableStream({\n start(controller) {\n // Send initial connection message\n const connectMsg = `data: ${JSON.stringify({ type: 'connected' })}\\n\\n`\n controller.enqueue(encoder.encode(connectMsg))\n\n const docsPath = join(process.cwd(), 'docs')\n\n // Watch the docs directory recursively\n const watcher = watch(\n docsPath,\n { recursive: true },\n (eventType, filename) => {\n if (!filename) return\n\n // Only watch for .mdx and .json files (MDX files and category configs)\n if (filename.endsWith('.mdx') || filename.endsWith('.json')) {\n console.log(`[MDX Watch] ${eventType}: ${filename}`)\n\n const message = `data: ${JSON.stringify({\n type: 'change',\n file: filename,\n eventType\n })}\\n\\n`\n\n try {\n controller.enqueue(encoder.encode(message))\n } catch (error) {\n console.error('[MDX Watch] Error sending message:', error)\n }\n }\n }\n )\n\n // Handle client disconnect\n request.signal.addEventListener('abort', () => {\n console.log('[MDX Watch] Client disconnected')\n watcher.close()\n try {\n controller.close()\n } catch (error) {\n // Controller might already be closed\n }\n })\n\n // Handle errors\n watcher.on('error', (error) => {\n console.error('[MDX Watch] Watcher error:', error)\n watcher.close()\n try {\n controller.close()\n } catch (e) {\n // Controller might already be closed\n }\n })\n }\n })\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache, no-transform',\n 'Connection': 'keep-alive',\n },\n })\n}"],"mappings":";AACA,SAAS,aAAa;AACtB,SAAS,YAAY;AAEd,IAAM,UAAU;AAChB,IAAM,UAAU;AAOvB,eAAsB,IAAI,SAAsB;AAE9C,MAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,WAAO,IAAI,SAAS,+BAA+B,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpE;AAEA,QAAM,UAAU,IAAI,YAAY;AAEhC,QAAM,SAAS,IAAI,eAAe;AAAA,IAChC,MAAM,YAAY;AAEhB,YAAM,aAAa,SAAS,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC,CAAC;AAAA;AAAA;AACjE,iBAAW,QAAQ,QAAQ,OAAO,UAAU,CAAC;AAE7C,YAAM,WAAW,KAAK,QAAQ,IAAI,GAAG,MAAM;AAG3C,YAAM,UAAU;AAAA,QACd;AAAA,QACA,EAAE,WAAW,KAAK;AAAA,QAClB,CAAC,WAAW,aAAa;AACvB,cAAI,CAAC,SAAU;AAGf,cAAI,SAAS,SAAS,MAAM,KAAK,SAAS,SAAS,OAAO,GAAG;AAC3D,oBAAQ,IAAI,eAAe,SAAS,KAAK,QAAQ,EAAE;AAEnD,kBAAM,UAAU,SAAS,KAAK,UAAU;AAAA,cACtC,MAAM;AAAA,cACN,MAAM;AAAA,cACN;AAAA,YACF,CAAC,CAAC;AAAA;AAAA;AAEF,gBAAI;AACF,yBAAW,QAAQ,QAAQ,OAAO,OAAO,CAAC;AAAA,YAC5C,SAAS,OAAO;AACd,sBAAQ,MAAM,sCAAsC,KAAK;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,cAAQ,OAAO,iBAAiB,SAAS,MAAM;AAC7C,gBAAQ,IAAI,iCAAiC;AAC7C,gBAAQ,MAAM;AACd,YAAI;AACF,qBAAW,MAAM;AAAA,QACnB,SAAS,OAAO;AAAA,QAEhB;AAAA,MACF,CAAC;AAGD,cAAQ,GAAG,SAAS,CAAC,UAAU;AAC7B,gBAAQ,MAAM,8BAA8B,KAAK;AACjD,gBAAQ,MAAM;AACd,YAAI;AACF,qBAAW,MAAM;AAAA,QACnB,SAAS,GAAG;AAAA,QAEZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,SAAO,IAAI,SAAS,QAAQ;AAAA,IAC1B,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { Metadata } from 'next';
|
|
3
|
+
|
|
4
|
+
interface PageProps {
|
|
5
|
+
params: Promise<{
|
|
6
|
+
version: string;
|
|
7
|
+
slug: string[];
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Generate metadata for documentation pages
|
|
12
|
+
* This is exported so users can re-export it from their page.tsx
|
|
13
|
+
*/
|
|
14
|
+
declare function generateMetadata({ params }: PageProps): Promise<Metadata>;
|
|
15
|
+
/**
|
|
16
|
+
* Generate static params for all documentation pages
|
|
17
|
+
* This enables static generation at build time
|
|
18
|
+
*/
|
|
19
|
+
declare function generateStaticParams(): Promise<{
|
|
20
|
+
version: string;
|
|
21
|
+
slug: string[];
|
|
22
|
+
}[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Documentation page component
|
|
25
|
+
* Handles:
|
|
26
|
+
* - Regular documentation pages
|
|
27
|
+
* - Category index pages (with or without content)
|
|
28
|
+
* - 404 pages (when doc not found)
|
|
29
|
+
*/
|
|
30
|
+
declare function DocPage({ params }: PageProps): Promise<react_jsx_runtime.JSX.Element>;
|
|
31
|
+
|
|
32
|
+
export { DocPage as default, generateMetadata, generateStaticParams };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { Metadata } from 'next';
|
|
3
|
+
|
|
4
|
+
interface PageProps {
|
|
5
|
+
params: Promise<{
|
|
6
|
+
version: string;
|
|
7
|
+
slug: string[];
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Generate metadata for documentation pages
|
|
12
|
+
* This is exported so users can re-export it from their page.tsx
|
|
13
|
+
*/
|
|
14
|
+
declare function generateMetadata({ params }: PageProps): Promise<Metadata>;
|
|
15
|
+
/**
|
|
16
|
+
* Generate static params for all documentation pages
|
|
17
|
+
* This enables static generation at build time
|
|
18
|
+
*/
|
|
19
|
+
declare function generateStaticParams(): Promise<{
|
|
20
|
+
version: string;
|
|
21
|
+
slug: string[];
|
|
22
|
+
}[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Documentation page component
|
|
25
|
+
* Handles:
|
|
26
|
+
* - Regular documentation pages
|
|
27
|
+
* - Category index pages (with or without content)
|
|
28
|
+
* - 404 pages (when doc not found)
|
|
29
|
+
*/
|
|
30
|
+
declare function DocPage({ params }: PageProps): Promise<react_jsx_runtime.JSX.Element>;
|
|
31
|
+
|
|
32
|
+
export { DocPage as default, generateMetadata, generateStaticParams };
|