@nuasite/cms 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/README.md +237 -0
- package/dist/src/build-processor.d.ts +20 -0
- package/dist/src/build-processor.d.ts.map +1 -0
- package/dist/src/collection-scanner.d.ts +6 -0
- package/dist/src/collection-scanner.d.ts.map +1 -0
- package/dist/src/component-registry.d.ts +63 -0
- package/dist/src/component-registry.d.ts.map +1 -0
- package/dist/src/config.d.ts +24 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/dev-middleware.d.ts +20 -0
- package/dist/src/dev-middleware.d.ts.map +1 -0
- package/dist/src/editor/ai.d.ts +60 -0
- package/dist/src/editor/ai.d.ts.map +1 -0
- package/dist/src/editor/api.d.ts +140 -0
- package/dist/src/editor/api.d.ts.map +1 -0
- package/dist/src/editor/color-utils.d.ts +106 -0
- package/dist/src/editor/color-utils.d.ts.map +1 -0
- package/dist/src/editor/components/ai-chat.d.ts +11 -0
- package/dist/src/editor/components/ai-chat.d.ts.map +1 -0
- package/dist/src/editor/components/ai-tooltip.d.ts +12 -0
- package/dist/src/editor/components/ai-tooltip.d.ts.map +1 -0
- package/dist/src/editor/components/attribute-editor.d.ts +5 -0
- package/dist/src/editor/components/attribute-editor.d.ts.map +1 -0
- package/dist/src/editor/components/block-editor.d.ts +12 -0
- package/dist/src/editor/components/block-editor.d.ts.map +1 -0
- package/dist/src/editor/components/collections-browser.d.ts +2 -0
- package/dist/src/editor/components/collections-browser.d.ts.map +1 -0
- package/dist/src/editor/components/color-toolbar.d.ts +12 -0
- package/dist/src/editor/components/color-toolbar.d.ts.map +1 -0
- package/dist/src/editor/components/confirm-dialog.d.ts +2 -0
- package/dist/src/editor/components/confirm-dialog.d.ts.map +1 -0
- package/dist/src/editor/components/create-page-modal.d.ts +2 -0
- package/dist/src/editor/components/create-page-modal.d.ts.map +1 -0
- package/dist/src/editor/components/editable-highlights.d.ts +9 -0
- package/dist/src/editor/components/editable-highlights.d.ts.map +1 -0
- package/dist/src/editor/components/error-boundary.d.ts +32 -0
- package/dist/src/editor/components/error-boundary.d.ts.map +1 -0
- package/dist/src/editor/components/fields.d.ts +75 -0
- package/dist/src/editor/components/fields.d.ts.map +1 -0
- package/dist/src/editor/components/frontmatter-fields.d.ts +29 -0
- package/dist/src/editor/components/frontmatter-fields.d.ts.map +1 -0
- package/dist/src/editor/components/highlight-overlay.d.ts +64 -0
- package/dist/src/editor/components/highlight-overlay.d.ts.map +1 -0
- package/dist/src/editor/components/image-overlay.d.ts +12 -0
- package/dist/src/editor/components/image-overlay.d.ts.map +1 -0
- package/dist/src/editor/components/markdown-editor-overlay.d.ts +6 -0
- package/dist/src/editor/components/markdown-editor-overlay.d.ts.map +1 -0
- package/dist/src/editor/components/markdown-inline-editor.d.ts +10 -0
- package/dist/src/editor/components/markdown-inline-editor.d.ts.map +1 -0
- package/dist/src/editor/components/media-library.d.ts +2 -0
- package/dist/src/editor/components/media-library.d.ts.map +1 -0
- package/dist/src/editor/components/outline.d.ts +21 -0
- package/dist/src/editor/components/outline.d.ts.map +1 -0
- package/dist/src/editor/components/redirect-countdown.d.ts +2 -0
- package/dist/src/editor/components/redirect-countdown.d.ts.map +1 -0
- package/dist/src/editor/components/seo-editor.d.ts +2 -0
- package/dist/src/editor/components/seo-editor.d.ts.map +1 -0
- package/dist/src/editor/components/text-style-toolbar.d.ts +8 -0
- package/dist/src/editor/components/text-style-toolbar.d.ts.map +1 -0
- package/dist/src/editor/components/toast/toast-container.d.ts +7 -0
- package/dist/src/editor/components/toast/toast-container.d.ts.map +1 -0
- package/dist/src/editor/components/toast/toast.d.ts +7 -0
- package/dist/src/editor/components/toast/toast.d.ts.map +1 -0
- package/dist/src/editor/components/toast/types.d.ts +7 -0
- package/dist/src/editor/components/toast/types.d.ts.map +1 -0
- package/dist/src/editor/components/toolbar.d.ts +21 -0
- package/dist/src/editor/components/toolbar.d.ts.map +1 -0
- package/dist/src/editor/config.d.ts +4 -0
- package/dist/src/editor/config.d.ts.map +1 -0
- package/dist/src/editor/constants.d.ts +101 -0
- package/dist/src/editor/constants.d.ts.map +1 -0
- package/dist/src/editor/context.d.ts +14 -0
- package/dist/src/editor/context.d.ts.map +1 -0
- package/dist/src/editor/dom.d.ts +77 -0
- package/dist/src/editor/dom.d.ts.map +1 -0
- package/dist/src/editor/editor.d.ts +64 -0
- package/dist/src/editor/editor.d.ts.map +1 -0
- package/dist/src/editor/history.d.ts +20 -0
- package/dist/src/editor/history.d.ts.map +1 -0
- package/dist/src/editor/hooks/index.d.ts +14 -0
- package/dist/src/editor/hooks/index.d.ts.map +1 -0
- package/dist/src/editor/hooks/useAIHandlers.d.ts +22 -0
- package/dist/src/editor/hooks/useAIHandlers.d.ts.map +1 -0
- package/dist/src/editor/hooks/useBlockEditorHandlers.d.ts +18 -0
- package/dist/src/editor/hooks/useBlockEditorHandlers.d.ts.map +1 -0
- package/dist/src/editor/hooks/useElementDetection.d.ts +26 -0
- package/dist/src/editor/hooks/useElementDetection.d.ts.map +1 -0
- package/dist/src/editor/hooks/useImageHoverDetection.d.ts +12 -0
- package/dist/src/editor/hooks/useImageHoverDetection.d.ts.map +1 -0
- package/dist/src/editor/hooks/useTextSelection.d.ts +23 -0
- package/dist/src/editor/hooks/useTextSelection.d.ts.map +1 -0
- package/dist/src/editor/hooks/useTooltipState.d.ts +19 -0
- package/dist/src/editor/hooks/useTooltipState.d.ts.map +1 -0
- package/dist/src/editor/hooks/utils.d.ts +32 -0
- package/dist/src/editor/hooks/utils.d.ts.map +1 -0
- package/dist/src/editor/index.d.ts +12 -0
- package/dist/src/editor/index.d.ts.map +1 -0
- package/dist/src/editor/lib/cn.d.ts +3 -0
- package/dist/src/editor/lib/cn.d.ts.map +1 -0
- package/dist/src/editor/manifest.d.ts +19 -0
- package/dist/src/editor/manifest.d.ts.map +1 -0
- package/dist/src/editor/markdown-api.d.ts +36 -0
- package/dist/src/editor/markdown-api.d.ts.map +1 -0
- package/dist/src/editor/signals.d.ts +242 -0
- package/dist/src/editor/signals.d.ts.map +1 -0
- package/dist/src/editor/storage.d.ts +27 -0
- package/dist/src/editor/storage.d.ts.map +1 -0
- package/dist/src/editor/text-styling.d.ts +350 -0
- package/dist/src/editor/text-styling.d.ts.map +1 -0
- package/dist/src/editor/themes.d.ts +38 -0
- package/dist/src/editor/themes.d.ts.map +1 -0
- package/dist/src/editor/types.d.ts +454 -0
- package/dist/src/editor/types.d.ts.map +1 -0
- package/dist/src/error-collector.d.ts +56 -0
- package/dist/src/error-collector.d.ts.map +1 -0
- package/dist/src/handlers/component-ops.d.ts +34 -0
- package/dist/src/handlers/component-ops.d.ts.map +1 -0
- package/dist/src/handlers/markdown-ops.d.ts +41 -0
- package/dist/src/handlers/markdown-ops.d.ts.map +1 -0
- package/dist/src/handlers/request-utils.d.ts +20 -0
- package/dist/src/handlers/request-utils.d.ts.map +1 -0
- package/dist/src/handlers/source-writer.d.ts +51 -0
- package/dist/src/handlers/source-writer.d.ts.map +1 -0
- package/dist/src/html-processor.d.ts +63 -0
- package/dist/src/html-processor.d.ts.map +1 -0
- package/dist/src/index.d.ts +41 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/manifest-writer.d.ts +111 -0
- package/dist/src/manifest-writer.d.ts.map +1 -0
- package/dist/src/media/contember.d.ts +15 -0
- package/dist/src/media/contember.d.ts.map +1 -0
- package/dist/src/media/local.d.ts +9 -0
- package/dist/src/media/local.d.ts.map +1 -0
- package/dist/src/media/s3.d.ts +12 -0
- package/dist/src/media/s3.d.ts.map +1 -0
- package/dist/src/media/types.d.ts +40 -0
- package/dist/src/media/types.d.ts.map +1 -0
- package/dist/src/preview-generator.d.ts +19 -0
- package/dist/src/preview-generator.d.ts.map +1 -0
- package/dist/src/seo-processor.d.ts +23 -0
- package/dist/src/seo-processor.d.ts.map +1 -0
- package/dist/src/source-finder/ast-extractors.d.ts +35 -0
- package/dist/src/source-finder/ast-extractors.d.ts.map +1 -0
- package/dist/src/source-finder/ast-parser.d.ts +16 -0
- package/dist/src/source-finder/ast-parser.d.ts.map +1 -0
- package/dist/src/source-finder/cache.d.ts +18 -0
- package/dist/src/source-finder/cache.d.ts.map +1 -0
- package/dist/src/source-finder/collection-finder.d.ts +29 -0
- package/dist/src/source-finder/collection-finder.d.ts.map +1 -0
- package/dist/src/source-finder/cross-file-tracker.d.ts +39 -0
- package/dist/src/source-finder/cross-file-tracker.d.ts.map +1 -0
- package/dist/src/source-finder/element-finder.d.ts +42 -0
- package/dist/src/source-finder/element-finder.d.ts.map +1 -0
- package/dist/src/source-finder/image-finder.d.ts +24 -0
- package/dist/src/source-finder/image-finder.d.ts.map +1 -0
- package/dist/src/source-finder/index.d.ts +9 -0
- package/dist/src/source-finder/index.d.ts.map +1 -0
- package/dist/src/source-finder/search-index.d.ts +27 -0
- package/dist/src/source-finder/search-index.d.ts.map +1 -0
- package/dist/src/source-finder/snippet-utils.d.ts +90 -0
- package/dist/src/source-finder/snippet-utils.d.ts.map +1 -0
- package/dist/src/source-finder/source-lookup.d.ts +16 -0
- package/dist/src/source-finder/source-lookup.d.ts.map +1 -0
- package/dist/src/source-finder/types.d.ts +167 -0
- package/dist/src/source-finder/types.d.ts.map +1 -0
- package/dist/src/source-finder/variable-extraction.d.ts +37 -0
- package/dist/src/source-finder/variable-extraction.d.ts.map +1 -0
- package/dist/src/tailwind-colors.d.ts +54 -0
- package/dist/src/tailwind-colors.d.ts.map +1 -0
- package/dist/src/tsconfig.tsbuildinfo +1 -0
- package/dist/src/types.d.ts +367 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/utils.d.ts +61 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/vite-plugin.d.ts +14 -0
- package/dist/src/vite-plugin.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +80 -0
- package/src/build-processor.ts +784 -0
- package/src/collection-scanner.ts +304 -0
- package/src/component-registry.ts +393 -0
- package/src/config.ts +74 -0
- package/src/dev-middleware.ts +525 -0
- package/src/dist/src/tsconfig.tsbuildinfo +1 -0
- package/src/editor/ai.ts +185 -0
- package/src/editor/api.ts +513 -0
- package/src/editor/color-utils.ts +556 -0
- package/src/editor/components/ai-chat.tsx +632 -0
- package/src/editor/components/ai-tooltip.tsx +179 -0
- package/src/editor/components/attribute-editor.tsx +596 -0
- package/src/editor/components/block-editor.tsx +546 -0
- package/src/editor/components/collections-browser.tsx +248 -0
- package/src/editor/components/color-toolbar.tsx +314 -0
- package/src/editor/components/confirm-dialog.tsx +69 -0
- package/src/editor/components/create-page-modal.tsx +163 -0
- package/src/editor/components/editable-highlights.tsx +260 -0
- package/src/editor/components/error-boundary.tsx +87 -0
- package/src/editor/components/fields.tsx +387 -0
- package/src/editor/components/frontmatter-fields.tsx +469 -0
- package/src/editor/components/highlight-overlay.ts +229 -0
- package/src/editor/components/image-overlay.tsx +230 -0
- package/src/editor/components/markdown-editor-overlay.tsx +505 -0
- package/src/editor/components/markdown-inline-editor.tsx +780 -0
- package/src/editor/components/media-library.tsx +297 -0
- package/src/editor/components/outline.tsx +402 -0
- package/src/editor/components/redirect-countdown.tsx +45 -0
- package/src/editor/components/seo-editor.tsx +498 -0
- package/src/editor/components/text-style-toolbar.tsx +362 -0
- package/src/editor/components/toast/toast-container.tsx +15 -0
- package/src/editor/components/toast/toast.tsx +49 -0
- package/src/editor/components/toast/types.ts +7 -0
- package/src/editor/components/toolbar.tsx +366 -0
- package/src/editor/config.ts +12 -0
- package/src/editor/constants.ts +106 -0
- package/src/editor/context.tsx +38 -0
- package/src/editor/dom.ts +357 -0
- package/src/editor/editor.ts +1510 -0
- package/src/editor/env.d.ts +4 -0
- package/src/editor/history.ts +355 -0
- package/src/editor/hooks/index.ts +19 -0
- package/src/editor/hooks/useAIHandlers.ts +345 -0
- package/src/editor/hooks/useBlockEditorHandlers.ts +206 -0
- package/src/editor/hooks/useElementDetection.ts +284 -0
- package/src/editor/hooks/useImageHoverDetection.ts +102 -0
- package/src/editor/hooks/useTextSelection.ts +187 -0
- package/src/editor/hooks/useTooltipState.ts +126 -0
- package/src/editor/hooks/utils.ts +101 -0
- package/src/editor/index.tsx +481 -0
- package/src/editor/lib/cn.ts +4 -0
- package/src/editor/manifest.ts +25 -0
- package/src/editor/markdown-api.ts +209 -0
- package/src/editor/signals.ts +1351 -0
- package/src/editor/storage.ts +266 -0
- package/src/editor/styles.css +465 -0
- package/src/editor/text-styling.ts +773 -0
- package/src/editor/themes.ts +210 -0
- package/src/editor/types.ts +591 -0
- package/src/error-collector.ts +106 -0
- package/src/handlers/component-ops.ts +463 -0
- package/src/handlers/markdown-ops.ts +202 -0
- package/src/handlers/request-utils.ts +151 -0
- package/src/handlers/source-writer.ts +649 -0
- package/src/html-processor.ts +1108 -0
- package/src/index.ts +284 -0
- package/src/manifest-writer.ts +371 -0
- package/src/media/contember.ts +84 -0
- package/src/media/local.ts +114 -0
- package/src/media/s3.ts +133 -0
- package/src/media/types.ts +33 -0
- package/src/preview-generator.ts +293 -0
- package/src/seo-processor.ts +567 -0
- package/src/source-finder/ast-extractors.ts +185 -0
- package/src/source-finder/ast-parser.ts +150 -0
- package/src/source-finder/cache.ts +76 -0
- package/src/source-finder/collection-finder.ts +335 -0
- package/src/source-finder/cross-file-tracker.ts +741 -0
- package/src/source-finder/element-finder.ts +387 -0
- package/src/source-finder/image-finder.ts +283 -0
- package/src/source-finder/index.ts +37 -0
- package/src/source-finder/search-index.ts +525 -0
- package/src/source-finder/snippet-utils.ts +668 -0
- package/src/source-finder/source-lookup.ts +200 -0
- package/src/source-finder/types.ts +210 -0
- package/src/source-finder/variable-extraction.ts +406 -0
- package/src/tailwind-colors.ts +874 -0
- package/src/tsconfig.json +25 -0
- package/src/types.ts +406 -0
- package/src/utils.ts +186 -0
- package/src/vite-plugin.ts +42 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import fs from 'node:fs/promises'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { isMap, isPair, isScalar, LineCounter, parseDocument } from 'yaml'
|
|
4
|
+
|
|
5
|
+
import { getProjectRoot } from '../config'
|
|
6
|
+
import { getMarkdownFileCache } from './cache'
|
|
7
|
+
import { normalizeText } from './snippet-utils'
|
|
8
|
+
import type { CollectionInfo, MarkdownContent, SourceLocation } from './types'
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Markdown File Cache
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get cached markdown file content
|
|
16
|
+
*/
|
|
17
|
+
async function getCachedMarkdownFile(filePath: string): Promise<{ content: string; lines: string[] } | null> {
|
|
18
|
+
const cache = getMarkdownFileCache()
|
|
19
|
+
const cached = cache.get(filePath)
|
|
20
|
+
if (cached) return cached
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
24
|
+
const lines = content.split('\n')
|
|
25
|
+
const entry = { content, lines }
|
|
26
|
+
cache.set(filePath, entry)
|
|
27
|
+
return entry
|
|
28
|
+
} catch {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Collection Source Finding
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Find markdown collection file for a given page path.
|
|
39
|
+
*
|
|
40
|
+
* Uses slug-based reverse lookup: scans all collection directories for a
|
|
41
|
+
* matching entry regardless of the URL prefix. This supports localized or
|
|
42
|
+
* renamed routes (e.g. `/aktuality/my-article` with content in `src/content/news/`).
|
|
43
|
+
*
|
|
44
|
+
* @param pagePath - The URL path of the page (e.g., '/services/3d-tisk')
|
|
45
|
+
* @param contentDir - The content directory (default: 'src/content')
|
|
46
|
+
* @returns Collection info if found, undefined otherwise
|
|
47
|
+
*/
|
|
48
|
+
export async function findCollectionSource(
|
|
49
|
+
pagePath: string,
|
|
50
|
+
contentDir: string = 'src/content',
|
|
51
|
+
): Promise<CollectionInfo | undefined> {
|
|
52
|
+
// Remove leading/trailing slashes
|
|
53
|
+
const cleanPath = pagePath.replace(/^\/+|\/+$/g, '')
|
|
54
|
+
const pathParts = cleanPath.split('/')
|
|
55
|
+
|
|
56
|
+
if (pathParts.length < 1 || (pathParts.length === 1 && !pathParts[0])) {
|
|
57
|
+
return undefined
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const contentPath = path.join(getProjectRoot(), contentDir)
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await fs.access(contentPath)
|
|
64
|
+
} catch {
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// List all collection directories (skip _ and . prefixed)
|
|
69
|
+
let collectionDirs: string[]
|
|
70
|
+
try {
|
|
71
|
+
const entries = await fs.readdir(contentPath, { withFileTypes: true })
|
|
72
|
+
collectionDirs = entries
|
|
73
|
+
.filter(e => e.isDirectory() && !e.name.startsWith('_') && !e.name.startsWith('.'))
|
|
74
|
+
.map(e => e.name)
|
|
75
|
+
} catch {
|
|
76
|
+
return undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Try progressively longer tail segments as slug candidates
|
|
80
|
+
// For /a/b/c try: "c", then "b/c"
|
|
81
|
+
for (let i = pathParts.length - 1; i >= 1; i--) {
|
|
82
|
+
const slug = pathParts.slice(i).join('/')
|
|
83
|
+
const matches: { name: string; file: string }[] = []
|
|
84
|
+
|
|
85
|
+
for (const dir of collectionDirs) {
|
|
86
|
+
const collectionPath = path.join(contentPath, dir)
|
|
87
|
+
const mdFile = await findMarkdownFile(collectionPath, slug)
|
|
88
|
+
if (mdFile) {
|
|
89
|
+
matches.push({ name: dir, file: mdFile })
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (matches.length === 1 && matches[0]) {
|
|
94
|
+
return {
|
|
95
|
+
name: matches[0].name,
|
|
96
|
+
slug,
|
|
97
|
+
file: path.relative(getProjectRoot(), matches[0].file),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (matches.length > 1 && matches[0]) {
|
|
102
|
+
// Disambiguate: prefer collection whose name matches the URL prefix
|
|
103
|
+
const urlPrefix = pathParts[0]
|
|
104
|
+
const prefixMatch = matches.find(m => m.name === urlPrefix)
|
|
105
|
+
const chosen = prefixMatch || matches[0]
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
name: chosen.name,
|
|
109
|
+
slug,
|
|
110
|
+
file: path.relative(getProjectRoot(), chosen.file),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return undefined
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Find a markdown file in a collection directory by slug
|
|
120
|
+
*/
|
|
121
|
+
async function findMarkdownFile(collectionPath: string, slug: string): Promise<string | undefined> {
|
|
122
|
+
// Try direct match: slug.md or slug.mdx
|
|
123
|
+
const directPaths = [
|
|
124
|
+
path.join(collectionPath, `${slug}.md`),
|
|
125
|
+
path.join(collectionPath, `${slug}.mdx`),
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
for (const p of directPaths) {
|
|
129
|
+
try {
|
|
130
|
+
await fs.access(p)
|
|
131
|
+
return p
|
|
132
|
+
} catch {
|
|
133
|
+
// File doesn't exist, continue
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Try nested path for slugs with slashes
|
|
138
|
+
const slugParts = slug.split('/')
|
|
139
|
+
if (slugParts.length > 1) {
|
|
140
|
+
const nestedPath = path.join(collectionPath, ...slugParts.slice(0, -1))
|
|
141
|
+
const fileName = slugParts[slugParts.length - 1]
|
|
142
|
+
const nestedPaths = [
|
|
143
|
+
path.join(nestedPath, `${fileName}.md`),
|
|
144
|
+
path.join(nestedPath, `${fileName}.mdx`),
|
|
145
|
+
]
|
|
146
|
+
for (const p of nestedPaths) {
|
|
147
|
+
try {
|
|
148
|
+
await fs.access(p)
|
|
149
|
+
return p
|
|
150
|
+
} catch {
|
|
151
|
+
// File doesn't exist, continue
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Try index file in slug directory
|
|
157
|
+
const indexPaths = [
|
|
158
|
+
path.join(collectionPath, slug, 'index.md'),
|
|
159
|
+
path.join(collectionPath, slug, 'index.mdx'),
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
for (const p of indexPaths) {
|
|
163
|
+
try {
|
|
164
|
+
await fs.access(p)
|
|
165
|
+
return p
|
|
166
|
+
} catch {
|
|
167
|
+
// File doesn't exist, continue
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return undefined
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// Markdown Source Location Finding
|
|
176
|
+
// ============================================================================
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Find text content in a markdown file and return source location
|
|
180
|
+
* Only matches frontmatter fields, not body content (body is handled separately as a whole)
|
|
181
|
+
* @param textContent - The text content to search for
|
|
182
|
+
* @param collectionInfo - Collection information (name, slug, file path)
|
|
183
|
+
* @returns Source location if found in frontmatter
|
|
184
|
+
*/
|
|
185
|
+
export async function findMarkdownSourceLocation(
|
|
186
|
+
textContent: string,
|
|
187
|
+
collectionInfo: CollectionInfo,
|
|
188
|
+
): Promise<SourceLocation | undefined> {
|
|
189
|
+
try {
|
|
190
|
+
const filePath = path.join(getProjectRoot(), collectionInfo.file)
|
|
191
|
+
const cached = await getCachedMarkdownFile(filePath)
|
|
192
|
+
if (!cached) return undefined
|
|
193
|
+
|
|
194
|
+
const { lines } = cached
|
|
195
|
+
const normalizedSearch = normalizeText(textContent)
|
|
196
|
+
|
|
197
|
+
// Parse frontmatter
|
|
198
|
+
let frontmatterEnd = -1
|
|
199
|
+
let inFrontmatter = false
|
|
200
|
+
|
|
201
|
+
for (let i = 0; i < lines.length; i++) {
|
|
202
|
+
const line = lines[i]?.trim()
|
|
203
|
+
if (line === '---') {
|
|
204
|
+
if (!inFrontmatter) {
|
|
205
|
+
inFrontmatter = true
|
|
206
|
+
} else {
|
|
207
|
+
frontmatterEnd = i
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Search in frontmatter only (for title, subtitle, etc.)
|
|
214
|
+
if (frontmatterEnd > 0) {
|
|
215
|
+
for (let i = 1; i < frontmatterEnd; i++) {
|
|
216
|
+
const line = lines[i]
|
|
217
|
+
if (!line) continue
|
|
218
|
+
|
|
219
|
+
// Extract value from YAML key: value (keys can contain hyphens)
|
|
220
|
+
const match = line.match(/^\s*([\w-]+):\s*(.+)$/)
|
|
221
|
+
if (match) {
|
|
222
|
+
const key = match[1]
|
|
223
|
+
let value = match[2]?.trim() || ''
|
|
224
|
+
|
|
225
|
+
// Handle quoted strings
|
|
226
|
+
if (
|
|
227
|
+
(value.startsWith('"') && value.endsWith('"'))
|
|
228
|
+
|| (value.startsWith("'") && value.endsWith("'"))
|
|
229
|
+
) {
|
|
230
|
+
value = value.slice(1, -1)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (normalizeText(value) === normalizedSearch) {
|
|
234
|
+
return {
|
|
235
|
+
file: collectionInfo.file,
|
|
236
|
+
line: i + 1,
|
|
237
|
+
snippet: line,
|
|
238
|
+
type: 'collection',
|
|
239
|
+
variableName: key,
|
|
240
|
+
collectionName: collectionInfo.name,
|
|
241
|
+
collectionSlug: collectionInfo.slug,
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Body content is not searched line-by-line anymore
|
|
249
|
+
// Use parseMarkdownContent to get the full body as one entry
|
|
250
|
+
} catch {
|
|
251
|
+
// Error reading file
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return undefined
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ============================================================================
|
|
258
|
+
// Markdown Content Parsing
|
|
259
|
+
// ============================================================================
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Parse markdown file and extract frontmatter fields and full body content.
|
|
263
|
+
* Uses caching for better performance.
|
|
264
|
+
* @param collectionInfo - Collection information (name, slug, file path)
|
|
265
|
+
* @returns Parsed markdown content with frontmatter and body
|
|
266
|
+
*/
|
|
267
|
+
export async function parseMarkdownContent(
|
|
268
|
+
collectionInfo: CollectionInfo,
|
|
269
|
+
): Promise<MarkdownContent | undefined> {
|
|
270
|
+
try {
|
|
271
|
+
const filePath = path.join(getProjectRoot(), collectionInfo.file)
|
|
272
|
+
const cached = await getCachedMarkdownFile(filePath)
|
|
273
|
+
if (!cached) return undefined
|
|
274
|
+
|
|
275
|
+
const { lines } = cached
|
|
276
|
+
|
|
277
|
+
// Parse frontmatter
|
|
278
|
+
let frontmatterStart = -1
|
|
279
|
+
let frontmatterEnd = -1
|
|
280
|
+
|
|
281
|
+
for (let i = 0; i < lines.length; i++) {
|
|
282
|
+
const line = lines[i]?.trim()
|
|
283
|
+
if (line === '---') {
|
|
284
|
+
if (frontmatterStart === -1) {
|
|
285
|
+
frontmatterStart = i
|
|
286
|
+
} else {
|
|
287
|
+
frontmatterEnd = i
|
|
288
|
+
break
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const frontmatter: Record<string, { value: string; line: number }> = {}
|
|
294
|
+
|
|
295
|
+
// Extract frontmatter fields using yaml parser
|
|
296
|
+
if (frontmatterEnd > 0) {
|
|
297
|
+
const yamlStr = lines.slice(frontmatterStart + 1, frontmatterEnd).join('\n')
|
|
298
|
+
const lineCounter = new LineCounter()
|
|
299
|
+
const doc = parseDocument(yamlStr, { lineCounter })
|
|
300
|
+
|
|
301
|
+
if (isMap(doc.contents)) {
|
|
302
|
+
for (const pair of doc.contents.items) {
|
|
303
|
+
if (isPair(pair) && isScalar(pair.key)) {
|
|
304
|
+
const key = String(pair.key.value)
|
|
305
|
+
const value = isScalar(pair.value) ? String(pair.value.value) : ''
|
|
306
|
+
const keyRange = (pair.key as any).range
|
|
307
|
+
const yamlLine = keyRange ? lineCounter.linePos(keyRange[0]).line : 0
|
|
308
|
+
const fileLine = yamlLine + frontmatterStart + 1
|
|
309
|
+
if (key && value) {
|
|
310
|
+
frontmatter[key] = { value, line: fileLine }
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Extract body (everything after frontmatter)
|
|
318
|
+
const bodyStartLine = frontmatterEnd > 0 ? frontmatterEnd + 1 : 0
|
|
319
|
+
const bodyLines = lines.slice(bodyStartLine)
|
|
320
|
+
const body = bodyLines.join('\n').trim()
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
frontmatter,
|
|
324
|
+
body,
|
|
325
|
+
bodyStartLine: bodyStartLine + 1, // 1-indexed
|
|
326
|
+
file: collectionInfo.file,
|
|
327
|
+
collectionName: collectionInfo.name,
|
|
328
|
+
collectionSlug: collectionInfo.slug,
|
|
329
|
+
}
|
|
330
|
+
} catch {
|
|
331
|
+
// Error reading file
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return undefined
|
|
335
|
+
}
|