create-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.
Files changed (39) hide show
  1. package/LICENSE.MD +21 -0
  2. package/README.md +137 -0
  3. package/package.json +42 -0
  4. package/templates/minimal/README.md +132 -0
  5. package/templates/minimal/app/api/mdx-watch/route.ts +80 -0
  6. package/templates/minimal/app/docs/[version]/[...slug]/loading.tsx +7 -0
  7. package/templates/minimal/app/docs/[version]/[...slug]/page.tsx +212 -0
  8. package/templates/minimal/app/docs/[version]/not-found.tsx +10 -0
  9. package/templates/minimal/app/docs/[version]/page.tsx +27 -0
  10. package/templates/minimal/app/globals.css +1 -0
  11. package/templates/minimal/app/layout.tsx +89 -0
  12. package/templates/minimal/app/page.tsx +185 -0
  13. package/templates/minimal/docs/v1.0.0/about.mdx +57 -0
  14. package/templates/minimal/docs/v1.0.0/components/_category_.json +8 -0
  15. package/templates/minimal/docs/v1.0.0/components/callout.mdx +83 -0
  16. package/templates/minimal/docs/v1.0.0/components/code-block.mdx +103 -0
  17. package/templates/minimal/docs/v1.0.0/components/index.mdx +8 -0
  18. package/templates/minimal/docs/v1.0.0/components/tabs.mdx +92 -0
  19. package/templates/minimal/docs/v1.0.0/configuration.mdx +322 -0
  20. package/templates/minimal/docs/v1.0.0/features.mdx +197 -0
  21. package/templates/minimal/docs/v1.0.0/getting-started.mdx +183 -0
  22. package/templates/minimal/docs/v1.0.0/index.mdx +29 -0
  23. package/templates/minimal/middleware.ts +23 -0
  24. package/templates/minimal/next.config.default.mjs +36 -0
  25. package/templates/minimal/next.config.export.mjs +62 -0
  26. package/templates/minimal/next.config.mjs +18 -0
  27. package/templates/minimal/package-lock.json +7338 -0
  28. package/templates/minimal/package.json +32 -0
  29. package/templates/minimal/postcss.config.mjs +8 -0
  30. package/templates/minimal/public/api-specs/openapi-example.json +259 -0
  31. package/templates/minimal/public/api-specs/postman-example.json +205 -0
  32. package/templates/minimal/public/api-specs/test-api.json +256 -0
  33. package/templates/minimal/public/api-specs/users-api.json +264 -0
  34. package/templates/minimal/scripts/generate-redirects.mjs +88 -0
  35. package/templates/minimal/scripts/index-search.ts +159 -0
  36. package/templates/minimal/scripts/test-search.ts +83 -0
  37. package/templates/minimal/specra.config.json +124 -0
  38. package/templates/minimal/tsconfig.json +41 -0
  39. package/templates/minimal/yarn.lock +3909 -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,137 @@
1
+ # create-specra
2
+
3
+ The fastest way to create a new Specra documentation site. Scaffold a complete documentation project with a single command.
4
+
5
+ ## Usage
6
+
7
+ ### With npx (recommended)
8
+
9
+ ```bash
10
+ npx create-specra my-docs
11
+ ```
12
+
13
+ ### With npm
14
+
15
+ ```bash
16
+ npm create specra my-docs
17
+ ```
18
+
19
+ ### With yarn
20
+
21
+ ```bash
22
+ yarn create specra my-docs
23
+ ```
24
+
25
+ ### With pnpm
26
+
27
+ ```bash
28
+ pnpm create specra my-docs
29
+ ```
30
+
31
+ ## Options
32
+
33
+ ```bash
34
+ npx create-specra [project-directory] [options]
35
+ ```
36
+
37
+ ### Arguments
38
+
39
+ - `[project-directory]` - The directory to create the project in (optional, will prompt if not provided)
40
+
41
+ ### Options
42
+
43
+ - `--template <template>` - Template to use: `minimal` (will prompt if not provided)
44
+ - `--use-npm` - Use npm as the package manager
45
+ - `--use-pnpm` - Use pnpm as the package manager
46
+ - `--use-yarn` - Use yarn as the package manager
47
+ - `--skip-install` - Skip package installation
48
+
49
+ ## Templates
50
+
51
+ ### Minimal (Default)
52
+
53
+ Minimal setup to get started quickly:
54
+ - Basic documentation structure
55
+ - Essential configuration
56
+ - Clean starting point
57
+ - Ready to customize
58
+
59
+ ## Examples
60
+
61
+ Create a new project with interactive prompts:
62
+
63
+ ```bash
64
+ npx create-specra
65
+ ```
66
+
67
+ Create a project with the minimal template using npm:
68
+
69
+ ```bash
70
+ npx create-specra my-docs --template minimal --use-npm
71
+ ```
72
+
73
+ Create a project and skip installation:
74
+
75
+ ```bash
76
+ npx create-specra my-docs --skip-install
77
+ ```
78
+
79
+ ## What's Created
80
+
81
+ The CLI creates a new Next.js project with Specra pre-configured:
82
+
83
+ ```
84
+ my-docs/
85
+ ├── app/
86
+ │ ├── layout.tsx
87
+ │ ├── page.tsx
88
+ │ └── [...slug]/
89
+ │ └── page.tsx
90
+ ├── docs/
91
+ │ └── v1.0.0/
92
+ │ └── index.mdx
93
+ ├── public/
94
+ ├── specra.config.ts
95
+ ├── next.config.js
96
+ ├── tailwind.config.ts
97
+ └── package.json
98
+ ```
99
+
100
+ ## After Creation
101
+
102
+ Once your project is created, you can:
103
+
104
+ 1. Start the development server:
105
+ ```bash
106
+ cd my-docs
107
+ npm run dev
108
+ ```
109
+
110
+ 2. Open [http://localhost:3000](http://localhost:3000) in your browser
111
+
112
+ 3. Edit your documentation in the `docs/` directory
113
+
114
+ 4. Customize your site in `specra.config.ts`
115
+
116
+ ## What is Specra?
117
+
118
+ Specra is a modern documentation library for Next.js that provides:
119
+ - Multi-version documentation support
120
+ - API reference generation
121
+ - Full-text search
122
+ - MDX-powered content
123
+ - Beautiful UI components
124
+
125
+ ## Learn More
126
+
127
+ - [Specra on npm](https://www.npmjs.com/package/specra)
128
+ - [Next.js Documentation](https://nextjs.org/docs)
129
+ - [MDX Documentation](https://mdxjs.com)
130
+
131
+ ## License
132
+
133
+ MIT
134
+
135
+ ## Authors
136
+
137
+ dalmasonto, arthur-kamau
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "create-specra",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool to scaffold a new Specra documentation site with Next.js",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-specra": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "templates"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "dev": "tsup --watch",
16
+ "typecheck": "tsc --noEmit"
17
+ },
18
+ "keywords": [
19
+ "documentation",
20
+ "docs",
21
+ "nextjs",
22
+ "mdx",
23
+ "specra",
24
+ "create-app",
25
+ "cli"
26
+ ],
27
+ "author": "dalmasonto, arthur-kamau",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "commander": "^12.1.0",
31
+ "picocolors": "^1.1.1",
32
+ "prompts": "^2.4.2",
33
+ "validate-npm-package-name": "^6.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22",
37
+ "@types/prompts": "^2.4.9",
38
+ "@types/validate-npm-package-name": "^4.0.2",
39
+ "tsup": "^8.3.5",
40
+ "typescript": "^5"
41
+ }
42
+ }
@@ -0,0 +1,132 @@
1
+ # Specra Documentation Site
2
+
3
+ Welcome to your new Specra documentation site! This project was created with `create-specra`.
4
+
5
+ ## Getting Started
6
+
7
+ First, install dependencies and run the development server:
8
+
9
+ ```bash
10
+ npm install
11
+ npm run dev
12
+ # or
13
+ yarn install
14
+ yarn dev
15
+ # or
16
+ pnpm install
17
+ pnpm dev
18
+ ```
19
+
20
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see your documentation site.
21
+
22
+ ## Project Structure
23
+
24
+ ```
25
+ ├── app/ # Next.js app directory
26
+ │ ├── layout.tsx # Root layout
27
+ │ ├── page.tsx # Home page
28
+ │ └── docs/ # Documentation pages
29
+ ├── components/ # Reusable components
30
+ │ ├── docs/ # Documentation-specific components
31
+ │ └── ui/ # UI components
32
+ ├── docs/ # Your MDX documentation files
33
+ │ └── v1.0.0/ # Version 1.0.0 docs
34
+ ├── lib/ # Utility functions
35
+ │ ├── mdx.ts # MDX processing
36
+ │ ├── config.ts # Configuration
37
+ │ └── parsers/ # API parsers
38
+ ├── public/ # Static assets
39
+ └── specra.config.json # Specra configuration
40
+ ```
41
+
42
+ ## Writing Documentation
43
+
44
+ Add your MDX files in the `docs/v1.0.0/` directory:
45
+
46
+ ```mdx
47
+ ---
48
+ title: My Page
49
+ description: This is my documentation page
50
+ ---
51
+
52
+ # My Page
53
+
54
+ Your content here...
55
+ ```
56
+
57
+ ### Using Components
58
+
59
+ Import and use components in your MDX:
60
+
61
+ ```mdx
62
+ import { Callout } from '@/components/docs/callout'
63
+ import { CodeBlock } from '@/components/docs/code-block'
64
+ import { Tabs, Tab } from '@/components/docs/tabs'
65
+
66
+ <Callout type="info">
67
+ This is an info callout!
68
+ </Callout>
69
+
70
+ <Tabs>
71
+ <Tab title="JavaScript">
72
+ ```js
73
+ console.log('Hello World')
74
+ ```
75
+ </Tab>
76
+ <Tab title="TypeScript">
77
+ ```ts
78
+ console.log('Hello World')
79
+ ```
80
+ </Tab>
81
+ </Tabs>
82
+ ```
83
+
84
+ ## Configuration
85
+
86
+ Edit `specra.config.json` to customize your site:
87
+
88
+ ```json
89
+ {
90
+ "site": {
91
+ "title": "Your Docs",
92
+ "description": "Your documentation site",
93
+ "url": "https://yourdocs.com"
94
+ },
95
+ "navigation": {
96
+ "links": [
97
+ { "title": "Home", "href": "/" },
98
+ { "title": "Docs", "href": "/docs" }
99
+ ]
100
+ }
101
+ }
102
+ ```
103
+
104
+ ## Building for Production
105
+
106
+ ```bash
107
+ npm run build
108
+ npm run start
109
+ ```
110
+
111
+ ## Learn More
112
+
113
+ - [Specra Documentation](https://specra.dev/docs)
114
+ - [Next.js Documentation](https://nextjs.org/docs)
115
+ - [MDX Documentation](https://mdxjs.com)
116
+
117
+ ## Deployment
118
+
119
+ Deploy your Specra documentation site to Vercel, Netlify, or any hosting platform that supports Next.js.
120
+
121
+ ### Vercel
122
+
123
+ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new)
124
+
125
+ ### Netlify
126
+
127
+ [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start)
128
+
129
+ ## Need Help?
130
+
131
+ - Check the [documentation](https://specra.dev/docs)
132
+ - Report issues on [GitHub](https://github.com/yourusername/specra/issues)
@@ -0,0 +1,80 @@
1
+ import { NextRequest } from 'next/server'
2
+ import { watch } from 'fs'
3
+ import { join } from 'path'
4
+
5
+ export const dynamic = 'force-static'
6
+
7
+ export async function GET(request: NextRequest) {
8
+ // Only allow in development mode
9
+ if (process.env.NODE_ENV !== 'development') {
10
+ return new Response('Not available in production', { status: 404 })
11
+ }
12
+
13
+ const encoder = new TextEncoder()
14
+
15
+ const stream = new ReadableStream({
16
+ start(controller) {
17
+ // Send initial connection message
18
+ const connectMsg = `data: ${JSON.stringify({ type: 'connected' })}\n\n`
19
+ controller.enqueue(encoder.encode(connectMsg))
20
+
21
+ const docsPath = join(process.cwd(), 'docs')
22
+
23
+ // Watch the docs directory recursively
24
+ const watcher = watch(
25
+ docsPath,
26
+ { recursive: true },
27
+ (eventType, filename) => {
28
+ if (!filename) return
29
+
30
+ // Only watch for .mdx and .json files (MDX files and category configs)
31
+ if (filename.endsWith('.mdx') || filename.endsWith('.json')) {
32
+ console.log(`[MDX Watch] ${eventType}: ${filename}`)
33
+
34
+ const message = `data: ${JSON.stringify({
35
+ type: 'change',
36
+ file: filename,
37
+ eventType
38
+ })}\n\n`
39
+
40
+ try {
41
+ controller.enqueue(encoder.encode(message))
42
+ } catch (error) {
43
+ console.error('[MDX Watch] Error sending message:', error)
44
+ }
45
+ }
46
+ }
47
+ )
48
+
49
+ // Handle client disconnect
50
+ request.signal.addEventListener('abort', () => {
51
+ console.log('[MDX Watch] Client disconnected')
52
+ watcher.close()
53
+ try {
54
+ controller.close()
55
+ } catch (error) {
56
+ // Controller might already be closed
57
+ }
58
+ })
59
+
60
+ // Handle errors
61
+ watcher.on('error', (error) => {
62
+ console.error('[MDX Watch] Watcher error:', error)
63
+ watcher.close()
64
+ try {
65
+ controller.close()
66
+ } catch (e) {
67
+ // Controller might already be closed
68
+ }
69
+ })
70
+ }
71
+ })
72
+
73
+ return new Response(stream, {
74
+ headers: {
75
+ 'Content-Type': 'text/event-stream',
76
+ 'Cache-Control': 'no-cache, no-transform',
77
+ 'Connection': 'keep-alive',
78
+ },
79
+ })
80
+ }
@@ -0,0 +1,7 @@
1
+ export default function Loading() {
2
+ return (
3
+ <div className="flex items-center justify-center min-h-screen">
4
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-gray-900" />
5
+ </div>
6
+ )
7
+ }
@@ -0,0 +1,212 @@
1
+ import type { Metadata } from "next"
2
+ import { Suspense } from "react"
3
+ import {
4
+ extractTableOfContents,
5
+ getAdjacentDocs,
6
+ isCategoryPage,
7
+ getCachedVersions,
8
+ getCachedAllDocs,
9
+ getCachedDocBySlug,
10
+ getConfig,
11
+ SpecraConfig,
12
+ } from "specra/lib"
13
+ import {
14
+ DocLayout,
15
+ TableOfContents,
16
+ Header,
17
+ DocLayoutWrapper,
18
+ HotReloadIndicator,
19
+ DevModeBadge,
20
+ MdxHotReload,
21
+ CategoryIndex,
22
+ NotFoundContent,
23
+ DocLoading,
24
+ } from "specra/components"
25
+
26
+ import specraConfig from "./../../../../specra.config.json"
27
+
28
+ interface PageProps {
29
+ params: Promise<{
30
+ version: string
31
+ slug: string[]
32
+ }>
33
+ }
34
+
35
+ export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
36
+ const { version, slug: slugArray } = await params
37
+ const slug = slugArray.join("/")
38
+
39
+ const doc = await getCachedDocBySlug(slug, version)
40
+
41
+ if (!doc) {
42
+ return {
43
+ title: "Page Not Found",
44
+ description: "The requested documentation page could not be found.",
45
+ }
46
+ }
47
+
48
+ const title = doc.meta.title || doc.title
49
+ const description = doc.meta.description || `Documentation for ${title}`
50
+ const url = `/docs/${version}/${slug}`
51
+
52
+ return {
53
+ title: `${title}`,
54
+ description,
55
+ openGraph: {
56
+ title,
57
+ description,
58
+ url,
59
+ siteName: "Documentation Platform",
60
+ type: "article",
61
+ locale: "en_US",
62
+ },
63
+ twitter: {
64
+ card: "summary_large_image",
65
+ title,
66
+ description,
67
+ },
68
+ alternates: {
69
+ canonical: url,
70
+ },
71
+ }
72
+ }
73
+
74
+ export async function generateStaticParams() {
75
+ const versions = getCachedVersions()
76
+ const params = []
77
+
78
+ for (const version of versions) {
79
+ const docs = await getCachedAllDocs(version)
80
+ for (const doc of docs) {
81
+ // Add the custom slug path
82
+ params.push({
83
+ version,
84
+ slug: doc.slug.split("/").filter(Boolean),
85
+ })
86
+ }
87
+ }
88
+
89
+ return params
90
+ }
91
+
92
+ export default async function DocPage({ params }: PageProps) {
93
+ const { version, slug: slugArray } = await params
94
+ const slug = slugArray.join("/")
95
+
96
+ const allDocs = await getCachedAllDocs(version)
97
+ const versions = getCachedVersions()
98
+ const config = getConfig()
99
+ const isCategory = isCategoryPage(slug, allDocs)
100
+
101
+ // Try to get the doc (might be index.mdx or regular .mdx)
102
+ const doc = await getCachedDocBySlug(slug, version)
103
+
104
+ // If no doc found and it's a category, show category index
105
+ if (!doc && isCategory) {
106
+ // Find a doc in this category to get the tab group
107
+ const categoryDoc = allDocs.find((d) => d.slug.startsWith(slug + "/"))
108
+ const categoryTabGroup = categoryDoc?.meta?.tab_group || categoryDoc?.categoryTabGroup
109
+
110
+ return (
111
+ <>
112
+ <DocLayoutWrapper
113
+ header={<Header currentVersion={version} versions={versions} config={config} />}
114
+ docs={allDocs}
115
+ version={version}
116
+ content={
117
+ <CategoryIndex
118
+ categoryPath={slug}
119
+ version={version}
120
+ allDocs={allDocs}
121
+ title={slug.split("/").pop()?.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) || "Category"}
122
+ description="Browse the documentation in this section."
123
+ config={config}
124
+ />
125
+ }
126
+ toc={<div />}
127
+ config={config}
128
+ currentPageTabGroup={categoryTabGroup}
129
+ />
130
+ <MdxHotReload />
131
+ <HotReloadIndicator />
132
+ <DevModeBadge />
133
+ </>
134
+ )
135
+ }
136
+
137
+ // If no doc found, render 404 content within the layout (keeps sidebar visible)
138
+ if (!doc) {
139
+ return (
140
+ <>
141
+ <Suspense fallback={<DocLoading />}>
142
+ <DocLayoutWrapper
143
+ header={<Header currentVersion={version} versions={versions} config={config} />}
144
+ docs={allDocs}
145
+ version={version}
146
+ content={<NotFoundContent version={version} />}
147
+ toc={<div />}
148
+ config={config}
149
+ currentPageTabGroup={undefined}
150
+ />
151
+ <MdxHotReload />
152
+ <HotReloadIndicator />
153
+ <DevModeBadge />
154
+ </Suspense>
155
+ </>
156
+ )
157
+ }
158
+
159
+ const toc = extractTableOfContents(doc.content)
160
+ const { previous, next } = getAdjacentDocs(slug, allDocs)
161
+
162
+ // console.log("[v0] Extracted ToC:", toc)
163
+
164
+ // If doc exists but is also a category, show both content and children
165
+ const showCategoryIndex = isCategory && doc
166
+
167
+ // console.log("showCategoryIndex: ", showCategoryIndex)
168
+
169
+ // Get current page's tab group from doc metadata or category
170
+ const currentPageTabGroup = doc.meta?.tab_group || doc.categoryTabGroup
171
+
172
+ return (
173
+ <>
174
+ <Suspense fallback={<DocLoading />}>
175
+ <DocLayoutWrapper
176
+ header={<Header currentVersion={version} versions={versions} config={config} />}
177
+ docs={allDocs}
178
+ version={version}
179
+ content={
180
+ showCategoryIndex ? (
181
+ <CategoryIndex
182
+ categoryPath={slug}
183
+ version={version}
184
+ allDocs={allDocs}
185
+ title={doc.meta.title}
186
+ description={doc.meta.description}
187
+ content={doc.content}
188
+ config={config}
189
+ />
190
+ ) : (
191
+ <DocLayout
192
+ meta={doc.meta}
193
+ content={doc.content}
194
+ previousDoc={previous ? { title: previous.meta.title, slug: previous.slug } : undefined}
195
+ nextDoc={next ? { title: next.meta.title, slug: next.slug } : undefined}
196
+ version={version}
197
+ slug={slug}
198
+ config={config}
199
+ />
200
+ )
201
+ }
202
+ toc={showCategoryIndex ? <div /> : <TableOfContents items={toc} config={config} />}
203
+ config={config}
204
+ currentPageTabGroup={currentPageTabGroup}
205
+ />
206
+ <MdxHotReload />
207
+ <HotReloadIndicator />
208
+ <DevModeBadge />
209
+ </Suspense>
210
+ </>
211
+ )
212
+ }
@@ -0,0 +1,10 @@
1
+
2
+ import { VersionNotFound } from "specra/components"
3
+
4
+ export default function NotFound() {
5
+ return (
6
+ <>
7
+ <VersionNotFound />
8
+ </>
9
+ )
10
+ }
@@ -0,0 +1,27 @@
1
+ import { redirect } from "next/navigation"
2
+ import { getCachedVersions, getCachedAllDocs } from "specra/lib"
3
+
4
+ interface PageProps {
5
+ params: Promise<{
6
+ version: string
7
+ }>
8
+ }
9
+
10
+ export async function generateStaticParams() {
11
+ const versions = getCachedVersions()
12
+ return versions.map((version) => ({
13
+ version,
14
+ }))
15
+ }
16
+
17
+ export default async function VersionIndexPage({ params }: PageProps) {
18
+ const { version } = await params
19
+ const docs = await getCachedAllDocs(version)
20
+
21
+ if (docs.length === 0) {
22
+ redirect("/docs/v1.0.0")
23
+ }
24
+
25
+ // Redirect to first doc
26
+ redirect(`/docs/${version}/${docs[0].slug}`)
27
+ }
@@ -0,0 +1 @@
1
+ @import "specra/styles";