@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.
Files changed (269) hide show
  1. package/README.md +237 -0
  2. package/dist/src/build-processor.d.ts +20 -0
  3. package/dist/src/build-processor.d.ts.map +1 -0
  4. package/dist/src/collection-scanner.d.ts +6 -0
  5. package/dist/src/collection-scanner.d.ts.map +1 -0
  6. package/dist/src/component-registry.d.ts +63 -0
  7. package/dist/src/component-registry.d.ts.map +1 -0
  8. package/dist/src/config.d.ts +24 -0
  9. package/dist/src/config.d.ts.map +1 -0
  10. package/dist/src/dev-middleware.d.ts +20 -0
  11. package/dist/src/dev-middleware.d.ts.map +1 -0
  12. package/dist/src/editor/ai.d.ts +60 -0
  13. package/dist/src/editor/ai.d.ts.map +1 -0
  14. package/dist/src/editor/api.d.ts +140 -0
  15. package/dist/src/editor/api.d.ts.map +1 -0
  16. package/dist/src/editor/color-utils.d.ts +106 -0
  17. package/dist/src/editor/color-utils.d.ts.map +1 -0
  18. package/dist/src/editor/components/ai-chat.d.ts +11 -0
  19. package/dist/src/editor/components/ai-chat.d.ts.map +1 -0
  20. package/dist/src/editor/components/ai-tooltip.d.ts +12 -0
  21. package/dist/src/editor/components/ai-tooltip.d.ts.map +1 -0
  22. package/dist/src/editor/components/attribute-editor.d.ts +5 -0
  23. package/dist/src/editor/components/attribute-editor.d.ts.map +1 -0
  24. package/dist/src/editor/components/block-editor.d.ts +12 -0
  25. package/dist/src/editor/components/block-editor.d.ts.map +1 -0
  26. package/dist/src/editor/components/collections-browser.d.ts +2 -0
  27. package/dist/src/editor/components/collections-browser.d.ts.map +1 -0
  28. package/dist/src/editor/components/color-toolbar.d.ts +12 -0
  29. package/dist/src/editor/components/color-toolbar.d.ts.map +1 -0
  30. package/dist/src/editor/components/confirm-dialog.d.ts +2 -0
  31. package/dist/src/editor/components/confirm-dialog.d.ts.map +1 -0
  32. package/dist/src/editor/components/create-page-modal.d.ts +2 -0
  33. package/dist/src/editor/components/create-page-modal.d.ts.map +1 -0
  34. package/dist/src/editor/components/editable-highlights.d.ts +9 -0
  35. package/dist/src/editor/components/editable-highlights.d.ts.map +1 -0
  36. package/dist/src/editor/components/error-boundary.d.ts +32 -0
  37. package/dist/src/editor/components/error-boundary.d.ts.map +1 -0
  38. package/dist/src/editor/components/fields.d.ts +75 -0
  39. package/dist/src/editor/components/fields.d.ts.map +1 -0
  40. package/dist/src/editor/components/frontmatter-fields.d.ts +29 -0
  41. package/dist/src/editor/components/frontmatter-fields.d.ts.map +1 -0
  42. package/dist/src/editor/components/highlight-overlay.d.ts +64 -0
  43. package/dist/src/editor/components/highlight-overlay.d.ts.map +1 -0
  44. package/dist/src/editor/components/image-overlay.d.ts +12 -0
  45. package/dist/src/editor/components/image-overlay.d.ts.map +1 -0
  46. package/dist/src/editor/components/markdown-editor-overlay.d.ts +6 -0
  47. package/dist/src/editor/components/markdown-editor-overlay.d.ts.map +1 -0
  48. package/dist/src/editor/components/markdown-inline-editor.d.ts +10 -0
  49. package/dist/src/editor/components/markdown-inline-editor.d.ts.map +1 -0
  50. package/dist/src/editor/components/media-library.d.ts +2 -0
  51. package/dist/src/editor/components/media-library.d.ts.map +1 -0
  52. package/dist/src/editor/components/outline.d.ts +21 -0
  53. package/dist/src/editor/components/outline.d.ts.map +1 -0
  54. package/dist/src/editor/components/redirect-countdown.d.ts +2 -0
  55. package/dist/src/editor/components/redirect-countdown.d.ts.map +1 -0
  56. package/dist/src/editor/components/seo-editor.d.ts +2 -0
  57. package/dist/src/editor/components/seo-editor.d.ts.map +1 -0
  58. package/dist/src/editor/components/text-style-toolbar.d.ts +8 -0
  59. package/dist/src/editor/components/text-style-toolbar.d.ts.map +1 -0
  60. package/dist/src/editor/components/toast/toast-container.d.ts +7 -0
  61. package/dist/src/editor/components/toast/toast-container.d.ts.map +1 -0
  62. package/dist/src/editor/components/toast/toast.d.ts +7 -0
  63. package/dist/src/editor/components/toast/toast.d.ts.map +1 -0
  64. package/dist/src/editor/components/toast/types.d.ts +7 -0
  65. package/dist/src/editor/components/toast/types.d.ts.map +1 -0
  66. package/dist/src/editor/components/toolbar.d.ts +21 -0
  67. package/dist/src/editor/components/toolbar.d.ts.map +1 -0
  68. package/dist/src/editor/config.d.ts +4 -0
  69. package/dist/src/editor/config.d.ts.map +1 -0
  70. package/dist/src/editor/constants.d.ts +101 -0
  71. package/dist/src/editor/constants.d.ts.map +1 -0
  72. package/dist/src/editor/context.d.ts +14 -0
  73. package/dist/src/editor/context.d.ts.map +1 -0
  74. package/dist/src/editor/dom.d.ts +77 -0
  75. package/dist/src/editor/dom.d.ts.map +1 -0
  76. package/dist/src/editor/editor.d.ts +64 -0
  77. package/dist/src/editor/editor.d.ts.map +1 -0
  78. package/dist/src/editor/history.d.ts +20 -0
  79. package/dist/src/editor/history.d.ts.map +1 -0
  80. package/dist/src/editor/hooks/index.d.ts +14 -0
  81. package/dist/src/editor/hooks/index.d.ts.map +1 -0
  82. package/dist/src/editor/hooks/useAIHandlers.d.ts +22 -0
  83. package/dist/src/editor/hooks/useAIHandlers.d.ts.map +1 -0
  84. package/dist/src/editor/hooks/useBlockEditorHandlers.d.ts +18 -0
  85. package/dist/src/editor/hooks/useBlockEditorHandlers.d.ts.map +1 -0
  86. package/dist/src/editor/hooks/useElementDetection.d.ts +26 -0
  87. package/dist/src/editor/hooks/useElementDetection.d.ts.map +1 -0
  88. package/dist/src/editor/hooks/useImageHoverDetection.d.ts +12 -0
  89. package/dist/src/editor/hooks/useImageHoverDetection.d.ts.map +1 -0
  90. package/dist/src/editor/hooks/useTextSelection.d.ts +23 -0
  91. package/dist/src/editor/hooks/useTextSelection.d.ts.map +1 -0
  92. package/dist/src/editor/hooks/useTooltipState.d.ts +19 -0
  93. package/dist/src/editor/hooks/useTooltipState.d.ts.map +1 -0
  94. package/dist/src/editor/hooks/utils.d.ts +32 -0
  95. package/dist/src/editor/hooks/utils.d.ts.map +1 -0
  96. package/dist/src/editor/index.d.ts +12 -0
  97. package/dist/src/editor/index.d.ts.map +1 -0
  98. package/dist/src/editor/lib/cn.d.ts +3 -0
  99. package/dist/src/editor/lib/cn.d.ts.map +1 -0
  100. package/dist/src/editor/manifest.d.ts +19 -0
  101. package/dist/src/editor/manifest.d.ts.map +1 -0
  102. package/dist/src/editor/markdown-api.d.ts +36 -0
  103. package/dist/src/editor/markdown-api.d.ts.map +1 -0
  104. package/dist/src/editor/signals.d.ts +242 -0
  105. package/dist/src/editor/signals.d.ts.map +1 -0
  106. package/dist/src/editor/storage.d.ts +27 -0
  107. package/dist/src/editor/storage.d.ts.map +1 -0
  108. package/dist/src/editor/text-styling.d.ts +350 -0
  109. package/dist/src/editor/text-styling.d.ts.map +1 -0
  110. package/dist/src/editor/themes.d.ts +38 -0
  111. package/dist/src/editor/themes.d.ts.map +1 -0
  112. package/dist/src/editor/types.d.ts +454 -0
  113. package/dist/src/editor/types.d.ts.map +1 -0
  114. package/dist/src/error-collector.d.ts +56 -0
  115. package/dist/src/error-collector.d.ts.map +1 -0
  116. package/dist/src/handlers/component-ops.d.ts +34 -0
  117. package/dist/src/handlers/component-ops.d.ts.map +1 -0
  118. package/dist/src/handlers/markdown-ops.d.ts +41 -0
  119. package/dist/src/handlers/markdown-ops.d.ts.map +1 -0
  120. package/dist/src/handlers/request-utils.d.ts +20 -0
  121. package/dist/src/handlers/request-utils.d.ts.map +1 -0
  122. package/dist/src/handlers/source-writer.d.ts +51 -0
  123. package/dist/src/handlers/source-writer.d.ts.map +1 -0
  124. package/dist/src/html-processor.d.ts +63 -0
  125. package/dist/src/html-processor.d.ts.map +1 -0
  126. package/dist/src/index.d.ts +41 -0
  127. package/dist/src/index.d.ts.map +1 -0
  128. package/dist/src/manifest-writer.d.ts +111 -0
  129. package/dist/src/manifest-writer.d.ts.map +1 -0
  130. package/dist/src/media/contember.d.ts +15 -0
  131. package/dist/src/media/contember.d.ts.map +1 -0
  132. package/dist/src/media/local.d.ts +9 -0
  133. package/dist/src/media/local.d.ts.map +1 -0
  134. package/dist/src/media/s3.d.ts +12 -0
  135. package/dist/src/media/s3.d.ts.map +1 -0
  136. package/dist/src/media/types.d.ts +40 -0
  137. package/dist/src/media/types.d.ts.map +1 -0
  138. package/dist/src/preview-generator.d.ts +19 -0
  139. package/dist/src/preview-generator.d.ts.map +1 -0
  140. package/dist/src/seo-processor.d.ts +23 -0
  141. package/dist/src/seo-processor.d.ts.map +1 -0
  142. package/dist/src/source-finder/ast-extractors.d.ts +35 -0
  143. package/dist/src/source-finder/ast-extractors.d.ts.map +1 -0
  144. package/dist/src/source-finder/ast-parser.d.ts +16 -0
  145. package/dist/src/source-finder/ast-parser.d.ts.map +1 -0
  146. package/dist/src/source-finder/cache.d.ts +18 -0
  147. package/dist/src/source-finder/cache.d.ts.map +1 -0
  148. package/dist/src/source-finder/collection-finder.d.ts +29 -0
  149. package/dist/src/source-finder/collection-finder.d.ts.map +1 -0
  150. package/dist/src/source-finder/cross-file-tracker.d.ts +39 -0
  151. package/dist/src/source-finder/cross-file-tracker.d.ts.map +1 -0
  152. package/dist/src/source-finder/element-finder.d.ts +42 -0
  153. package/dist/src/source-finder/element-finder.d.ts.map +1 -0
  154. package/dist/src/source-finder/image-finder.d.ts +24 -0
  155. package/dist/src/source-finder/image-finder.d.ts.map +1 -0
  156. package/dist/src/source-finder/index.d.ts +9 -0
  157. package/dist/src/source-finder/index.d.ts.map +1 -0
  158. package/dist/src/source-finder/search-index.d.ts +27 -0
  159. package/dist/src/source-finder/search-index.d.ts.map +1 -0
  160. package/dist/src/source-finder/snippet-utils.d.ts +90 -0
  161. package/dist/src/source-finder/snippet-utils.d.ts.map +1 -0
  162. package/dist/src/source-finder/source-lookup.d.ts +16 -0
  163. package/dist/src/source-finder/source-lookup.d.ts.map +1 -0
  164. package/dist/src/source-finder/types.d.ts +167 -0
  165. package/dist/src/source-finder/types.d.ts.map +1 -0
  166. package/dist/src/source-finder/variable-extraction.d.ts +37 -0
  167. package/dist/src/source-finder/variable-extraction.d.ts.map +1 -0
  168. package/dist/src/tailwind-colors.d.ts +54 -0
  169. package/dist/src/tailwind-colors.d.ts.map +1 -0
  170. package/dist/src/tsconfig.tsbuildinfo +1 -0
  171. package/dist/src/types.d.ts +367 -0
  172. package/dist/src/types.d.ts.map +1 -0
  173. package/dist/src/utils.d.ts +61 -0
  174. package/dist/src/utils.d.ts.map +1 -0
  175. package/dist/src/vite-plugin.d.ts +14 -0
  176. package/dist/src/vite-plugin.d.ts.map +1 -0
  177. package/dist/types/tsconfig.tsbuildinfo +1 -0
  178. package/package.json +80 -0
  179. package/src/build-processor.ts +784 -0
  180. package/src/collection-scanner.ts +304 -0
  181. package/src/component-registry.ts +393 -0
  182. package/src/config.ts +74 -0
  183. package/src/dev-middleware.ts +525 -0
  184. package/src/dist/src/tsconfig.tsbuildinfo +1 -0
  185. package/src/editor/ai.ts +185 -0
  186. package/src/editor/api.ts +513 -0
  187. package/src/editor/color-utils.ts +556 -0
  188. package/src/editor/components/ai-chat.tsx +632 -0
  189. package/src/editor/components/ai-tooltip.tsx +179 -0
  190. package/src/editor/components/attribute-editor.tsx +596 -0
  191. package/src/editor/components/block-editor.tsx +546 -0
  192. package/src/editor/components/collections-browser.tsx +248 -0
  193. package/src/editor/components/color-toolbar.tsx +314 -0
  194. package/src/editor/components/confirm-dialog.tsx +69 -0
  195. package/src/editor/components/create-page-modal.tsx +163 -0
  196. package/src/editor/components/editable-highlights.tsx +260 -0
  197. package/src/editor/components/error-boundary.tsx +87 -0
  198. package/src/editor/components/fields.tsx +387 -0
  199. package/src/editor/components/frontmatter-fields.tsx +469 -0
  200. package/src/editor/components/highlight-overlay.ts +229 -0
  201. package/src/editor/components/image-overlay.tsx +230 -0
  202. package/src/editor/components/markdown-editor-overlay.tsx +505 -0
  203. package/src/editor/components/markdown-inline-editor.tsx +780 -0
  204. package/src/editor/components/media-library.tsx +297 -0
  205. package/src/editor/components/outline.tsx +402 -0
  206. package/src/editor/components/redirect-countdown.tsx +45 -0
  207. package/src/editor/components/seo-editor.tsx +498 -0
  208. package/src/editor/components/text-style-toolbar.tsx +362 -0
  209. package/src/editor/components/toast/toast-container.tsx +15 -0
  210. package/src/editor/components/toast/toast.tsx +49 -0
  211. package/src/editor/components/toast/types.ts +7 -0
  212. package/src/editor/components/toolbar.tsx +366 -0
  213. package/src/editor/config.ts +12 -0
  214. package/src/editor/constants.ts +106 -0
  215. package/src/editor/context.tsx +38 -0
  216. package/src/editor/dom.ts +357 -0
  217. package/src/editor/editor.ts +1510 -0
  218. package/src/editor/env.d.ts +4 -0
  219. package/src/editor/history.ts +355 -0
  220. package/src/editor/hooks/index.ts +19 -0
  221. package/src/editor/hooks/useAIHandlers.ts +345 -0
  222. package/src/editor/hooks/useBlockEditorHandlers.ts +206 -0
  223. package/src/editor/hooks/useElementDetection.ts +284 -0
  224. package/src/editor/hooks/useImageHoverDetection.ts +102 -0
  225. package/src/editor/hooks/useTextSelection.ts +187 -0
  226. package/src/editor/hooks/useTooltipState.ts +126 -0
  227. package/src/editor/hooks/utils.ts +101 -0
  228. package/src/editor/index.tsx +481 -0
  229. package/src/editor/lib/cn.ts +4 -0
  230. package/src/editor/manifest.ts +25 -0
  231. package/src/editor/markdown-api.ts +209 -0
  232. package/src/editor/signals.ts +1351 -0
  233. package/src/editor/storage.ts +266 -0
  234. package/src/editor/styles.css +465 -0
  235. package/src/editor/text-styling.ts +773 -0
  236. package/src/editor/themes.ts +210 -0
  237. package/src/editor/types.ts +591 -0
  238. package/src/error-collector.ts +106 -0
  239. package/src/handlers/component-ops.ts +463 -0
  240. package/src/handlers/markdown-ops.ts +202 -0
  241. package/src/handlers/request-utils.ts +151 -0
  242. package/src/handlers/source-writer.ts +649 -0
  243. package/src/html-processor.ts +1108 -0
  244. package/src/index.ts +284 -0
  245. package/src/manifest-writer.ts +371 -0
  246. package/src/media/contember.ts +84 -0
  247. package/src/media/local.ts +114 -0
  248. package/src/media/s3.ts +133 -0
  249. package/src/media/types.ts +33 -0
  250. package/src/preview-generator.ts +293 -0
  251. package/src/seo-processor.ts +567 -0
  252. package/src/source-finder/ast-extractors.ts +185 -0
  253. package/src/source-finder/ast-parser.ts +150 -0
  254. package/src/source-finder/cache.ts +76 -0
  255. package/src/source-finder/collection-finder.ts +335 -0
  256. package/src/source-finder/cross-file-tracker.ts +741 -0
  257. package/src/source-finder/element-finder.ts +387 -0
  258. package/src/source-finder/image-finder.ts +283 -0
  259. package/src/source-finder/index.ts +37 -0
  260. package/src/source-finder/search-index.ts +525 -0
  261. package/src/source-finder/snippet-utils.ts +668 -0
  262. package/src/source-finder/source-lookup.ts +200 -0
  263. package/src/source-finder/types.ts +210 -0
  264. package/src/source-finder/variable-extraction.ts +406 -0
  265. package/src/tailwind-colors.ts +874 -0
  266. package/src/tsconfig.json +25 -0
  267. package/src/types.ts +406 -0
  268. package/src/utils.ts +186 -0
  269. package/src/vite-plugin.ts +42 -0
@@ -0,0 +1,185 @@
1
+ import type { BabelNode, LineTransformer, VariableDefinition } from './types'
2
+
3
+ // ============================================================================
4
+ // String Value Extraction
5
+ // ============================================================================
6
+
7
+ /**
8
+ * Extract string value from a Babel node (StringLiteral or simple TemplateLiteral)
9
+ */
10
+ export function getStringValue(node: BabelNode): string | null {
11
+ if (node.type === 'StringLiteral') {
12
+ return node.value as string
13
+ }
14
+ if (node.type === 'TemplateLiteral') {
15
+ const quasis = node.quasis as Array<{ value: { cooked: string | null } }> | undefined
16
+ const expressions = node.expressions as unknown[] | undefined
17
+ if (quasis?.length === 1 && expressions?.length === 0) {
18
+ return quasis[0]?.value.cooked ?? null
19
+ }
20
+ }
21
+ return null
22
+ }
23
+
24
+ // ============================================================================
25
+ // Object and Array Extraction
26
+ // ============================================================================
27
+
28
+ /**
29
+ * Extract property name from an object key node.
30
+ * Handles both `{ name: value }` (Identifier) and `{ "name": value }` (StringLiteral).
31
+ */
32
+ function getKeyName(key: BabelNode): string | null {
33
+ if (key.type === 'Identifier') return key.name as string
34
+ if (key.type === 'StringLiteral') return key.value as string
35
+ return null
36
+ }
37
+
38
+ /**
39
+ * Recursively extract properties from an object expression
40
+ * @param objNode - The ObjectExpression node
41
+ * @param parentPath - The full path to this object (e.g., 'config' or 'config.nav')
42
+ * @param definitions - Array to collect definitions into
43
+ * @param lineTransformer - Transforms Babel line numbers to file line numbers
44
+ */
45
+ export function extractObjectProperties(
46
+ objNode: BabelNode,
47
+ parentPath: string,
48
+ definitions: VariableDefinition[],
49
+ lineTransformer: LineTransformer,
50
+ ): void {
51
+ const properties = objNode.properties as BabelNode[] | undefined
52
+ for (const prop of properties ?? []) {
53
+ if (prop.type !== 'ObjectProperty') continue
54
+ const key = prop.key as BabelNode | undefined
55
+ const value = prop.value as BabelNode | undefined
56
+ if (!key || !value) continue
57
+ const propName = getKeyName(key)
58
+ if (!propName) continue
59
+ const fullPath = `${parentPath}.${propName}`
60
+ const propLoc = prop.loc as { start: { line: number } } | undefined
61
+ const propLine = lineTransformer(propLoc?.start.line ?? 1)
62
+
63
+ const stringValue = getStringValue(value)
64
+ if (stringValue !== null) {
65
+ definitions.push({
66
+ name: propName,
67
+ value: stringValue,
68
+ line: propLine,
69
+ parentName: parentPath,
70
+ })
71
+ }
72
+
73
+ // Recurse for nested objects
74
+ if (value.type === 'ObjectExpression') {
75
+ extractObjectProperties(value, fullPath, definitions, lineTransformer)
76
+ }
77
+
78
+ // Handle arrays within objects
79
+ if (value.type === 'ArrayExpression') {
80
+ extractArrayElements(value, fullPath, definitions, lineTransformer, propLine)
81
+ }
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Extract elements from an array expression
87
+ * @param arrNode - The ArrayExpression node
88
+ * @param parentPath - The full path to this array (e.g., 'items' or 'config.items')
89
+ * @param definitions - Array to collect definitions into
90
+ * @param lineTransformer - Transforms Babel line numbers to file line numbers
91
+ * @param defaultLine - Fallback line if element has no location
92
+ */
93
+ export function extractArrayElements(
94
+ arrNode: BabelNode,
95
+ parentPath: string,
96
+ definitions: VariableDefinition[],
97
+ lineTransformer: LineTransformer,
98
+ defaultLine: number,
99
+ ): void {
100
+ const elements = arrNode.elements as BabelNode[] | undefined
101
+ for (let i = 0; i < (elements?.length ?? 0); i++) {
102
+ const elem = elements![i]
103
+ if (!elem) continue
104
+
105
+ const elemLoc = elem.loc as { start: { line: number } } | undefined
106
+ const elemLine = elemLoc ? lineTransformer(elemLoc.start.line) : defaultLine
107
+ const indexPath = `${parentPath}[${i}]`
108
+
109
+ // Handle string values in array
110
+ const elemValue = getStringValue(elem)
111
+ if (elemValue !== null) {
112
+ definitions.push({
113
+ name: String(i),
114
+ value: elemValue,
115
+ line: elemLine,
116
+ parentName: parentPath,
117
+ })
118
+ }
119
+
120
+ // Handle array of objects: [{ text: 'Home' }] or [{ "text": 'Home' }]
121
+ if (elem.type === 'ObjectExpression') {
122
+ const objProperties = elem.properties as BabelNode[] | undefined
123
+ for (const prop of objProperties ?? []) {
124
+ if (prop.type !== 'ObjectProperty') continue
125
+ const key = prop.key as BabelNode | undefined
126
+ const value = prop.value as BabelNode | undefined
127
+ if (!key || !value) continue
128
+ const propName = getKeyName(key)
129
+ if (!propName) continue
130
+ const propLoc = prop.loc as { start: { line: number } } | undefined
131
+ const propLine = propLoc ? lineTransformer(propLoc.start.line) : elemLine
132
+
133
+ const stringValue = getStringValue(value)
134
+ if (stringValue !== null) {
135
+ definitions.push({
136
+ name: propName,
137
+ value: stringValue,
138
+ line: propLine,
139
+ parentName: indexPath,
140
+ })
141
+ }
142
+
143
+ // Recurse for nested objects within array elements
144
+ if (value.type === 'ObjectExpression') {
145
+ extractObjectProperties(value, `${indexPath}.${propName}`, definitions, lineTransformer)
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ // ============================================================================
153
+ // Path Building Utilities
154
+ // ============================================================================
155
+
156
+ /**
157
+ * Build the full path for a variable definition.
158
+ * For array indices (numeric names), uses bracket notation: items[0]
159
+ * For object properties, uses dot notation: config.nav.title
160
+ */
161
+ export function buildDefinitionPath(def: VariableDefinition): string {
162
+ if (!def.parentName) {
163
+ return def.name
164
+ }
165
+ // Check if the name is a numeric index (for arrays)
166
+ if (/^\d+$/.test(def.name)) {
167
+ return `${def.parentName}[${def.name}]`
168
+ }
169
+ return `${def.parentName}.${def.name}`
170
+ }
171
+
172
+ /**
173
+ * Parse an expression path and extract the full path for variable lookup.
174
+ * Handles patterns like: varName, obj.prop, items[0], config.nav.title, links[0].text
175
+ * @returns The full expression path or null if not a simple variable reference
176
+ */
177
+ export function parseExpressionPath(exprText: string): string | null {
178
+ // Match patterns like: varName, obj.prop, items[0], config.nav.title, links[0].text
179
+ // Pattern breakdown: word characters, dots, and bracket notation with numbers
180
+ const match = exprText.match(/^\s*([\w]+(?:\.[\w]+|\[\d+\])*(?:\.[\w]+)?)\s*$/)
181
+ if (match) {
182
+ return match[1]!
183
+ }
184
+ return null
185
+ }
@@ -0,0 +1,150 @@
1
+ import { parse as parseAstro } from '@astrojs/compiler'
2
+ import type { Node as AstroNode } from '@astrojs/compiler/types'
3
+ import { parse as parseBabel } from '@babel/parser'
4
+ import fs from 'node:fs/promises'
5
+
6
+ import { getErrorCollector } from '../error-collector'
7
+ import { getParsedFileCache } from './cache'
8
+ import type { BabelFile, CachedParsedFile, ParsedAstroFile } from './types'
9
+ import { extractImports, extractPropAliases, extractVariableDefinitions } from './variable-extraction'
10
+
11
+ // ============================================================================
12
+ // Astro File Parsing
13
+ // ============================================================================
14
+
15
+ /**
16
+ * Parse an Astro file and return both template AST and frontmatter content
17
+ */
18
+ export async function parseAstroFile(content: string): Promise<ParsedAstroFile> {
19
+ const result = await parseAstro(content, { position: true })
20
+
21
+ // Find frontmatter node
22
+ let frontmatterContent: string | null = null
23
+ let frontmatterStartLine = 0
24
+
25
+ for (const child of result.ast.children) {
26
+ if (child.type === 'frontmatter') {
27
+ frontmatterContent = child.value
28
+ frontmatterStartLine = child.position?.start.line ?? 1
29
+ break
30
+ }
31
+ }
32
+
33
+ return {
34
+ ast: result.ast,
35
+ frontmatterContent,
36
+ frontmatterStartLine,
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Parse frontmatter JavaScript/TypeScript with Babel
42
+ * @param content - The frontmatter content to parse
43
+ * @param filePath - Optional file path for error reporting
44
+ */
45
+ export function parseFrontmatter(content: string, filePath?: string): BabelFile | null {
46
+ try {
47
+ return parseBabel(content, {
48
+ sourceType: 'module',
49
+ plugins: ['typescript'],
50
+ errorRecovery: true,
51
+ }) as unknown as BabelFile
52
+ } catch (error) {
53
+ // Record parse errors for aggregated reporting
54
+ if (filePath) {
55
+ getErrorCollector().addWarning(
56
+ `Frontmatter parse: ${filePath}`,
57
+ error instanceof Error ? error.message : String(error),
58
+ )
59
+ }
60
+ return null
61
+ }
62
+ }
63
+
64
+ // ============================================================================
65
+ // Cached File Access
66
+ // ============================================================================
67
+
68
+ /** Deduplicates concurrent parsing requests for the same file */
69
+ const inFlightParsing = new Map<string, Promise<CachedParsedFile | null>>()
70
+
71
+ /**
72
+ * Get a cached parsed file, parsing it if not cached.
73
+ * Concurrent requests for the same file are deduplicated to avoid
74
+ * redundant parsing and potential race conditions.
75
+ */
76
+ export async function getCachedParsedFile(filePath: string): Promise<CachedParsedFile | null> {
77
+ const cache = getParsedFileCache()
78
+ const cached = cache.get(filePath)
79
+ if (cached) return cached
80
+
81
+ // Deduplicate concurrent requests for the same file
82
+ const inFlight = inFlightParsing.get(filePath)
83
+ if (inFlight) return inFlight
84
+
85
+ const promise = parseAndCacheFile(filePath, cache)
86
+ inFlightParsing.set(filePath, promise)
87
+
88
+ try {
89
+ return await promise
90
+ } finally {
91
+ inFlightParsing.delete(filePath)
92
+ }
93
+ }
94
+
95
+ async function parseAndCacheFile(
96
+ filePath: string,
97
+ cache: Map<string, CachedParsedFile>,
98
+ ): Promise<CachedParsedFile | null> {
99
+ try {
100
+ const content = await fs.readFile(filePath, 'utf-8')
101
+ const lines = content.split('\n')
102
+
103
+ // Only parse .astro files with AST
104
+ if (!filePath.endsWith('.astro')) {
105
+ // For tsx/jsx, just cache content/lines for regex search
106
+ const entry: CachedParsedFile = {
107
+ content,
108
+ lines,
109
+ ast: { type: 'root', children: [] } as unknown as AstroNode,
110
+ frontmatterContent: null,
111
+ frontmatterStartLine: 0,
112
+ variableDefinitions: [],
113
+ propAliases: new Map(),
114
+ imports: [],
115
+ }
116
+ cache.set(filePath, entry)
117
+ return entry
118
+ }
119
+
120
+ const { ast, frontmatterContent, frontmatterStartLine } = await parseAstroFile(content)
121
+
122
+ let variableDefinitions: CachedParsedFile['variableDefinitions'] = []
123
+ let propAliases: Map<string, string> = new Map()
124
+ let imports: CachedParsedFile['imports'] = []
125
+ if (frontmatterContent) {
126
+ const frontmatterAst = parseFrontmatter(frontmatterContent, filePath)
127
+ if (frontmatterAst) {
128
+ variableDefinitions = extractVariableDefinitions(frontmatterAst, frontmatterStartLine)
129
+ propAliases = extractPropAliases(frontmatterAst)
130
+ imports = extractImports(frontmatterAst)
131
+ }
132
+ }
133
+
134
+ const entry: CachedParsedFile = {
135
+ content,
136
+ lines,
137
+ ast,
138
+ frontmatterContent,
139
+ frontmatterStartLine,
140
+ variableDefinitions,
141
+ propAliases,
142
+ imports,
143
+ }
144
+
145
+ cache.set(filePath, entry)
146
+ return entry
147
+ } catch {
148
+ return null
149
+ }
150
+ }
@@ -0,0 +1,76 @@
1
+ import type { CachedParsedFile, ImageIndexEntry, SearchIndexEntry } from './types'
2
+
3
+ // ============================================================================
4
+ // File Parsing Cache - Avoid re-parsing the same files
5
+ // ============================================================================
6
+
7
+ /** Cache for parsed Astro files - cleared between builds */
8
+ const parsedFileCache = new Map<string, CachedParsedFile>()
9
+
10
+ /** Cache for directory listings - cleared between builds */
11
+ const directoryCache = new Map<string, string[]>()
12
+
13
+ /** Cache for markdown file contents - cleared between builds */
14
+ const markdownFileCache = new Map<string, { content: string; lines: string[] }>()
15
+
16
+ /** Search indexes built once per build — use the same array instance and clear with length=0 to avoid stale references */
17
+ const textSearchIndex: SearchIndexEntry[] = []
18
+ const imageSearchIndex: ImageIndexEntry[] = []
19
+ let searchIndexInitialized = false
20
+
21
+ // ============================================================================
22
+ // Cache Access Functions
23
+ // ============================================================================
24
+
25
+ export function getParsedFileCache(): Map<string, CachedParsedFile> {
26
+ return parsedFileCache
27
+ }
28
+
29
+ export function getDirectoryCache(): Map<string, string[]> {
30
+ return directoryCache
31
+ }
32
+
33
+ export function getMarkdownFileCache(): Map<string, { content: string; lines: string[] }> {
34
+ return markdownFileCache
35
+ }
36
+
37
+ export function getTextSearchIndex(): SearchIndexEntry[] {
38
+ return textSearchIndex
39
+ }
40
+
41
+ export function getImageSearchIndex(): ImageIndexEntry[] {
42
+ return imageSearchIndex
43
+ }
44
+
45
+ export function isSearchIndexInitialized(): boolean {
46
+ return searchIndexInitialized
47
+ }
48
+
49
+ export function setSearchIndexInitialized(value: boolean): void {
50
+ searchIndexInitialized = value
51
+ }
52
+
53
+ export function addToTextSearchIndex(entry: SearchIndexEntry): void {
54
+ textSearchIndex.push(entry)
55
+ }
56
+
57
+ export function addToImageSearchIndex(entry: ImageIndexEntry): void {
58
+ imageSearchIndex.push(entry)
59
+ }
60
+
61
+ // ============================================================================
62
+ // Cache Clear Function
63
+ // ============================================================================
64
+
65
+ /**
66
+ * Clear all caches - call at start of each build
67
+ */
68
+ export function clearSourceFinderCache(): void {
69
+ parsedFileCache.clear()
70
+ directoryCache.clear()
71
+ markdownFileCache.clear()
72
+ // Clear arrays in-place to avoid stale references from consumers
73
+ textSearchIndex.length = 0
74
+ imageSearchIndex.length = 0
75
+ searchIndexInitialized = false
76
+ }