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
@@ -0,0 +1,159 @@
1
+ import { MeiliSearch } from "meilisearch"
2
+ import fs from "fs"
3
+ import path from "path"
4
+ import matter from "gray-matter"
5
+ import { getConfig } from "specra"
6
+ import { extractSearchText } from "specra/components"
7
+ // import { extractSearchText } from "@/components/docs/componentTextProps"
8
+
9
+ interface SearchDocument {
10
+ id: string
11
+ title: string
12
+ content: string
13
+ slug: string
14
+ version: string
15
+ category?: string
16
+ tags?: string[]
17
+ }
18
+
19
+
20
+ async function indexDocuments() {
21
+ const config = getConfig()
22
+ const searchConfig = config.search
23
+
24
+ if (!searchConfig?.enabled || searchConfig.provider !== "meilisearch") {
25
+ console.error("Meilisearch is not enabled in config")
26
+ process.exit(1)
27
+ }
28
+
29
+ const meilisearchConfig = searchConfig.meilisearch
30
+ if (!meilisearchConfig) {
31
+ console.error("Meilisearch configuration is missing")
32
+ process.exit(1)
33
+ }
34
+
35
+ console.log("Connecting to Meilisearch at:", meilisearchConfig.host)
36
+
37
+ const client = new MeiliSearch({
38
+ host: meilisearchConfig.host,
39
+ apiKey: meilisearchConfig.apiKey || "",
40
+ })
41
+
42
+ const index = client.index(meilisearchConfig.indexName)
43
+
44
+ // Get all MDX files
45
+ const docsDir = path.join(process.cwd(), "docs")
46
+ const documents: SearchDocument[] = []
47
+
48
+ function processDirectory(dir: string, version: string) {
49
+ const files = fs.readdirSync(dir)
50
+
51
+ for (const file of files) {
52
+ const filePath = path.join(dir, file)
53
+ const stat = fs.statSync(filePath)
54
+
55
+ if (stat.isDirectory()) {
56
+ processDirectory(filePath, version)
57
+ } else if (file.endsWith(".mdx") || file.endsWith(".md")) {
58
+ const content = fs.readFileSync(filePath, "utf-8")
59
+ const { data, content: mdxContent } = matter(content)
60
+
61
+ // Generate slug from file path
62
+ const relativePath = path.relative(path.join(docsDir, version), filePath)
63
+ const slug = relativePath
64
+ .replace(/\.(mdx|md)$/, "")
65
+ .replace(/\\/g, "/")
66
+
67
+ // Extract category from path
68
+ const pathParts = slug.split("/")
69
+ const category = pathParts.length > 1 ? pathParts[0] : undefined
70
+
71
+ // Clean content (remove code blocks and special chars for better search)
72
+ // const cleanContent = mdxContent
73
+ // .replace(/```[\s\S]*?```/g, "") // Remove code blocks
74
+ // .replace(/`[^`]+`/g, "") // Remove inline code
75
+ // .replace(/[#*_~]/g, "") // Remove markdown symbols
76
+ // .replace(/\n+/g, " ") // Replace newlines with spaces
77
+ // .trim()
78
+ // .slice(0, 1000) // Limit content length
79
+
80
+ const cleanContent = extractSearchText(mdxContent);
81
+
82
+ // console.log("------");
83
+ // console.log("Cleaned content: ");
84
+ // console.log(cleanContent);
85
+ // console.log("------");
86
+ // Create a valid document ID (replace periods with underscores)
87
+ // const docId = `${version.replace(/\./g, "_")}-${slug.replace(/\//g, "-")}`
88
+ const docId = slug.replace(/\//g, "-")
89
+
90
+ documents.push({
91
+ id: docId,
92
+ title: data.title || slug,
93
+ content: cleanContent,
94
+ slug: slug,
95
+ version: version,
96
+ category: category,
97
+ tags: data.tags || [],
98
+ })
99
+ }
100
+ }
101
+ }
102
+
103
+ // Process all version directories
104
+ const versions = fs.readdirSync(docsDir).filter((item) => {
105
+ const itemPath = path.join(docsDir, item)
106
+ return fs.statSync(itemPath).isDirectory()
107
+ })
108
+
109
+ console.log(`Found ${versions.length} version(s):`, versions.join(", "))
110
+
111
+ for (const version of versions) {
112
+ const versionPath = path.join(docsDir, version)
113
+ processDirectory(versionPath, version)
114
+ }
115
+
116
+ console.log(`Indexing ${documents.length} documents...`)
117
+
118
+ try {
119
+ // Configure searchable attributes first
120
+ console.log("Configuring search settings...")
121
+ await index.updateSearchableAttributes(["title", "content", "tags"])
122
+ await index.updateFilterableAttributes(["version", "category", "tags"])
123
+ await index.updateSortableAttributes(["title"])
124
+ await index.updateDistinctAttribute("id")
125
+ await index.updateSettings({
126
+ rankingRules: [
127
+ "words",
128
+ "typo",
129
+ "proximity",
130
+ "attribute",
131
+ "sort",
132
+ "exactness"
133
+ ]
134
+ })
135
+ console.log("✅ Search configuration updated!")
136
+
137
+ // Delete existing documents
138
+ console.log("Clearing old documents...")
139
+ const deleteTask = await index.deleteAllDocuments()
140
+ console.log("Delete task created:", deleteTask.taskUid)
141
+
142
+ // Add new documents
143
+ console.log("Adding documents...")
144
+ const addTask = await index.addDocuments(documents, { primaryKey: "id" })
145
+ console.log("Add task created:", addTask.taskUid)
146
+ console.log("✅ Documents sent for indexing!")
147
+
148
+ console.log("\n⏳ Indexing is processing in the background...")
149
+ console.log("Wait a few seconds, then run 'npm run test:search' to verify!")
150
+ } catch (error) {
151
+ console.error("Error indexing documents:", error)
152
+ if (error instanceof Error) {
153
+ console.error("Error message:", error.message)
154
+ }
155
+ process.exit(1)
156
+ }
157
+ }
158
+
159
+ indexDocuments()
@@ -0,0 +1,83 @@
1
+ import { MeiliSearch } from "meilisearch"
2
+ import { getConfig } from "specra"
3
+
4
+ async function testSearch() {
5
+ const config = getConfig()
6
+ const searchConfig = config.search
7
+
8
+ if (!searchConfig?.enabled || searchConfig.provider !== "meilisearch") {
9
+ console.error("Meilisearch is not enabled in config")
10
+ process.exit(1)
11
+ }
12
+
13
+ const meilisearchConfig = searchConfig.meilisearch
14
+ if (!meilisearchConfig) {
15
+ console.error("Meilisearch configuration is missing")
16
+ process.exit(1)
17
+ }
18
+
19
+ console.log("Connecting to Meilisearch at:", meilisearchConfig.host)
20
+ console.log("Using API Key:", meilisearchConfig.apiKey ? "Yes" : "No")
21
+
22
+ const client = new MeiliSearch({
23
+ host: meilisearchConfig.host,
24
+ apiKey: meilisearchConfig.apiKey || "",
25
+ })
26
+
27
+ const index = client.index(meilisearchConfig.indexName)
28
+
29
+ try {
30
+ // Get index stats
31
+ console.log("\n📊 Index Stats:")
32
+ const stats = await index.getStats()
33
+ console.log("- Number of documents:", stats.numberOfDocuments)
34
+ console.log("- Is indexing:", stats.isIndexing)
35
+ console.log("- Field distribution:", JSON.stringify(stats.fieldDistribution, null, 2))
36
+
37
+ // Get some documents
38
+ console.log("\n📄 Sample Documents:")
39
+ const documents = await index.getDocuments({ limit: 3 })
40
+ console.log("Total documents:", documents.results.length)
41
+ documents.results.forEach((doc: any, i: number) => {
42
+ console.log(`\nDocument ${i + 1}:`)
43
+ console.log("- ID:", doc.id)
44
+ console.log("- Title:", doc.title)
45
+ console.log("- Version:", doc.version)
46
+ console.log("- Slug:", doc.slug)
47
+ console.log("- Category:", doc.category)
48
+ console.log("- Content preview:", doc.content?.substring(0, 100) + "...")
49
+ })
50
+
51
+ // Test search
52
+ console.log("\n🔍 Testing Search:")
53
+ const searchQueries = ["documentation", "getting", "guide", "intro"]
54
+
55
+ for (const query of searchQueries) {
56
+ const results = await index.search(query, {
57
+ limit: 5,
58
+ })
59
+ console.log(`\nQuery: "${query}"`)
60
+ console.log(`- Results found: ${results.hits.length}`)
61
+ console.log(`- Processing time: ${results.processingTimeMs}ms`)
62
+ if (results.hits.length > 0) {
63
+ console.log("- First result:", (results.hits[0] as any).title)
64
+ }
65
+ }
66
+
67
+ // Get searchable attributes
68
+ console.log("\n⚙️ Index Settings:")
69
+ const searchableAttrs = await index.getSearchableAttributes()
70
+ console.log("- Searchable attributes:", searchableAttrs)
71
+
72
+ const filterableAttrs = await index.getFilterableAttributes()
73
+ console.log("- Filterable attributes:", filterableAttrs)
74
+
75
+ } catch (error) {
76
+ console.error("\n❌ Error:", error)
77
+ if (error instanceof Error) {
78
+ console.error("Message:", error.message)
79
+ }
80
+ }
81
+ }
82
+
83
+ testSearch()
@@ -0,0 +1,124 @@
1
+ {
2
+ "$schema": "./lib/config.types.ts",
3
+ "site": {
4
+ "title": "Documentation Library",
5
+ "description": "Comprehensive documentation for your project",
6
+ "url": "https://vercel.com/dalmasontos-projects/v0-documentation-library",
7
+ "baseUrl": "/",
8
+ "language": "en",
9
+ "organizationName": "Your Organization",
10
+ "projectName": "v0-documentation-library",
11
+ "activeVersion": "v1.0.0",
12
+ "favicon": "/icon-light-32x32.png"
13
+ },
14
+ "theme": {
15
+ "defaultMode": "system",
16
+ "respectPrefersColorScheme": true
17
+ },
18
+ "navigation": {
19
+ "showSidebar": true,
20
+ "collapsibleSidebar": true,
21
+ "showBreadcrumbs": true,
22
+ "showTableOfContents": true,
23
+ "tocPosition": "right",
24
+ "tocMaxDepth": 3,
25
+ "tabGroups": [
26
+ {
27
+ "id": "guides",
28
+ "label": "Guides",
29
+ "icon": "book-open"
30
+ },
31
+ {
32
+ "id": "api",
33
+ "label": "API Reference",
34
+ "icon": "zap"
35
+ },
36
+ {
37
+ "id": "components",
38
+ "label": "Components",
39
+ "icon": "layers"
40
+ }
41
+ ]
42
+ },
43
+ "social": {
44
+ "github": "https://github.com/dalmasonto",
45
+ "twitter": "https://twitter.com/dalmasonto",
46
+ "discord": "https://discord.com/invite/dalmasonto",
47
+ "custom": [
48
+ {
49
+ "label": "Website",
50
+ "href": "https://craftfolio.com/dalmasonto"
51
+ }
52
+ ]
53
+ },
54
+ "search": {
55
+ "enabled": false,
56
+ "placeholder": "Search documentation...",
57
+ "provider": "meilisearch",
58
+ "meilisearch": {
59
+ "host": "http://localhost:7700",
60
+ "apiKey": "aSampleMasterKey",
61
+ "indexName": "docs"
62
+ }
63
+ },
64
+ "analytics": {
65
+ "googleAnalytics": "",
66
+ "plausible": ""
67
+ },
68
+ "footer": {
69
+ "copyright": "Copyright © 2024 Specra. All rights reserved.",
70
+ "links": [
71
+ {
72
+ "title": "Documentation",
73
+ "items": [
74
+ {
75
+ "label": "Getting Started",
76
+ "href": "/docs/v1.0.0/getting-started"
77
+ },
78
+ {
79
+ "label": "API Reference",
80
+ "href": "/docs/v1.0.0/api"
81
+ }
82
+ ]
83
+ },
84
+ {
85
+ "title": "Community",
86
+ "items": [
87
+ {
88
+ "label": "GitHub",
89
+ "href": "https://github.com/yourusername/your-repo"
90
+ },
91
+ {
92
+ "label": "Discord",
93
+ "href": "#"
94
+ }
95
+ ]
96
+ }
97
+ ]
98
+ },
99
+ "banner": {
100
+ "enabled": false,
101
+ "message": "🎉 This is a development version. Some features may not work as expected.",
102
+ "type": "error",
103
+ "dismissible": true
104
+ },
105
+ "features": {
106
+ "editUrl": "https://github.com/yourusername/your-repo/edit/main/docs",
107
+ "showLastUpdated": true,
108
+ "showReadingTime": true,
109
+ "showAuthors": false,
110
+ "showTags": true,
111
+ "versioning": true,
112
+ "i18n": false
113
+ },
114
+ "env": {
115
+ "API_BASE_URL": "https://api.example.com",
116
+ "API_VERSION": "v1",
117
+ "CDN_URL": "https://cdn.example.com"
118
+ },
119
+ "deployment": {
120
+ "target": "github-pages",
121
+ "basePath": "",
122
+ "customDomain": false
123
+ }
124
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [
4
+ "dom",
5
+ "dom.iterable",
6
+ "esnext"
7
+ ],
8
+ "allowJs": true,
9
+ "target": "ES6",
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "noEmit": true,
13
+ "esModuleInterop": true,
14
+ "module": "esnext",
15
+ "moduleResolution": "bundler",
16
+ "resolveJsonModule": true,
17
+ "isolatedModules": true,
18
+ "jsx": "react-jsx",
19
+ "incremental": true,
20
+ "plugins": [
21
+ {
22
+ "name": "next"
23
+ }
24
+ ],
25
+ "paths": {
26
+ "@/*": [
27
+ "./*"
28
+ ]
29
+ }
30
+ },
31
+ "include": [
32
+ "next-env.d.ts",
33
+ "**/*.ts",
34
+ "**/*.tsx",
35
+ ".next/types/**/*.ts",
36
+ ".next/dev/types/**/*.ts"
37
+ ],
38
+ "exclude": [
39
+ "node_modules"
40
+ ]
41
+ }