davaux 0.8.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 (333) hide show
  1. package/BASELINE.md +169 -0
  2. package/CLAUDE.md +518 -0
  3. package/LICENSE +21 -0
  4. package/README.md +36 -0
  5. package/ROADMAP.md +198 -0
  6. package/build.mjs +101 -0
  7. package/client/control.ts +247 -0
  8. package/client/hydrate.ts +37 -0
  9. package/client/index.ts +19 -0
  10. package/client/jsx-runtime.ts +209 -0
  11. package/client/resource.ts +122 -0
  12. package/client/signal.ts +211 -0
  13. package/client/store.ts +110 -0
  14. package/client/useHead.ts +63 -0
  15. package/dist/build/config.d.ts +3 -0
  16. package/dist/build/config.d.ts.map +1 -0
  17. package/dist/build/config.js +38 -0
  18. package/dist/build/config.js.map +7 -0
  19. package/dist/build/index.d.ts +2 -0
  20. package/dist/build/index.d.ts.map +1 -0
  21. package/dist/build/index.js +13 -0
  22. package/dist/build/index.js.map +7 -0
  23. package/dist/build/plugins.d.ts +7 -0
  24. package/dist/build/plugins.d.ts.map +1 -0
  25. package/dist/build/plugins.js +85 -0
  26. package/dist/build/plugins.js.map +7 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +427 -0
  30. package/dist/cli.js.map +7 -0
  31. package/dist/client/control.d.ts +49 -0
  32. package/dist/client/control.d.ts.map +1 -0
  33. package/dist/client/control.js +154 -0
  34. package/dist/client/control.js.map +7 -0
  35. package/dist/client/hydrate.d.ts +7 -0
  36. package/dist/client/hydrate.d.ts.map +1 -0
  37. package/dist/client/hydrate.js +23 -0
  38. package/dist/client/hydrate.js.map +7 -0
  39. package/dist/client/index.d.ts +12 -0
  40. package/dist/client/index.d.ts.map +1 -0
  41. package/dist/client/index.js +32 -0
  42. package/dist/client/index.js.map +7 -0
  43. package/dist/client/jsx-runtime.d.ts +40 -0
  44. package/dist/client/jsx-runtime.d.ts.map +1 -0
  45. package/dist/client/jsx-runtime.js +139 -0
  46. package/dist/client/jsx-runtime.js.map +7 -0
  47. package/dist/client/resource.d.ts +31 -0
  48. package/dist/client/resource.d.ts.map +1 -0
  49. package/dist/client/resource.js +64 -0
  50. package/dist/client/resource.js.map +7 -0
  51. package/dist/client/signal.d.ts +90 -0
  52. package/dist/client/signal.d.ts.map +1 -0
  53. package/dist/client/signal.js +115 -0
  54. package/dist/client/signal.js.map +7 -0
  55. package/dist/client/store.d.ts +26 -0
  56. package/dist/client/store.d.ts.map +1 -0
  57. package/dist/client/store.js +63 -0
  58. package/dist/client/store.js.map +7 -0
  59. package/dist/client/useHead.d.ts +28 -0
  60. package/dist/client/useHead.d.ts.map +1 -0
  61. package/dist/client/useHead.js +33 -0
  62. package/dist/client/useHead.js.map +7 -0
  63. package/dist/config.d.ts +182 -0
  64. package/dist/config.d.ts.map +1 -0
  65. package/dist/config.js +21 -0
  66. package/dist/config.js.map +7 -0
  67. package/dist/create-multisite.d.ts +2 -0
  68. package/dist/create-multisite.d.ts.map +1 -0
  69. package/dist/create-multisite.js +291 -0
  70. package/dist/create-multisite.js.map +7 -0
  71. package/dist/create.d.ts +2 -0
  72. package/dist/create.d.ts.map +1 -0
  73. package/dist/create.js +179 -0
  74. package/dist/create.js.map +7 -0
  75. package/dist/dev/blueprints.d.ts +11 -0
  76. package/dist/dev/blueprints.d.ts.map +1 -0
  77. package/dist/dev/blueprints.js +65 -0
  78. package/dist/dev/blueprints.js.map +7 -0
  79. package/dist/dev/components.d.ts +19 -0
  80. package/dist/dev/components.d.ts.map +1 -0
  81. package/dist/dev/components.js +87 -0
  82. package/dist/dev/components.js.map +7 -0
  83. package/dist/dev/insert.d.ts +11 -0
  84. package/dist/dev/insert.d.ts.map +1 -0
  85. package/dist/dev/insert.js +160 -0
  86. package/dist/dev/insert.js.map +7 -0
  87. package/dist/dev/remove.d.ts +53 -0
  88. package/dist/dev/remove.d.ts.map +1 -0
  89. package/dist/dev/remove.js +518 -0
  90. package/dist/dev/remove.js.map +7 -0
  91. package/dist/dev/watch.d.ts +26 -0
  92. package/dist/dev/watch.d.ts.map +1 -0
  93. package/dist/dev/watch.js +2905 -0
  94. package/dist/dev/watch.js.map +7 -0
  95. package/dist/errors.d.ts +6 -0
  96. package/dist/errors.d.ts.map +1 -0
  97. package/dist/errors.js +63 -0
  98. package/dist/errors.js.map +7 -0
  99. package/dist/generate.d.ts +2 -0
  100. package/dist/generate.d.ts.map +1 -0
  101. package/dist/generate.js +191 -0
  102. package/dist/generate.js.map +7 -0
  103. package/dist/index.d.ts +9 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +57 -0
  106. package/dist/index.js.map +7 -0
  107. package/dist/island.d.ts +24 -0
  108. package/dist/island.d.ts.map +1 -0
  109. package/dist/island.js +15 -0
  110. package/dist/island.js.map +7 -0
  111. package/dist/jsx-runtime.d.ts +406 -0
  112. package/dist/jsx-runtime.d.ts.map +1 -0
  113. package/dist/jsx-runtime.js +90 -0
  114. package/dist/jsx-runtime.js.map +7 -0
  115. package/dist/link.d.ts +27 -0
  116. package/dist/link.d.ts.map +1 -0
  117. package/dist/link.js +29 -0
  118. package/dist/link.js.map +7 -0
  119. package/dist/oml/fragment.d.ts +16 -0
  120. package/dist/oml/fragment.d.ts.map +1 -0
  121. package/dist/oml/fragment.js +26 -0
  122. package/dist/oml/fragment.js.map +7 -0
  123. package/dist/oml/index.d.ts +11 -0
  124. package/dist/oml/index.d.ts.map +1 -0
  125. package/dist/oml/index.js +21 -0
  126. package/dist/oml/index.js.map +7 -0
  127. package/dist/oml/jsx-runtime.d.ts +34 -0
  128. package/dist/oml/jsx-runtime.d.ts.map +1 -0
  129. package/dist/oml/jsx-runtime.js +59 -0
  130. package/dist/oml/jsx-runtime.js.map +7 -0
  131. package/dist/oml/jsx.d.ts +14 -0
  132. package/dist/oml/jsx.d.ts.map +1 -0
  133. package/dist/oml/jsx.js +96 -0
  134. package/dist/oml/jsx.js.map +7 -0
  135. package/dist/oml/page.d.ts +7 -0
  136. package/dist/oml/page.d.ts.map +1 -0
  137. package/dist/oml/page.js +6 -0
  138. package/dist/oml/page.js.map +7 -0
  139. package/dist/oml/render.d.ts +13 -0
  140. package/dist/oml/render.d.ts.map +1 -0
  141. package/dist/oml/render.js +117 -0
  142. package/dist/oml/render.js.map +7 -0
  143. package/dist/oml/types.d.ts +79 -0
  144. package/dist/oml/types.d.ts.map +1 -0
  145. package/dist/oml/types.js +64 -0
  146. package/dist/oml/types.js.map +7 -0
  147. package/dist/router/handler.d.ts +53 -0
  148. package/dist/router/handler.d.ts.map +1 -0
  149. package/dist/router/handler.js +342 -0
  150. package/dist/router/handler.js.map +7 -0
  151. package/dist/router/matcher.d.ts +21 -0
  152. package/dist/router/matcher.d.ts.map +1 -0
  153. package/dist/router/matcher.js +28 -0
  154. package/dist/router/matcher.js.map +7 -0
  155. package/dist/router/scanner.d.ts +17 -0
  156. package/dist/router/scanner.d.ts.map +1 -0
  157. package/dist/router/scanner.js +197 -0
  158. package/dist/router/scanner.js.map +7 -0
  159. package/dist/server/index.d.ts +23 -0
  160. package/dist/server/index.d.ts.map +1 -0
  161. package/dist/server/index.js +29 -0
  162. package/dist/server/index.js.map +7 -0
  163. package/dist/signal.d.ts +15 -0
  164. package/dist/signal.d.ts.map +1 -0
  165. package/dist/signal.js +29 -0
  166. package/dist/signal.js.map +7 -0
  167. package/dist/ssg.d.ts +45 -0
  168. package/dist/ssg.d.ts.map +1 -0
  169. package/dist/ssg.js +175 -0
  170. package/dist/ssg.js.map +7 -0
  171. package/dist/test/actions.test.d.ts +2 -0
  172. package/dist/test/actions.test.d.ts.map +1 -0
  173. package/dist/test/body-limits.test.d.ts +2 -0
  174. package/dist/test/body-limits.test.d.ts.map +1 -0
  175. package/dist/test/errors.test.d.ts +2 -0
  176. package/dist/test/errors.test.d.ts.map +1 -0
  177. package/dist/test/fixtures/routes/[id].page.d.ts +4 -0
  178. package/dist/test/fixtures/routes/[id].page.d.ts.map +1 -0
  179. package/dist/test/fixtures/routes/_error.d.ts +3 -0
  180. package/dist/test/fixtures/routes/_error.d.ts.map +1 -0
  181. package/dist/test/fixtures/routes/_global.d.ts +3 -0
  182. package/dist/test/fixtures/routes/_global.d.ts.map +1 -0
  183. package/dist/test/fixtures/routes/_layout-template.d.ts +3 -0
  184. package/dist/test/fixtures/routes/_layout-template.d.ts.map +1 -0
  185. package/dist/test/fixtures/routes/_layout.d.ts +3 -0
  186. package/dist/test/fixtures/routes/_layout.d.ts.map +1 -0
  187. package/dist/test/fixtures/routes/_layout_scripts.d.ts +3 -0
  188. package/dist/test/fixtures/routes/_layout_scripts.d.ts.map +1 -0
  189. package/dist/test/fixtures/routes/_middleware.d.ts +3 -0
  190. package/dist/test/fixtures/routes/_middleware.d.ts.map +1 -0
  191. package/dist/test/fixtures/routes/_redirect301_mw.d.ts +3 -0
  192. package/dist/test/fixtures/routes/_redirect301_mw.d.ts.map +1 -0
  193. package/dist/test/fixtures/routes/_redirect_mw.d.ts +3 -0
  194. package/dist/test/fixtures/routes/_redirect_mw.d.ts.map +1 -0
  195. package/dist/test/fixtures/routes/about.page.d.ts +3 -0
  196. package/dist/test/fixtures/routes/about.page.d.ts.map +1 -0
  197. package/dist/test/fixtures/routes/action.page.d.ts +6 -0
  198. package/dist/test/fixtures/routes/action.page.d.ts.map +1 -0
  199. package/dist/test/fixtures/routes/api/form-all.post.d.ts +3 -0
  200. package/dist/test/fixtures/routes/api/form-all.post.d.ts.map +1 -0
  201. package/dist/test/fixtures/routes/api/form-limited.post.d.ts +6 -0
  202. package/dist/test/fixtures/routes/api/form-limited.post.d.ts.map +1 -0
  203. package/dist/test/fixtures/routes/api/response-obj.get.d.ts +3 -0
  204. package/dist/test/fixtures/routes/api/response-obj.get.d.ts.map +1 -0
  205. package/dist/test/fixtures/routes/api/upload.post.d.ts +12 -0
  206. package/dist/test/fixtures/routes/api/upload.post.d.ts.map +1 -0
  207. package/dist/test/fixtures/routes/api/users.get.d.ts +6 -0
  208. package/dist/test/fixtures/routes/api/users.get.d.ts.map +1 -0
  209. package/dist/test/fixtures/routes/api/xml.get.d.ts +3 -0
  210. package/dist/test/fixtures/routes/api/xml.get.d.ts.map +1 -0
  211. package/dist/test/fixtures/routes/auth/_middleware.d.ts +3 -0
  212. package/dist/test/fixtures/routes/auth/_middleware.d.ts.map +1 -0
  213. package/dist/test/fixtures/routes/auth/protected.page.d.ts +3 -0
  214. package/dist/test/fixtures/routes/auth/protected.page.d.ts.map +1 -0
  215. package/dist/test/fixtures/routes/index.page.d.ts +3 -0
  216. package/dist/test/fixtures/routes/index.page.d.ts.map +1 -0
  217. package/dist/test/fixtures/routes/oml.page.d.ts +3 -0
  218. package/dist/test/fixtures/routes/oml.page.d.ts.map +1 -0
  219. package/dist/test/fixtures/routes/redirect.page.d.ts +3 -0
  220. package/dist/test/fixtures/routes/redirect.page.d.ts.map +1 -0
  221. package/dist/test/fixtures/routes/ssg/[slug].page.d.ts +5 -0
  222. package/dist/test/fixtures/routes/ssg/[slug].page.d.ts.map +1 -0
  223. package/dist/test/fixtures/routes/ssg/server.page.d.ts +4 -0
  224. package/dist/test/fixtures/routes/ssg/server.page.d.ts.map +1 -0
  225. package/dist/test/fixtures/routes/state.page.d.ts +3 -0
  226. package/dist/test/fixtures/routes/state.page.d.ts.map +1 -0
  227. package/dist/test/fixtures/routes/throw.page.d.ts +3 -0
  228. package/dist/test/fixtures/routes/throw.page.d.ts.map +1 -0
  229. package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts +3 -0
  230. package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts.map +1 -0
  231. package/dist/test/helpers.d.ts +37 -0
  232. package/dist/test/helpers.d.ts.map +1 -0
  233. package/dist/test/layouts.test.d.ts +2 -0
  234. package/dist/test/layouts.test.d.ts.map +1 -0
  235. package/dist/test/middleware.test.d.ts +2 -0
  236. package/dist/test/middleware.test.d.ts.map +1 -0
  237. package/dist/test/multipart.test.d.ts +2 -0
  238. package/dist/test/multipart.test.d.ts.map +1 -0
  239. package/dist/test/oml-routing.test.d.ts +2 -0
  240. package/dist/test/oml-routing.test.d.ts.map +1 -0
  241. package/dist/test/oml.test.d.ts +2 -0
  242. package/dist/test/oml.test.d.ts.map +1 -0
  243. package/dist/test/redirects.test.d.ts +2 -0
  244. package/dist/test/redirects.test.d.ts.map +1 -0
  245. package/dist/test/routing.test.d.ts +2 -0
  246. package/dist/test/routing.test.d.ts.map +1 -0
  247. package/dist/test/ssg.test.d.ts +2 -0
  248. package/dist/test/ssg.test.d.ts.map +1 -0
  249. package/dist/test/web-response.test.d.ts +2 -0
  250. package/dist/test/web-response.test.d.ts.map +1 -0
  251. package/dist/types.d.ts +314 -0
  252. package/dist/types.d.ts.map +1 -0
  253. package/dist/types.js +292 -0
  254. package/dist/types.js.map +7 -0
  255. package/package.json +103 -0
  256. package/pka.config.json +32 -0
  257. package/src/build/config.ts +42 -0
  258. package/src/build/index.ts +6 -0
  259. package/src/build/plugins.ts +118 -0
  260. package/src/cli.ts +502 -0
  261. package/src/config.ts +197 -0
  262. package/src/create-multisite.ts +310 -0
  263. package/src/create.ts +194 -0
  264. package/src/dev/blueprints.ts +75 -0
  265. package/src/dev/components.ts +108 -0
  266. package/src/dev/insert.ts +221 -0
  267. package/src/dev/remove.ts +677 -0
  268. package/src/dev/watch.ts +3098 -0
  269. package/src/env.d.ts +5 -0
  270. package/src/errors.ts +64 -0
  271. package/src/generate.ts +228 -0
  272. package/src/index.ts +67 -0
  273. package/src/island.ts +47 -0
  274. package/src/jsx-runtime.d.ts +408 -0
  275. package/src/jsx-runtime.d.ts.map +1 -0
  276. package/src/jsx-runtime.ts +536 -0
  277. package/src/link.ts +49 -0
  278. package/src/oml/fragment.ts +54 -0
  279. package/src/oml/index.ts +21 -0
  280. package/src/oml/jsx-runtime.ts +121 -0
  281. package/src/oml/jsx.ts +151 -0
  282. package/src/oml/page.ts +13 -0
  283. package/src/oml/render.ts +181 -0
  284. package/src/oml/types.ts +159 -0
  285. package/src/router/handler.ts +515 -0
  286. package/src/router/matcher.ts +52 -0
  287. package/src/router/scanner.ts +272 -0
  288. package/src/server/index.ts +49 -0
  289. package/src/signal.ts +39 -0
  290. package/src/ssg.ts +253 -0
  291. package/src/test/actions.test.ts +40 -0
  292. package/src/test/body-limits.test.ts +83 -0
  293. package/src/test/errors.test.ts +53 -0
  294. package/src/test/fixtures/routes/[id].page.ts +3 -0
  295. package/src/test/fixtures/routes/_error.ts +6 -0
  296. package/src/test/fixtures/routes/_global.ts +8 -0
  297. package/src/test/fixtures/routes/_layout-template.ts +7 -0
  298. package/src/test/fixtures/routes/_layout.ts +7 -0
  299. package/src/test/fixtures/routes/_layout_scripts.ts +8 -0
  300. package/src/test/fixtures/routes/_middleware.ts +8 -0
  301. package/src/test/fixtures/routes/_redirect301_mw.ts +5 -0
  302. package/src/test/fixtures/routes/_redirect_mw.ts +5 -0
  303. package/src/test/fixtures/routes/about.page.ts +6 -0
  304. package/src/test/fixtures/routes/action.page.ts +11 -0
  305. package/src/test/fixtures/routes/api/form-all.post.ts +5 -0
  306. package/src/test/fixtures/routes/api/form-limited.post.ts +6 -0
  307. package/src/test/fixtures/routes/api/response-obj.get.ts +17 -0
  308. package/src/test/fixtures/routes/api/upload.post.ts +14 -0
  309. package/src/test/fixtures/routes/api/users.get.ts +3 -0
  310. package/src/test/fixtures/routes/api/xml.get.ts +5 -0
  311. package/src/test/fixtures/routes/auth/_middleware.ts +11 -0
  312. package/src/test/fixtures/routes/auth/protected.page.ts +3 -0
  313. package/src/test/fixtures/routes/index.page.ts +3 -0
  314. package/src/test/fixtures/routes/oml.page.ts +7 -0
  315. package/src/test/fixtures/routes/redirect.page.ts +3 -0
  316. package/src/test/fixtures/routes/ssg/[slug].page.ts +8 -0
  317. package/src/test/fixtures/routes/ssg/server.page.ts +5 -0
  318. package/src/test/fixtures/routes/state.page.ts +4 -0
  319. package/src/test/fixtures/routes/throw.page.ts +5 -0
  320. package/src/test/fixtures/routes/wiki/[...slug].page.ts +3 -0
  321. package/src/test/helpers.ts +132 -0
  322. package/src/test/layouts.test.ts +76 -0
  323. package/src/test/middleware.test.ts +69 -0
  324. package/src/test/multipart.test.ts +91 -0
  325. package/src/test/oml-routing.test.ts +59 -0
  326. package/src/test/oml.test.ts +429 -0
  327. package/src/test/redirects.test.ts +32 -0
  328. package/src/test/routing.test.ts +118 -0
  329. package/src/test/ssg.test.ts +273 -0
  330. package/src/test/web-response.test.ts +33 -0
  331. package/src/types.ts +670 -0
  332. package/tsconfig.client.json +17 -0
  333. package/tsconfig.json +20 -0
@@ -0,0 +1,221 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs'
2
+
3
+ export type InsertResult =
4
+ | { inserted: true; file: string; warning?: string }
5
+ | { inserted: false; error: string }
6
+
7
+ function leadingWs(line: string): string {
8
+ return line.match(/^(\s*)/)?.[1] ?? ''
9
+ }
10
+
11
+ function findInsertPoint(lines: string[]): { idx: number; indent: string } | null {
12
+ // Explicit marker takes priority
13
+ for (let i = 0; i < lines.length; i++) {
14
+ if (lines[i].includes('@davaux-insert')) {
15
+ return { idx: i, indent: leadingWs(lines[i]) }
16
+ }
17
+ }
18
+
19
+ // Find `return (` line
20
+ let returnIdx = -1
21
+ for (let i = 0; i < lines.length; i++) {
22
+ if (/return\s*\(/.test(lines[i])) {
23
+ returnIdx = i
24
+ break
25
+ }
26
+ }
27
+ if (returnIdx === -1) return null
28
+
29
+ // Find the closing `)` of the return at the same or shallower indent
30
+ const returnIndentLen = leadingWs(lines[returnIdx]).length
31
+ let returnCloseIdx = -1
32
+ for (let i = returnIdx + 1; i < lines.length; i++) {
33
+ const t = lines[i].trim()
34
+ const indLen = leadingWs(lines[i]).length
35
+ if ((t === ')' || t === ');') && indLen <= returnIndentLen + 2) {
36
+ returnCloseIdx = i
37
+ break
38
+ }
39
+ }
40
+ if (returnCloseIdx === -1) return null
41
+
42
+ // Find the outermost JSX wrapper element
43
+ let wrapperIdx = -1
44
+ let wrapperTag = ''
45
+ let wrapperIndent = ''
46
+ for (let i = returnIdx + 1; i < returnCloseIdx; i++) {
47
+ const t = lines[i].trim()
48
+ if (!t) continue
49
+ // Fragment
50
+ if (t === '<>') {
51
+ wrapperIdx = i
52
+ wrapperTag = ''
53
+ wrapperIndent = leadingWs(lines[i])
54
+ break
55
+ }
56
+ // Single-line opening tag: <Tag> or <Tag attr="x">
57
+ const m = t.match(/^<([A-Za-z][A-Za-z0-9.]*)(?:\s[^>]*)?>$/)
58
+ if (m) {
59
+ wrapperIdx = i
60
+ wrapperTag = m[1]
61
+ wrapperIndent = leadingWs(lines[i])
62
+ break
63
+ }
64
+ }
65
+ if (wrapperIdx === -1) return null
66
+
67
+ // Find the closing tag at the same indent level
68
+ const closeTag = wrapperTag ? `</${wrapperTag}>` : '</>'
69
+ let closingIdx = -1
70
+ for (let i = wrapperIdx + 1; i < returnCloseIdx; i++) {
71
+ if (leadingWs(lines[i]) === wrapperIndent && lines[i].trim() === closeTag) {
72
+ closingIdx = i
73
+ break
74
+ }
75
+ }
76
+ if (closingIdx === -1) return null
77
+
78
+ const useTabs = wrapperIndent.includes('\t')
79
+ const childIndent = wrapperIndent + (useTabs ? '\t' : ' ')
80
+ return { idx: closingIdx, indent: childIndent }
81
+ }
82
+
83
+ function addMissingImports(lines: string[], imports: Record<string, string>): number {
84
+ let lastImportIdx = -1
85
+ for (let i = 0; i < lines.length; i++) {
86
+ if (lines[i].trimStart().startsWith('import ')) lastImportIdx = i
87
+ }
88
+ let added = 0
89
+ for (const [name, path] of Object.entries(imports)) {
90
+ const already = lines.some(
91
+ (l) =>
92
+ l.includes(`import ${name}`) ||
93
+ l.includes(`{ ${name} }`) ||
94
+ new RegExp(`\\b${name}\\b`).test(l.replace(/^import\s+/, '')),
95
+ )
96
+ if (!already) {
97
+ lines.splice(lastImportIdx + 1 + added, 0, `import ${name} from '${path}'`)
98
+ added++
99
+ }
100
+ }
101
+ return added
102
+ }
103
+
104
+ // Find the full span of the Nth element (opening through matching closing tag).
105
+ // Mirrors findFullElementSpan in remove.ts — kept local to avoid a circular dep.
106
+ function findElemSpan(lines: string[], tagName: string, instanceIndex: number): [number, number] | null {
107
+ // PascalCase components: anchor to start-of-line so inline prop occurrences are not counted.
108
+ // This keeps the index consistent with the OML tree traversal in countNameBefore.
109
+ const isPascal = /^[A-Z]/.test(tagName)
110
+ const openRe = isPascal
111
+ ? new RegExp(`^\\s*<${tagName}(\\s|/>|>|$)`)
112
+ : new RegExp(`<${tagName}(\\s|>|\\/)`)
113
+ let count = 0
114
+ for (let i = 0; i < lines.length; i++) {
115
+ if (openRe.test(lines[i])) {
116
+ if (count === instanceIndex) {
117
+ const tagPos = lines[i].search(openRe)
118
+ const openIndent = lines[i].match(/^(\s*)/)?.[1] ?? ''
119
+ const rest = lines[i].slice(tagPos)
120
+ if (/\/>/.test(rest) || new RegExp(`</${tagName}>`).test(rest)) return [i, i]
121
+ for (let j = i + 1; j < lines.length; j++) {
122
+ const t = lines[j].trim()
123
+ const ind = lines[j].match(/^(\s*)/)?.[1] ?? ''
124
+ if (t.startsWith(`</${tagName}>`) && ind.length <= openIndent.length) return [i, j]
125
+ if (t && ind.length < openIndent.length) break
126
+ }
127
+ return [i, i]
128
+ }
129
+ count++
130
+ }
131
+ }
132
+ return null
133
+ }
134
+
135
+ export function insertAfterElement(
136
+ filePath: string,
137
+ tagName: string,
138
+ instanceIndex: number,
139
+ newJsx: string,
140
+ componentImports: Record<string, string> = {},
141
+ ): InsertResult {
142
+ let source: string
143
+ try {
144
+ source = readFileSync(filePath, 'utf-8')
145
+ } catch {
146
+ return { inserted: false, error: `Cannot read file: ${filePath}` }
147
+ }
148
+
149
+ const lines = source.split('\n')
150
+ const span = findElemSpan(lines, tagName, instanceIndex)
151
+ if (!span) return { inserted: false, error: `No <${tagName}> found at index ${instanceIndex}` }
152
+
153
+ const importCount = addMissingImports(lines, componentImports)
154
+ const adjustedEnd = span[1] + importCount
155
+
156
+ const indent = leadingWs(lines[span[0] + importCount])
157
+ const formatted = newJsx
158
+ .split('\n')
159
+ .map((l) => (l ? indent + l : l))
160
+ .join('\n')
161
+
162
+ lines.splice(adjustedEnd + 1, 0, formatted)
163
+
164
+ try {
165
+ writeFileSync(filePath, lines.join('\n'), 'utf-8')
166
+ } catch {
167
+ return { inserted: false, error: `Cannot write file: ${filePath}` }
168
+ }
169
+
170
+ return { inserted: true, file: filePath }
171
+ }
172
+
173
+ export function insertJsx(
174
+ filePath: string,
175
+ jsxSnippet: string,
176
+ componentImports: Record<string, string>,
177
+ ): InsertResult {
178
+ let source: string
179
+ try {
180
+ source = readFileSync(filePath, 'utf-8')
181
+ } catch {
182
+ return { inserted: false, error: `Cannot read file: ${filePath}` }
183
+ }
184
+
185
+ const lines = source.split('\n')
186
+ const point = findInsertPoint(lines)
187
+ if (!point) {
188
+ return {
189
+ inserted: false,
190
+ error:
191
+ 'Cannot find insertion point. Add {/* @davaux-insert */} in your JSX where you want components to be inserted.',
192
+ }
193
+ }
194
+
195
+ const importCount = addMissingImports(lines, componentImports)
196
+ const insertIdx = point.idx + importCount
197
+
198
+ const formatted = jsxSnippet
199
+ .split('\n')
200
+ .map((l) => (l ? point.indent + l : l))
201
+ .join('\n')
202
+
203
+ lines.splice(insertIdx, 0, formatted)
204
+
205
+ try {
206
+ writeFileSync(filePath, lines.join('\n'), 'utf-8')
207
+ } catch {
208
+ return { inserted: false, error: `Cannot write file: ${filePath}` }
209
+ }
210
+
211
+ // Warn if an uppercase component was inserted without a matching import in the file
212
+ const componentName = jsxSnippet.match(/^<([A-Z][A-Za-z0-9.]*)/)?.[1]
213
+ const hasImport =
214
+ !componentName ||
215
+ lines.some((l) => l.trimStart().startsWith('import') && l.includes(componentName))
216
+ const warning = hasImport
217
+ ? undefined
218
+ : `No import found for ${componentName}. Add an import manually, or set the "imports" field in the blueprint JSON to automate it.`
219
+
220
+ return { inserted: true, file: filePath, warning }
221
+ }