@velarscript/web 0.31.0 → 0.33.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 (393) hide show
  1. package/README.md +1 -1
  2. package/dist/analysis/calls/intrinsics/config.d.ts +10 -0
  3. package/dist/analysis/calls/intrinsics/config.d.ts.map +1 -0
  4. package/dist/analysis/calls/intrinsics/config.js +19 -0
  5. package/dist/analysis/calls/intrinsics/config.js.map +1 -0
  6. package/dist/analysis/calls/intrinsics/forms.d.ts +10 -0
  7. package/dist/analysis/calls/intrinsics/forms.d.ts.map +1 -0
  8. package/dist/analysis/calls/intrinsics/forms.js +38 -0
  9. package/dist/analysis/calls/intrinsics/forms.js.map +1 -0
  10. package/dist/analysis/calls/intrinsics/http.d.ts +10 -0
  11. package/dist/analysis/calls/intrinsics/http.d.ts.map +1 -0
  12. package/dist/analysis/calls/intrinsics/http.js +37 -0
  13. package/dist/analysis/calls/intrinsics/http.js.map +1 -0
  14. package/dist/analysis/calls/intrinsics/reactive.d.ts +10 -0
  15. package/dist/analysis/calls/intrinsics/reactive.d.ts.map +1 -0
  16. package/dist/analysis/calls/intrinsics/reactive.js +30 -0
  17. package/dist/analysis/calls/intrinsics/reactive.js.map +1 -0
  18. package/dist/analysis/calls/intrinsics/storage.d.ts +16 -0
  19. package/dist/analysis/calls/intrinsics/storage.d.ts.map +1 -0
  20. package/dist/analysis/calls/intrinsics/storage.js +91 -0
  21. package/dist/analysis/calls/intrinsics/storage.js.map +1 -0
  22. package/dist/analysis/calls/intrinsics/web.d.ts +10 -0
  23. package/dist/analysis/calls/intrinsics/web.d.ts.map +1 -0
  24. package/dist/analysis/calls/intrinsics/web.js +100 -0
  25. package/dist/analysis/calls/intrinsics/web.js.map +1 -0
  26. package/dist/analysis/calls/intrinsics.d.ts +13 -0
  27. package/dist/analysis/calls/intrinsics.d.ts.map +1 -0
  28. package/dist/analysis/calls/intrinsics.js +31 -0
  29. package/dist/analysis/calls/intrinsics.js.map +1 -0
  30. package/dist/analysis/components/contracts.d.ts +19 -0
  31. package/dist/analysis/components/contracts.d.ts.map +1 -0
  32. package/dist/analysis/components/contracts.js +83 -0
  33. package/dist/analysis/components/contracts.js.map +1 -0
  34. package/dist/analysis/components/declaration.d.ts +4 -0
  35. package/dist/analysis/components/declaration.d.ts.map +1 -0
  36. package/dist/analysis/components/declaration.js +163 -0
  37. package/dist/analysis/components/declaration.js.map +1 -0
  38. package/dist/analysis/components/host.d.ts +61 -0
  39. package/dist/analysis/components/host.d.ts.map +1 -0
  40. package/dist/analysis/components/host.js +22 -0
  41. package/dist/analysis/components/host.js.map +1 -0
  42. package/dist/analysis/components/lifecycle.d.ts +22 -0
  43. package/dist/analysis/components/lifecycle.d.ts.map +1 -0
  44. package/dist/analysis/components/lifecycle.js +33 -0
  45. package/dist/analysis/components/lifecycle.js.map +1 -0
  46. package/dist/analysis/components/ownership.d.ts +74 -0
  47. package/dist/analysis/components/ownership.d.ts.map +1 -0
  48. package/dist/analysis/components/ownership.js +106 -0
  49. package/dist/analysis/components/ownership.js.map +1 -0
  50. package/dist/analysis/declarations.d.ts +99 -0
  51. package/dist/analysis/declarations.d.ts.map +1 -0
  52. package/dist/analysis/declarations.js +176 -0
  53. package/dist/analysis/declarations.js.map +1 -0
  54. package/dist/analysis/inference.d.ts +75 -0
  55. package/dist/analysis/inference.d.ts.map +1 -0
  56. package/dist/analysis/inference.js +257 -0
  57. package/dist/analysis/inference.js.map +1 -0
  58. package/dist/analysis/jsx/attributes.d.ts +103 -0
  59. package/dist/analysis/jsx/attributes.d.ts.map +1 -0
  60. package/dist/analysis/jsx/attributes.js +601 -0
  61. package/dist/analysis/jsx/attributes.js.map +1 -0
  62. package/dist/analysis/jsx/elements.d.ts +17 -0
  63. package/dist/analysis/jsx/elements.d.ts.map +1 -0
  64. package/dist/analysis/jsx/elements.js +213 -0
  65. package/dist/analysis/jsx/elements.js.map +1 -0
  66. package/dist/analysis/jsx/host.d.ts +61 -0
  67. package/dist/analysis/jsx/host.d.ts.map +1 -0
  68. package/dist/analysis/jsx/host.js +15 -0
  69. package/dist/analysis/jsx/host.js.map +1 -0
  70. package/dist/analysis/jsx/keys.d.ts +15 -0
  71. package/dist/analysis/jsx/keys.d.ts.map +1 -0
  72. package/dist/analysis/jsx/keys.js +67 -0
  73. package/dist/analysis/jsx/keys.js.map +1 -0
  74. package/dist/analysis/jsx/security.d.ts +29 -0
  75. package/dist/analysis/jsx/security.d.ts.map +1 -0
  76. package/dist/analysis/jsx/security.js +61 -0
  77. package/dist/analysis/jsx/security.js.map +1 -0
  78. package/dist/analysis/jsx-detection.d.ts +49 -0
  79. package/dist/analysis/jsx-detection.d.ts.map +1 -0
  80. package/dist/analysis/jsx-detection.js +196 -0
  81. package/dist/analysis/jsx-detection.js.map +1 -0
  82. package/dist/analysis/keyed-rebuild.d.ts +148 -0
  83. package/dist/analysis/keyed-rebuild.d.ts.map +1 -0
  84. package/dist/analysis/keyed-rebuild.js +299 -0
  85. package/dist/analysis/keyed-rebuild.js.map +1 -0
  86. package/dist/analysis/keyframes-analysis.d.ts +6 -0
  87. package/dist/analysis/keyframes-analysis.d.ts.map +1 -0
  88. package/dist/analysis/keyframes-analysis.js +79 -0
  89. package/dist/analysis/keyframes-analysis.js.map +1 -0
  90. package/dist/analysis/look/builders.d.ts +29 -0
  91. package/dist/analysis/look/builders.d.ts.map +1 -0
  92. package/dist/analysis/look/builders.js +174 -0
  93. package/dist/analysis/look/builders.js.map +1 -0
  94. package/dist/analysis/look/conditions.d.ts +13 -0
  95. package/dist/analysis/look/conditions.d.ts.map +1 -0
  96. package/dist/analysis/look/conditions.js +84 -0
  97. package/dist/analysis/look/conditions.js.map +1 -0
  98. package/dist/analysis/look/edits.d.ts +36 -0
  99. package/dist/analysis/look/edits.d.ts.map +1 -0
  100. package/dist/analysis/look/edits.js +80 -0
  101. package/dist/analysis/look/edits.js.map +1 -0
  102. package/dist/analysis/look/entries.d.ts +56 -0
  103. package/dist/analysis/look/entries.d.ts.map +1 -0
  104. package/dist/analysis/look/entries.js +223 -0
  105. package/dist/analysis/look/entries.js.map +1 -0
  106. package/dist/analysis/look/host.d.ts +47 -0
  107. package/dist/analysis/look/host.d.ts.map +1 -0
  108. package/dist/analysis/look/host.js +16 -0
  109. package/dist/analysis/look/host.js.map +1 -0
  110. package/dist/analysis/look/tokens.d.ts +75 -0
  111. package/dist/analysis/look/tokens.d.ts.map +1 -0
  112. package/dist/analysis/look/tokens.js +156 -0
  113. package/dist/analysis/look/tokens.js.map +1 -0
  114. package/dist/analysis/look/values.d.ts +45 -0
  115. package/dist/analysis/look/values.d.ts.map +1 -0
  116. package/dist/analysis/look/values.js +148 -0
  117. package/dist/analysis/look/values.js.map +1 -0
  118. package/dist/analysis/look-conditions.d.ts +47 -0
  119. package/dist/analysis/look-conditions.d.ts.map +1 -0
  120. package/dist/analysis/look-conditions.js +157 -0
  121. package/dist/analysis/look-conditions.js.map +1 -0
  122. package/dist/analysis/look-sites.d.ts +62 -0
  123. package/dist/analysis/look-sites.d.ts.map +1 -0
  124. package/dist/analysis/look-sites.js +121 -0
  125. package/dist/analysis/look-sites.js.map +1 -0
  126. package/dist/analysis/look-values.d.ts +19 -0
  127. package/dist/analysis/look-values.d.ts.map +1 -1
  128. package/dist/analysis/look-values.js +38 -0
  129. package/dist/analysis/look-values.js.map +1 -1
  130. package/dist/analysis/look-vocabulary-guidance.d.ts +76 -0
  131. package/dist/analysis/look-vocabulary-guidance.d.ts.map +1 -0
  132. package/dist/analysis/look-vocabulary-guidance.js +338 -0
  133. package/dist/analysis/look-vocabulary-guidance.js.map +1 -0
  134. package/dist/analysis/media-conditions.d.ts +33 -0
  135. package/dist/analysis/media-conditions.d.ts.map +1 -0
  136. package/dist/analysis/media-conditions.js +92 -0
  137. package/dist/analysis/media-conditions.js.map +1 -0
  138. package/dist/analysis/program-passes.d.ts +106 -0
  139. package/dist/analysis/program-passes.d.ts.map +1 -0
  140. package/dist/analysis/program-passes.js +149 -0
  141. package/dist/analysis/program-passes.js.map +1 -0
  142. package/dist/analysis/reactivity/bindings.d.ts +37 -0
  143. package/dist/analysis/reactivity/bindings.d.ts.map +1 -0
  144. package/dist/analysis/reactivity/bindings.js +88 -0
  145. package/dist/analysis/reactivity/bindings.js.map +1 -0
  146. package/dist/analysis/reactivity/derived.d.ts +39 -0
  147. package/dist/analysis/reactivity/derived.d.ts.map +1 -0
  148. package/dist/analysis/reactivity/derived.js +80 -0
  149. package/dist/analysis/reactivity/derived.js.map +1 -0
  150. package/dist/analysis/reactivity/host.d.ts +38 -0
  151. package/dist/analysis/reactivity/host.d.ts.map +1 -0
  152. package/dist/analysis/reactivity/host.js +14 -0
  153. package/dist/analysis/reactivity/host.js.map +1 -0
  154. package/dist/analysis/reactivity/retired-accessors.d.ts +82 -0
  155. package/dist/analysis/reactivity/retired-accessors.d.ts.map +1 -0
  156. package/dist/analysis/reactivity/retired-accessors.js +118 -0
  157. package/dist/analysis/reactivity/retired-accessors.js.map +1 -0
  158. package/dist/analysis/renderable.d.ts +47 -0
  159. package/dist/analysis/renderable.d.ts.map +1 -0
  160. package/dist/analysis/renderable.js +105 -0
  161. package/dist/analysis/renderable.js.map +1 -0
  162. package/dist/analysis/routes.d.ts +64 -0
  163. package/dist/analysis/routes.d.ts.map +1 -0
  164. package/dist/analysis/routes.js +182 -0
  165. package/dist/analysis/routes.js.map +1 -0
  166. package/dist/analysis/url-attributes.d.ts +12 -0
  167. package/dist/analysis/url-attributes.d.ts.map +1 -0
  168. package/dist/analysis/url-attributes.js +48 -0
  169. package/dist/analysis/url-attributes.js.map +1 -0
  170. package/dist/analysis/watch-cycles.d.ts +102 -0
  171. package/dist/analysis/watch-cycles.d.ts.map +1 -1
  172. package/dist/analysis/watch-cycles.js +240 -1
  173. package/dist/analysis/watch-cycles.js.map +1 -1
  174. package/dist/analysis/watch-subject.d.ts +45 -0
  175. package/dist/analysis/watch-subject.d.ts.map +1 -0
  176. package/dist/analysis/watch-subject.js +272 -0
  177. package/dist/analysis/watch-subject.js.map +1 -0
  178. package/dist/analysis/web-types.d.ts +27 -0
  179. package/dist/analysis/web-types.d.ts.map +1 -0
  180. package/dist/analysis/web-types.js +170 -0
  181. package/dist/analysis/web-types.js.map +1 -0
  182. package/dist/analyzer.d.ts +18 -617
  183. package/dist/analyzer.d.ts.map +1 -1
  184. package/dist/analyzer.js +211 -4853
  185. package/dist/analyzer.js.map +1 -1
  186. package/dist/compiler.d.ts +20 -0
  187. package/dist/compiler.d.ts.map +1 -1
  188. package/dist/compiler.js +31 -864
  189. package/dist/compiler.js.map +1 -1
  190. package/dist/documentation.d.ts +28 -0
  191. package/dist/documentation.d.ts.map +1 -0
  192. package/dist/documentation.js +67 -0
  193. package/dist/documentation.js.map +1 -0
  194. package/dist/editor/completions.d.ts +12 -0
  195. package/dist/editor/completions.d.ts.map +1 -0
  196. package/dist/editor/completions.js +6 -0
  197. package/dist/editor/completions.js.map +1 -0
  198. package/dist/editor/jsx-completion.d.ts +12 -0
  199. package/dist/editor/jsx-completion.d.ts.map +1 -0
  200. package/dist/editor/jsx-completion.js +137 -0
  201. package/dist/editor/jsx-completion.js.map +1 -0
  202. package/dist/editor/look-completion.d.ts +29 -0
  203. package/dist/editor/look-completion.d.ts.map +1 -0
  204. package/dist/editor/look-completion.js +159 -0
  205. package/dist/editor/look-completion.js.map +1 -0
  206. package/dist/editor.d.ts.map +1 -1
  207. package/dist/editor.js +12 -288
  208. package/dist/editor.js.map +1 -1
  209. package/dist/emit/components.d.ts +33 -0
  210. package/dist/emit/components.d.ts.map +1 -0
  211. package/dist/emit/components.js +135 -0
  212. package/dist/emit/components.js.map +1 -0
  213. package/dist/emit/jsx.d.ts +57 -0
  214. package/dist/emit/jsx.d.ts.map +1 -0
  215. package/dist/emit/jsx.js +384 -0
  216. package/dist/emit/jsx.js.map +1 -0
  217. package/dist/emit/look-css.d.ts +44 -0
  218. package/dist/emit/look-css.d.ts.map +1 -0
  219. package/dist/emit/look-css.js +177 -0
  220. package/dist/emit/look-css.js.map +1 -0
  221. package/dist/emit/look.d.ts +39 -0
  222. package/dist/emit/look.d.ts.map +1 -0
  223. package/dist/emit/look.js +218 -0
  224. package/dist/emit/look.js.map +1 -0
  225. package/dist/emit/runtime-imports.d.ts +40 -0
  226. package/dist/emit/runtime-imports.d.ts.map +1 -0
  227. package/dist/emit/runtime-imports.js +188 -0
  228. package/dist/emit/runtime-imports.js.map +1 -0
  229. package/dist/emitter.d.ts +15 -68
  230. package/dist/emitter.d.ts.map +1 -1
  231. package/dist/emitter.js +79 -1008
  232. package/dist/emitter.js.map +1 -1
  233. package/dist/look/animation.d.ts +7 -0
  234. package/dist/look/animation.d.ts.map +1 -0
  235. package/dist/look/animation.js +13 -0
  236. package/dist/look/animation.js.map +1 -0
  237. package/dist/look/builders.d.ts +35 -0
  238. package/dist/look/builders.d.ts.map +1 -0
  239. package/dist/look/builders.js +74 -0
  240. package/dist/look/builders.js.map +1 -0
  241. package/dist/look/css-functions.d.ts +28 -0
  242. package/dist/look/css-functions.d.ts.map +1 -0
  243. package/dist/look/css-functions.js +54 -0
  244. package/dist/look/css-functions.js.map +1 -0
  245. package/dist/look/keyword-sets.d.ts +41 -0
  246. package/dist/look/keyword-sets.d.ts.map +1 -0
  247. package/dist/look/keyword-sets.js +117 -0
  248. package/dist/look/keyword-sets.js.map +1 -0
  249. package/dist/look/keywords.d.ts +55 -0
  250. package/dist/look/keywords.d.ts.map +1 -0
  251. package/dist/look/keywords.js +387 -0
  252. package/dist/look/keywords.js.map +1 -0
  253. package/dist/look/media.d.ts +19 -0
  254. package/dist/look/media.d.ts.map +1 -0
  255. package/dist/look/media.js +27 -0
  256. package/dist/look/media.js.map +1 -0
  257. package/dist/look/naming.d.ts +9 -0
  258. package/dist/look/naming.d.ts.map +1 -0
  259. package/dist/look/naming.js +45 -0
  260. package/dist/look/naming.js.map +1 -0
  261. package/dist/look/properties.d.ts +16 -0
  262. package/dist/look/properties.d.ts.map +1 -0
  263. package/dist/look/properties.js +52 -0
  264. package/dist/look/properties.js.map +1 -0
  265. package/dist/look/shorthands.d.ts +25 -0
  266. package/dist/look/shorthands.d.ts.map +1 -0
  267. package/dist/look/shorthands.js +130 -0
  268. package/dist/look/shorthands.js.map +1 -0
  269. package/dist/look/tokens.d.ts +38 -0
  270. package/dist/look/tokens.d.ts.map +1 -0
  271. package/dist/look/tokens.js +59 -0
  272. package/dist/look/tokens.js.map +1 -0
  273. package/dist/look/units.d.ts +24 -0
  274. package/dist/look/units.d.ts.map +1 -0
  275. package/dist/look/units.js +33 -0
  276. package/dist/look/units.js.map +1 -0
  277. package/dist/look.d.ts +32 -227
  278. package/dist/look.d.ts.map +1 -1
  279. package/dist/look.js +28 -933
  280. package/dist/look.js.map +1 -1
  281. package/dist/module-roster.d.ts +16 -0
  282. package/dist/module-roster.d.ts.map +1 -0
  283. package/dist/module-roster.js +28 -0
  284. package/dist/module-roster.js.map +1 -0
  285. package/dist/modules/app.d.ts +9 -0
  286. package/dist/modules/app.d.ts.map +1 -0
  287. package/dist/modules/app.js +20 -0
  288. package/dist/modules/app.js.map +1 -0
  289. package/dist/modules/browser-test.d.ts +9 -0
  290. package/dist/modules/browser-test.d.ts.map +1 -0
  291. package/dist/modules/browser-test.js +67 -0
  292. package/dist/modules/browser-test.js.map +1 -0
  293. package/dist/modules/browser.d.ts +10 -0
  294. package/dist/modules/browser.d.ts.map +1 -0
  295. package/dist/modules/browser.js +89 -0
  296. package/dist/modules/browser.js.map +1 -0
  297. package/dist/modules/config.d.ts +9 -0
  298. package/dist/modules/config.d.ts.map +1 -0
  299. package/dist/modules/config.js +14 -0
  300. package/dist/modules/config.js.map +1 -0
  301. package/dist/modules/files.d.ts +9 -0
  302. package/dist/modules/files.d.ts.map +1 -0
  303. package/dist/modules/files.js +16 -0
  304. package/dist/modules/files.js.map +1 -0
  305. package/dist/modules/forms.d.ts +9 -0
  306. package/dist/modules/forms.d.ts.map +1 -0
  307. package/dist/modules/forms.js +26 -0
  308. package/dist/modules/forms.js.map +1 -0
  309. package/dist/modules/http.d.ts +9 -0
  310. package/dist/modules/http.d.ts.map +1 -0
  311. package/dist/modules/http.js +127 -0
  312. package/dist/modules/http.js.map +1 -0
  313. package/dist/modules/look.d.ts +9 -0
  314. package/dist/modules/look.d.ts.map +1 -0
  315. package/dist/modules/look.js +58 -0
  316. package/dist/modules/look.js.map +1 -0
  317. package/dist/modules/realtime.d.ts +9 -0
  318. package/dist/modules/realtime.d.ts.map +1 -0
  319. package/dist/modules/realtime.js +163 -0
  320. package/dist/modules/realtime.js.map +1 -0
  321. package/dist/modules/storage.d.ts +9 -0
  322. package/dist/modules/storage.d.ts.map +1 -0
  323. package/dist/modules/storage.js +59 -0
  324. package/dist/modules/storage.js.map +1 -0
  325. package/dist/modules/types.d.ts +78 -0
  326. package/dist/modules/types.d.ts.map +1 -0
  327. package/dist/modules/types.js +154 -0
  328. package/dist/modules/types.js.map +1 -0
  329. package/dist/modules/web.d.ts +9 -0
  330. package/dist/modules/web.d.ts.map +1 -0
  331. package/dist/modules/web.js +63 -0
  332. package/dist/modules/web.js.map +1 -0
  333. package/dist/modules/websocket.d.ts +11 -0
  334. package/dist/modules/websocket.d.ts.map +1 -0
  335. package/dist/modules/websocket.js +75 -0
  336. package/dist/modules/websocket.js.map +1 -0
  337. package/dist/parser/expressions/jsx.d.ts +17 -0
  338. package/dist/parser/expressions/jsx.d.ts.map +1 -0
  339. package/dist/parser/expressions/jsx.js +43 -0
  340. package/dist/parser/expressions/jsx.js.map +1 -0
  341. package/dist/parser/expressions/keyframes.d.ts +15 -0
  342. package/dist/parser/expressions/keyframes.d.ts.map +1 -0
  343. package/dist/parser/expressions/keyframes.js +33 -0
  344. package/dist/parser/expressions/keyframes.js.map +1 -0
  345. package/dist/parser/expressions/look.d.ts +16 -0
  346. package/dist/parser/expressions/look.d.ts.map +1 -0
  347. package/dist/parser/expressions/look.js +37 -0
  348. package/dist/parser/expressions/look.js.map +1 -0
  349. package/dist/parser/expressions/visual-block.d.ts +17 -0
  350. package/dist/parser/expressions/visual-block.d.ts.map +1 -0
  351. package/dist/parser/expressions/visual-block.js +21 -0
  352. package/dist/parser/expressions/visual-block.js.map +1 -0
  353. package/dist/parser/keyframes-source.d.ts +24 -0
  354. package/dist/parser/keyframes-source.d.ts.map +1 -0
  355. package/dist/parser/keyframes-source.js +133 -0
  356. package/dist/parser/keyframes-source.js.map +1 -0
  357. package/dist/parser/look-source.d.ts +25 -0
  358. package/dist/parser/look-source.d.ts.map +1 -0
  359. package/dist/parser/look-source.js +189 -0
  360. package/dist/parser/look-source.js.map +1 -0
  361. package/dist/parser/spans.d.ts +24 -0
  362. package/dist/parser/spans.d.ts.map +1 -0
  363. package/dist/parser/spans.js +113 -0
  364. package/dist/parser/spans.js.map +1 -0
  365. package/dist/parser/statements/components.d.ts +37 -0
  366. package/dist/parser/statements/components.d.ts.map +1 -0
  367. package/dist/parser/statements/components.js +185 -0
  368. package/dist/parser/statements/components.js.map +1 -0
  369. package/dist/parser/statements/heads.d.ts +38 -0
  370. package/dist/parser/statements/heads.d.ts.map +1 -0
  371. package/dist/parser/statements/heads.js +55 -0
  372. package/dist/parser/statements/heads.js.map +1 -0
  373. package/dist/parser/statements/imports.d.ts +9 -0
  374. package/dist/parser/statements/imports.d.ts.map +1 -0
  375. package/dist/parser/statements/imports.js +31 -0
  376. package/dist/parser/statements/imports.js.map +1 -0
  377. package/dist/parser/statements/reactive.d.ts +31 -0
  378. package/dist/parser/statements/reactive.d.ts.map +1 -0
  379. package/dist/parser/statements/reactive.js +68 -0
  380. package/dist/parser/statements/reactive.js.map +1 -0
  381. package/dist/parser/statements/unsafe-css.d.ts +21 -0
  382. package/dist/parser/statements/unsafe-css.d.ts.map +1 -0
  383. package/dist/parser/statements/unsafe-css.js +42 -0
  384. package/dist/parser/statements/unsafe-css.js.map +1 -0
  385. package/dist/parser.d.ts +37 -50
  386. package/dist/parser.d.ts.map +1 -1
  387. package/dist/parser.js +98 -882
  388. package/dist/parser.js.map +1 -1
  389. package/dist/runtime-sources.generated.d.ts +1 -1
  390. package/dist/runtime-sources.generated.d.ts.map +1 -1
  391. package/dist/runtime-sources.generated.js +4 -4
  392. package/dist/runtime-sources.generated.js.map +1 -1
  393. package/package.json +2 -2
package/dist/parser.js CHANGED
@@ -1,107 +1,97 @@
1
- import { typeParameterDeclarationFormsPhrase } from "@velarscript/compiler";
2
- import { Parser, scanStringLiteral, } from "@velarscript/compiler/extension";
3
- import {} from "./ast.js";
4
- import { WEB_JSX_TOKEN, WEB_KEYFRAMES_TOKEN, WEB_LOOK_TOKEN, WEB_UNSAFE_CSS_TOKEN, } from "./lexer.js";
5
- import { visualBlockLayout } from "./visual-blocks.js";
6
- const span = (start, end) => ({ start, end });
7
- const diagnostic = (code, message, sourceSpan) => ({ code, message, span: sourceSpan });
8
- const recoveredDiagnostic = (code, message, sourceSpan) => ({ code, message, span: sourceSpan, recovered: true });
9
- const renderBlockSpellings = new Set(["render", "show", "view"]);
10
- const lifecycleHookSpellings = new Set(["mounted", "cleanup"]);
11
- // The tokens that may follow the declared name in each Web declaration head.
12
- const componentHeaderShapes = new Set(["leftParen", "colon", "less", "identifier"]);
13
- const reactiveBindingShapes = new Set(["assign", "colon"]);
14
- const actionHeaderShapes = new Set(["leftParen"]);
15
- // Tokens that open a fresh value rather than continuing the expression before
16
- // them; only these make `expose` the component's expose item.
17
- const exposeValueStartKinds = new Set([
18
- "identifier", "string", "fstring", "number", "unitNumber", "true", "false", "null", "super", "leftBrace", "extensionToken",
19
- ]);
1
+ /**
2
+ * The Web parser's composition root. `parser.ts` was one 1,050-line module;
3
+ * D115 P4 R3e split the syntax families into `parser/` and left the class that
4
+ * the Web extension registers here — its one field, the eight `protected` seams,
5
+ * and the host every collaborator reaches back through.
6
+ *
7
+ * Where to look:
8
+ *
9
+ * - `parser/spans.ts` span shifting, the three diagnostic constructors
10
+ * - `parser/look-source.ts` a `look:` block's own indented source
11
+ * - `parser/keyframes-source.ts` a `keyframes:` block's own indented source
12
+ * - `parser/statements/heads.ts` whether a contextual word is a declaration head
13
+ * - `parser/statements/components.ts` props, lifecycle names, the component item loop
14
+ * - `parser/statements/reactive.ts` state, computed, resource, action, watch
15
+ * - `parser/statements/imports.ts` `import css unsafe`
16
+ * - `parser/statements/unsafe-css.ts` the inline `unsafe css` block and its placement
17
+ * - `parser/expressions/jsx.ts` the JSX token and its interpolations
18
+ * - `parser/expressions/look.ts` `look:` as a value
19
+ * - `parser/expressions/keyframes.ts` `keyframes:` as a value
20
+ * - `parser/expressions/visual-block.ts` consuming the block those two are written in
21
+ *
22
+ * A collaborator never names `VelarWebParser`: it declares the interface it
23
+ * needs, and that interface is the record of what it depends on. The host's
24
+ * properties are live reads of the parser, because the token cursor moves under
25
+ * a collaborator while it runs.
26
+ */
27
+ import {} from "@velarscript/compiler";
28
+ import { Parser, } from "@velarscript/compiler/extension";
29
+ import { WEB_JSX_TOKEN } from "./lexer.js";
30
+ import { parseJsxExpression } from "./parser/expressions/jsx.js";
31
+ import { parseKeyframesExpression } from "./parser/expressions/keyframes.js";
32
+ import { parseLookExpression } from "./parser/expressions/look.js";
33
+ import { parseComponent, parseComponentProps } from "./parser/statements/components.js";
34
+ import { actionHeaderShapes, blockHeaderAhead, componentHeaderShapes, exposeItemAhead, namedDeclarationAhead, reactiveBindingShapes, } from "./parser/statements/heads.js";
35
+ import { parseCssImport } from "./parser/statements/imports.js";
36
+ import { parseActionDeclaration, parseComputedDeclaration, parseResourceDeclaration, parseStateDeclaration, parseWatchDeclaration, } from "./parser/statements/reactive.js";
37
+ import { parseUnsafeCssStatement } from "./parser/statements/unsafe-css.js";
38
+ import { diagnostic, span } from "./parser/spans.js";
20
39
  export class VelarWebParser extends Parser {
21
40
  insideComponentProps = 0;
41
+ host;
42
+ /**
43
+ * The one object every collaborator is handed. Its properties are live reads
44
+ * of the parser: the cursor moves under a collaborator, so `current()`, the
45
+ * diagnostics array and `insideComponentProps` must be the parser's own and
46
+ * not a snapshot taken when the host was built. `insideComponentProps` is
47
+ * `private` here, so it arrives as an accessor pair — the component body loop
48
+ * raises it, the prop list reads it back.
49
+ */
50
+ webParserHost() {
51
+ const parser = this;
52
+ return {
53
+ advance: () => parser.advance(),
54
+ check: (kind) => parser.check(kind),
55
+ checkWord: (value) => parser.checkWord(value),
56
+ consumeNewlines: () => parser.consumeNewlines(),
57
+ current: () => parser.current(),
58
+ get diagnostics() { return parser.diagnostics; },
59
+ expect: (kind, message) => parser.expect(kind, message),
60
+ expectStatementBoundary: () => parser.expectStatementBoundary(),
61
+ get insideComponentProps() { return parser.insideComponentProps; },
62
+ set insideComponentProps(value) { parser.insideComponentProps = value; },
63
+ match: (kind) => parser.match(kind),
64
+ matchExtensionKeyword: (value) => parser.matchExtensionKeyword(value),
65
+ matchWord: (value) => parser.matchWord(value),
66
+ parseBlock: () => parser.parseBlock(),
67
+ parseExpression: (minimumPrecedence) => parser.parseExpression(minimumPrecedence),
68
+ parseNestedExpression: (fragment, offset, bracketFragment, sourceOffsets) => parser.parseNestedExpression(fragment, offset, bracketFragment, sourceOffsets),
69
+ parseParameters: () => parser.parseParameters(),
70
+ parseStatement: () => parser.parseStatement(),
71
+ parseTypeParameters: () => parser.parseTypeParameters(),
72
+ parseTypeReference: (allowTrailingOptional) => parser.parseTypeReference(allowTrailingOptional),
73
+ peekKind: (distance) => parser.peekKind(distance),
74
+ peekValue: (distance) => parser.peekValue(distance),
75
+ previous: () => parser.previous(),
76
+ skipMistypedDeclaration: () => parser.skipMistypedDeclaration(),
77
+ };
78
+ }
22
79
  constructor(tokens, lexicalExtensions) {
23
80
  super(tokens, lexicalExtensions);
81
+ this.host = this.webParserHost();
24
82
  }
25
83
  /**
26
84
  * WEB-N4: a component prop list is where a Web author reaches for the HTML
27
- * names, and `class`, `look`, and the Core keywords are all tokens rather than
28
- * identifiers. Recovering the keyword as the prop name turns an eleven-message
29
- * parser cascade into one directed message, and a declaration-position `?`
30
- * teaches the default value that actually makes a prop omittable.
85
+ * names, and Core's own list is what every other parameter list is read with.
31
86
  */
32
87
  parseParameters() {
33
88
  if (this.insideComponentProps === 0)
34
89
  return super.parseParameters();
35
- this.expect("leftParen", "Expected '('");
36
- const parameters = [];
37
- if (!this.check("rightParen")) {
38
- do {
39
- if (this.match("ellipsis"))
40
- this.diagnostics.push(diagnostic("VEL2016", "Components use named props and do not support rest parameters", this.previous().span));
41
- // 'class' and 'look' are the props every component already carries, so
42
- // the same message answers both the keyword token and the now-ordinary
43
- // name.
44
- const universalProp = this.checkWord("look") || this.check("class");
45
- if (universalProp) {
46
- const token = this.advance();
47
- this.diagnostics.push(diagnostic("VEL2016", `Every component already accepts '${token.value}'; remove it from the prop list and pass it at the call site with ${token.value}={...}`, token.span));
48
- }
49
- const nameToken = universalProp ? this.previous() : this.check("identifier") ? this.advance() : this.componentPropKeyword();
50
- if (!nameToken) {
51
- this.diagnostics.push(diagnostic("VEL2016", "A component prop list holds 'name: Type' props separated by commas", this.current().span));
52
- break;
53
- }
54
- let optionalMarker = false;
55
- if (this.check("question")) {
56
- this.advance();
57
- optionalMarker = true;
58
- }
59
- const type = this.match("colon") ? this.parseTypeReference() : null;
60
- let defaultValue = this.match("assign") ? this.parseExpression() : null;
61
- if (optionalMarker) {
62
- this.diagnostics.push(diagnostic("VEL2016", `A component prop becomes omittable through its default value, not through '?': write '${nameToken.value}: Type = default' for a real default, or '${nameToken.value}: Type? = null' when absence is the value`, span(nameToken.span.start, (defaultValue ?? type ?? nameToken).span.end)));
63
- defaultValue ??= { kind: "LiteralExpression", value: null, raw: "null", span: nameToken.span };
64
- }
65
- parameters.push({
66
- name: nameToken.value,
67
- type: optionalMarker && type ? { syntax: { kind: "OptionalTypeSyntax", inner: type.syntax, span: type.span }, span: type.span } : type,
68
- defaultValue,
69
- rest: false,
70
- span: span(nameToken.span.start, (defaultValue ?? type ?? nameToken).span.end),
71
- });
72
- } while (this.match("comma") && !this.check("rightParen"));
73
- }
74
- this.expect("rightParen", "Expected ')' after parameters");
75
- return parameters;
76
- }
77
- /**
78
- * Consumes a keyword standing where a prop name belongs and reports the one
79
- * message that names it, or returns null when the token cannot be a name.
80
- */
81
- componentPropKeyword() {
82
- const token = this.current();
83
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(token.value))
84
- return null;
85
- if (token.kind === "identifier" || token.kind === "eof" || token.kind === "newline")
86
- return null;
87
- this.advance();
88
- this.diagnostics.push(diagnostic("VEL2016", `'${token.value}' is a VelarScript keyword and cannot name a component prop; choose another name`, token.span));
89
- return token;
90
+ return parseComponentProps(this.host);
90
91
  }
91
92
  createNestedParser(tokens) {
92
93
  return new VelarWebParser(tokens, this.lexicalExtensions);
93
94
  }
94
- /**
95
- * `@` selects the component's closed compiler namespace. The role names are
96
- * `mounted` and `cleanup`; neither is found through author-name lookup.
97
- */
98
- matchLifecycleHook(name) {
99
- if (!this.check("at") || this.peekKind(1) !== "identifier" || this.peekValue(1) !== name)
100
- return false;
101
- this.advance();
102
- this.advance();
103
- return true;
104
- }
105
95
  validateExtensionTypeArguments(name, arguments_, nameSpan) {
106
96
  if (name !== "Component")
107
97
  return false;
@@ -125,41 +115,41 @@ export class VelarWebParser extends Parser {
125
115
  this.skipMistypedDeclaration();
126
116
  return { kind: "PassStatement", span: span(keyword.span.start, name.span.end) };
127
117
  }
128
- if (this.namedDeclarationAhead("component", componentHeaderShapes)) {
118
+ if (namedDeclarationAhead(this.host, "component", componentHeaderShapes)) {
129
119
  this.advance();
130
120
  if (modifiers.abstract)
131
121
  this.diagnostics.push(diagnostic("VEL2013", "Only classes can be declared with 'abstract'", this.previous().span));
132
122
  if (modifiers.asynchronous)
133
123
  this.diagnostics.push(diagnostic("VEL2013", "Components are not declared with 'async'", this.previous().span));
134
- return this.parseComponent(start, modifiers.exported);
124
+ return parseComponent(this.host, start, modifiers.exported);
135
125
  }
136
126
  if (modifiers.abstract || modifiers.asynchronous)
137
127
  return undefined;
138
- if (this.namedDeclarationAhead("state", reactiveBindingShapes)) {
128
+ if (namedDeclarationAhead(this.host, "state", reactiveBindingShapes)) {
139
129
  this.advance();
140
- return this.parseStateDeclaration(start, modifiers.exported);
130
+ return parseStateDeclaration(this.host, start, modifiers.exported);
141
131
  }
142
- if (this.namedDeclarationAhead("computed", reactiveBindingShapes)) {
132
+ if (namedDeclarationAhead(this.host, "computed", reactiveBindingShapes)) {
143
133
  this.advance();
144
- return this.parseComputedDeclaration(start, modifiers.exported);
134
+ return parseComputedDeclaration(this.host, start, modifiers.exported);
145
135
  }
146
- if (this.namedDeclarationAhead("resource", reactiveBindingShapes)) {
136
+ if (namedDeclarationAhead(this.host, "resource", reactiveBindingShapes)) {
147
137
  this.advance();
148
138
  if (modifiers.exported)
149
139
  this.diagnostics.push(diagnostic("VEL2018", "A resource is component-owned and cannot be exported", this.previous().span));
150
- return this.parseResourceDeclaration(start, modifiers.exported);
140
+ return parseResourceDeclaration(this.host, start, modifiers.exported);
151
141
  }
152
- if (this.namedDeclarationAhead("action", actionHeaderShapes)) {
142
+ if (namedDeclarationAhead(this.host, "action", actionHeaderShapes)) {
153
143
  this.advance();
154
- return this.parseActionDeclaration(start, modifiers.exported);
144
+ return parseActionDeclaration(this.host, start, modifiers.exported);
155
145
  }
156
- if (this.blockHeaderAhead("watch")) {
146
+ if (blockHeaderAhead(this.host, "watch")) {
157
147
  this.advance();
158
148
  if (modifiers.exported)
159
149
  this.diagnostics.push(diagnostic("VEL2001", "A watch block cannot be exported", this.previous().span));
160
- return this.parseWatchDeclaration(start);
150
+ return parseWatchDeclaration(this.host, start);
161
151
  }
162
- if (this.exposeItemAhead()) {
152
+ if (exposeItemAhead(this.host)) {
163
153
  this.advance();
164
154
  const value = this.parseExpression();
165
155
  this.diagnostics.push(diagnostic("VEL5056", "'expose' is only valid as a top-level component item; declare 'exposes HandleType' on that component", span(start, value.span.end)));
@@ -168,119 +158,14 @@ export class VelarWebParser extends Parser {
168
158
  return undefined;
169
159
  }
170
160
  parseUnsafeExtensionStatement(start) {
171
- if (!this.checkWord("css") || this.peekKind(1) !== "extensionToken"
172
- || this.peekValue(1) !== WEB_UNSAFE_CSS_TOKEN)
173
- return undefined;
174
- this.advance();
175
- const token = this.advance();
176
- const payload = token.payload;
177
- if (!payload || payload.kind !== "WebUnsafeCssBlockSyntax") {
178
- this.diagnostics.push(diagnostic("VEL5037", "The inline unsafe CSS token is missing its raw source", token.span));
179
- return { kind: "PassStatement", span: token.span };
180
- }
181
- const placement = this.parseUnsafeCssPlacement();
182
- const declaration = {
183
- kind: "ExtensionStatement:web:unsafe-css",
184
- source: { kind: "inline", css: payload.css, span: payload.contentSpan },
185
- placement,
186
- span: span(start, this.previous().span.end),
187
- };
188
- return declaration;
189
- }
190
- /**
191
- * D30 item 16: a Web declaration head is claimed only in its own declaration
192
- * shape — the word, a name, and one of the tokens that can follow it there.
193
- * `state = 1`, `state(x)`, and `state.field` all keep the identifier reading.
194
- */
195
- namedDeclarationAhead(word, shapes) {
196
- return this.checkWord(word) && this.peekKind(1) === "identifier" && shapes.has(this.peekKind(2));
197
- }
198
- /**
199
- * `watch` opens a block: its header line ends in ':' and an indented body
200
- * follows. No expression statement can end in ':', so the lookahead is exact
201
- * and `watch = 1` / `watch(value)` stay ordinary code.
202
- */
203
- blockHeaderAhead(word) {
204
- if (!this.checkWord(word))
205
- return false;
206
- let depth = 0;
207
- let offset = 1;
208
- for (;; offset += 1) {
209
- const kind = this.peekKind(offset);
210
- if (kind === "leftParen" || kind === "leftBracket" || kind === "leftBrace")
211
- depth += 1;
212
- else if (kind === "rightParen" || kind === "rightBracket" || kind === "rightBrace")
213
- depth -= 1;
214
- else if (kind === "eof")
215
- return false;
216
- else if (depth === 0 && (kind === "newline" || kind === "dedent"))
217
- break;
218
- }
219
- if (offset < 3 || this.peekKind(offset - 1) !== "colon")
220
- return false;
221
- while (this.peekKind(offset) === "newline")
222
- offset += 1;
223
- return this.peekKind(offset) === "indent";
224
- }
225
- /**
226
- * `expose value` is the one Web statement head followed by a bare expression
227
- * rather than by a name and a shape token. It is claimed only when the next
228
- * token opens a fresh value, so `expose(handle)` reads as a call and
229
- * `expose = handle` as an assignment — the identifier reading wins wherever
230
- * the two could compete.
231
- */
232
- exposeItemAhead() {
233
- return this.checkWord("expose") && exposeValueStartKinds.has(this.peekKind(1));
161
+ return parseUnsafeCssStatement(this.host, start);
234
162
  }
235
163
  parseExtensionImport(start) {
236
- // `import css unsafe "./file.css"` against `import css from "./module.vel"`:
237
- // the CSS boundary is claimed only when the word is followed by the
238
- // boundary marker or by the path itself, so a module named `css` still
239
- // imports by name.
240
- if (!this.checkWord("css") || !(this.peekKind(1) === "unsafe" || this.peekKind(1) === "string"))
241
- return undefined;
242
- this.advance();
243
- this.expect("unsafe", "Native CSS is an unsafe boundary; write 'import css unsafe'");
244
- const source = this.expect("string", "Expected a relative .css path after 'import css unsafe'");
245
- const placement = this.parseUnsafeCssPlacement();
246
- if ((!source.value.startsWith("./") && !source.value.startsWith("../")) || !source.value.endsWith(".css")) {
247
- this.diagnostics.push(diagnostic("VEL5037", "Unsafe CSS imports require an explicit relative path ending in '.css'", source.span));
248
- }
249
- const declaration = {
250
- kind: "ExtensionStatement:web:unsafe-css",
251
- source: { kind: "external", path: source.value, span: source.span },
252
- placement,
253
- span: span(start, this.previous().span.end),
254
- };
255
- return declaration;
256
- }
257
- parseUnsafeCssPlacement() {
258
- let placement = "before";
259
- if (this.current().kind === "identifier" && this.current().value === "before") {
260
- this.expect("identifier", "Expected 'before'");
261
- placement = "before";
262
- }
263
- else if (this.current().kind === "identifier" && this.current().value === "after") {
264
- this.expect("identifier", "Expected 'after'");
265
- placement = "after";
266
- }
267
- else
268
- this.diagnostics.push(diagnostic("VEL5037", "Unsafe CSS must explicitly declare 'before look' or 'after look'", this.current().span));
269
- if (!this.matchExtensionKeyword("look")) {
270
- this.diagnostics.push(diagnostic("VEL5037", "Unsafe CSS order must end with 'look'", this.current().span));
271
- }
272
- return placement;
164
+ return parseCssImport(this.host, start);
273
165
  }
274
166
  parseExtensionExpression(token) {
275
- if (token.kind === "extensionToken" && token.value === WEB_JSX_TOKEN) {
276
- const payload = token.payload;
277
- if (!payload || payload.kind !== "WebJsxElementSyntax") {
278
- this.diagnostics.push(diagnostic("VEL5001", "The Web JSX token is missing its structured syntax", token.span));
279
- return { kind: "LiteralExpression", value: null, raw: "null", span: token.span };
280
- }
281
- const syntax = shiftJsxSyntax(payload, token.span.start - payload.span.start);
282
- return jsxExpression(syntax, (source) => this.parseJsxEmbedded(source), (item) => this.diagnostics.push(item));
283
- }
167
+ if (token.kind === "extensionToken" && token.value === WEB_JSX_TOKEN)
168
+ return parseJsxExpression(this.host, token);
284
169
  if (token.kind !== "identifier")
285
170
  return undefined;
286
171
  // `look:` and `keyframes:` open a block-valued expression. Without the ':'
@@ -288,679 +173,10 @@ export class VelarWebParser extends Parser {
288
173
  if (!this.check("colon"))
289
174
  return undefined;
290
175
  if (token.value === "keyframes")
291
- return this.parseKeyframesExpression(token);
176
+ return parseKeyframesExpression(this.host, token);
292
177
  if (token.value !== "look")
293
178
  return undefined;
294
- // LOK-I3: an unfinished Look value used to unravel into six diagnostics as each
295
- // expectation failed in turn; the whole shape is read up front, once, instead.
296
- const layout = visualBlockLayout((distance) => this.peekKind(distance));
297
- if (layout === "none") {
298
- this.diagnostics.push(diagnostic("VEL5038", "A Look value is written as 'look:' followed by an indented block of 'property = value' entries", token.span));
299
- this.skipMistypedDeclaration();
300
- return { kind: "LiteralExpression", value: null, raw: "null", span: token.span };
301
- }
302
- if (layout === "empty") {
303
- // The colon stays unconsumed so the surrounding statement still ends at
304
- // its own newline; only this one message describes the missing block.
305
- this.diagnostics.push(diagnostic("VEL5038", "A Look block requires at least one indented 'property = value' entry", token.span));
306
- this.advance();
307
- return { kind: "LiteralExpression", value: null, raw: "null", span: token.span };
308
- }
309
- const block = this.consumeVisualBlock(layout, "Expected Look entries", "Expected the end of the Look block");
310
- const payload = block.value === WEB_LOOK_TOKEN ? block.payload : undefined;
311
- if (!payload || payload.kind !== "WebLookBlockSyntax") {
312
- this.diagnostics.push(diagnostic("VEL5038", "The Look block is missing its structured syntax", block.span));
313
- }
314
- const syntax = payload?.kind === "WebLookBlockSyntax"
315
- ? shiftLookSyntax(payload, block.span.start - payload.span.start)
316
- : undefined;
317
- const entries = new LookSourceParser(syntax ?? { kind: "WebLookBlockSyntax", lines: [], span: block.span }, (text, offset, openingIndent) => openingIndent
318
- ? this.parseNestedExpression(openingIndent + text, offset - openingIndent.length, true)
319
- : this.parseNestedExpression(text, offset), (item) => this.diagnostics.push(item)).parse();
320
- const expression = { kind: "ExtensionExpression:web:look", entries, span: span(token.span.start, block.span.end) };
321
- return expression;
322
- }
323
- /** Consumes the ':' , the layout around the block, and the block itself. */
324
- consumeVisualBlock(layout, entries, end) {
325
- this.advance();
326
- if (layout === "bracketed")
327
- return this.expect("extensionToken", entries);
328
- this.consumeNewlines();
329
- this.advance();
330
- const block = this.expect("extensionToken", entries);
331
- this.consumeNewlines();
332
- this.expect("dedent", end);
333
- return block;
334
- }
335
- parseKeyframesExpression(token) {
336
- const layout = visualBlockLayout((distance) => this.peekKind(distance));
337
- if (layout === "none") {
338
- this.diagnostics.push(diagnostic("VEL5060", "A keyframes value is written as 'keyframes:' followed by indented 'from:', 'to:', or 'N%:' stops", token.span));
339
- this.skipMistypedDeclaration();
340
- return { kind: "LiteralExpression", value: null, raw: "null", span: token.span };
341
- }
342
- if (layout === "empty") {
343
- this.diagnostics.push(diagnostic("VEL5060", "A keyframes block requires at least one indented stop", token.span));
344
- this.advance();
345
- return { kind: "LiteralExpression", value: null, raw: "null", span: token.span };
346
- }
347
- const block = this.consumeVisualBlock(layout, "Expected keyframe stops", "Expected the end of the keyframes block");
348
- const payload = block.value === WEB_KEYFRAMES_TOKEN ? block.payload : undefined;
349
- if (!payload || payload.kind !== "WebKeyframesBlockSyntax") {
350
- this.diagnostics.push(diagnostic("VEL5060", "The keyframes block is missing its structured syntax", block.span));
351
- }
352
- const syntax = payload?.kind === "WebKeyframesBlockSyntax"
353
- ? shiftKeyframesSyntax(payload, block.span.start - payload.span.start)
354
- : undefined;
355
- const stops = new KeyframesSourceParser(syntax ?? { kind: "WebKeyframesBlockSyntax", lines: [], span: block.span }, (text, offset, openingIndent) => openingIndent
356
- ? this.parseNestedExpression(openingIndent + text, offset - openingIndent.length, true)
357
- : this.parseNestedExpression(text, offset), (item) => this.diagnostics.push(item)).parse();
358
- const expression = { kind: "ExtensionExpression:web:keyframes", stops, span: span(token.span.start, block.span.end) };
359
- return expression;
360
- }
361
- // A '{for item in items: ...}' block inside JSX gets targeted guidance to
362
- // '.map(...)' instead of an expression-parse cascade; there is no magic JSX
363
- // control flow. The child recovers as an inert null literal so the rest of
364
- // the module still analyzes and reports its own guidance in the same compile.
365
- parseJsxEmbedded(source) {
366
- // WEB-U13: '{/* ... */}' is the JSX comment habit. VelarScript has no block
367
- // comment at all, so the interpolation gets one message naming '//' instead
368
- // of two 'Expected an expression' failures.
369
- if (/^\s*\/[*/]/u.test(source.source)) {
370
- this.diagnostics.push(recoveredDiagnostic("VEL5002", "JSX has no comment form; write a '//' comment on its own line outside the markup", source.span));
371
- return { kind: "LiteralExpression", value: null, raw: "null", span: source.span };
372
- }
373
- if (/^\s*for\b/u.test(source.source)) {
374
- const detail = /^\s*for\s+([A-Za-z_][A-Za-z0-9_]*)\s+in\s+([^:{\n]+):/u.exec(source.source);
375
- const binding = detail?.[1] ?? "item";
376
- const iterable = detail?.[2]?.trim() || "items";
377
- this.diagnostics.push({
378
- code: "VEL5049",
379
- message: `Use '{${iterable}.map((${binding}) => ...)}'; JSX has no 'for' blocks, so lists render with '.map(...)'`,
380
- span: source.span,
381
- recovered: true,
382
- });
383
- return { kind: "LiteralExpression", value: null, raw: "null", span: source.span };
384
- }
385
- // JSX interpolation braces are a bracket context: the expression inside
386
- // '{...}' continues across physical lines exactly as inside parentheses.
387
- const layoutAtStart = /^[ \t]*(?:rf|fr|f|r)?["'](?:\r\n|\r|\n)/u.test(source.source);
388
- return layoutAtStart
389
- ? this.parseNestedExpression(source.openingIndent + source.source, source.span.start - source.openingIndent.length, true)
390
- : this.parseNestedExpression(source.source, source.span.start, true);
391
- }
392
- parseStateDeclaration(start, exported) {
393
- const name = this.expect("identifier", "Expected a state name");
394
- const type = this.match("colon") ? this.parseTypeReference() : null;
395
- this.expect("assign", "Expected '=' after state name");
396
- const initializer = this.parseExpression();
397
- return { kind: "ExtensionStatement:web:state", exported, name: name.value, type, initializer, span: span(start, initializer.span.end) };
398
- }
399
- /**
400
- * D71 rule 182: `computed` parses exactly where `state` parses, through the
401
- * same shape lookahead — the two halves of the reactive row differ in what
402
- * they mean, not in how they are written.
403
- */
404
- parseComputedDeclaration(start, exported) {
405
- const name = this.expect("identifier", "Expected a computed name");
406
- const type = this.match("colon") ? this.parseTypeReference() : null;
407
- this.expect("assign", "Expected '=' after computed name");
408
- const initializer = this.parseExpression();
409
- return { kind: "ExtensionStatement:web:computed", exported, name: name.value, type, initializer, span: span(start, initializer.span.end) };
410
- }
411
- parseResourceDeclaration(start, exported) {
412
- const name = this.expect("identifier", "Expected a resource name");
413
- const type = this.match("colon") ? this.parseTypeReference() : null;
414
- this.expect("assign", "Expected '=' after resource name");
415
- const initializer = this.parseExpression();
416
- return { kind: "ExtensionStatement:web:resource", exported, name: name.value, type, initializer, span: span(start, initializer.span.end) };
417
- }
418
- parseActionDeclaration(start, exported) {
419
- const name = this.expect("identifier", "Expected an action name");
420
- const parameters = this.parseParameters();
421
- const parameterListEnd = this.previous().span.end;
422
- const returnType = this.match("arrow") ? this.parseTypeReference() : null;
423
- const body = this.parseBlock();
424
- const end = body.at(-1)?.span.end ?? returnType?.span.end ?? name.span.end;
425
- return {
426
- kind: "ExtensionStatement:web:action",
427
- exported,
428
- name: name.value,
429
- parameters,
430
- returnType,
431
- ...(returnType ? { resultAnnotationSpan: span(parameterListEnd, returnType.span.end) } : {}),
432
- signatureSpan: span(start, returnType?.span.end ?? parameterListEnd),
433
- body,
434
- span: span(start, end),
435
- };
436
- }
437
- parseWatchDeclaration(start) {
438
- const expression = this.parseExpression();
439
- let currentName = null;
440
- let previousName = null;
441
- if (this.matchWord("as")) {
442
- currentName = this.expect("identifier", "Expected the current watch value name").value;
443
- this.expect("comma", "Expected ',' between watch value names");
444
- previousName = this.expect("identifier", "Expected the previous watch value name").value;
445
- }
446
- const body = this.parseBlock();
447
- return { kind: "ExtensionStatement:web:watch", expression, currentName, previousName, body, span: span(start, body.at(-1)?.span.end ?? expression.span.end) };
448
- }
449
- parseComponent(start, exported) {
450
- const name = this.expect("identifier", "Expected a component name");
451
- if (this.check("less")) {
452
- this.parseTypeParameters();
453
- this.diagnostics.push(diagnostic("VEL2025", `Component '${name.value}' cannot declare type parameters; ${typeParameterDeclarationFormsPhrase()} take '<T>'`, name.span));
454
- }
455
- this.insideComponentProps += 1;
456
- const parameters = this.check("leftParen") ? this.parseParameters() : [];
457
- this.insideComponentProps -= 1;
458
- for (const parameter of parameters) {
459
- if (parameter.rest) {
460
- this.diagnostics.push(diagnostic("VEL2016", "Components use named props and do not support rest parameters", parameter.span));
461
- }
462
- }
463
- const handleType = this.matchExtensionKeyword("exposes") ? this.parseTypeReference() : null;
464
- this.expect("colon", "Expected ':' before component body");
465
- this.expect("newline", "Expected a newline before component body");
466
- this.consumeNewlines();
467
- this.expect("indent", "Expected an indented component body");
468
- const body = [];
469
- this.consumeNewlines();
470
- while (!this.check("dedent") && !this.check("eof")) {
471
- const itemStart = this.current().span.start;
472
- let item = null;
473
- if (this.exposeItemAhead()) {
474
- this.advance();
475
- const value = this.parseExpression();
476
- item = { kind: "ExtensionStatement:web:expose", value, span: span(itemStart, value.span.end) };
477
- }
478
- else if (this.namedDeclarationAhead("state", reactiveBindingShapes)) {
479
- this.advance();
480
- item = this.parseStateDeclaration(itemStart, false);
481
- }
482
- else if (this.namedDeclarationAhead("computed", reactiveBindingShapes)) {
483
- this.advance();
484
- item = this.parseComputedDeclaration(itemStart, false);
485
- }
486
- else if (this.namedDeclarationAhead("resource", reactiveBindingShapes)) {
487
- this.advance();
488
- item = this.parseResourceDeclaration(itemStart, false);
489
- }
490
- else if (this.namedDeclarationAhead("action", actionHeaderShapes)) {
491
- this.advance();
492
- item = this.parseActionDeclaration(itemStart, false);
493
- }
494
- else if (this.blockHeaderAhead("watch")) {
495
- this.advance();
496
- item = this.parseWatchDeclaration(itemStart);
497
- }
498
- else if (this.matchLifecycleHook("mounted")) {
499
- const body = this.parseBlock();
500
- item = { kind: "ExtensionStatement:web:mounted", body, span: span(itemStart, body.at(-1)?.span.end ?? itemStart) };
501
- }
502
- else if (this.matchLifecycleHook("cleanup")) {
503
- const body = this.parseBlock();
504
- item = { kind: "ExtensionStatement:web:cleanup", body, span: span(itemStart, body.at(-1)?.span.end ?? itemStart) };
505
- }
506
- else if (this.check("at")) {
507
- const marker = this.advance();
508
- const name = this.check("identifier") ? this.advance() : null;
509
- this.diagnostics.push(diagnostic("VEL5061", name
510
- ? `Unknown compiler-owned name '@${name.value}' in a component; the component namespace contains only '@mounted:' and '@cleanup:'`
511
- : "Expected a compiler-owned component name after '@'; the component namespace contains only '@mounted:' and '@cleanup:'", span(marker.span.start, (name ?? marker).span.end)));
512
- this.skipMistypedDeclaration();
513
- }
514
- else if (this.check("identifier") && lifecycleHookSpellings.has(this.current().value) && this.peekKind(1) === "colon") {
515
- // The bare words are ordinary author names. Recover as the one accepted
516
- // compiler-owned spelling so the body keeps analyzing, while retaining
517
- // a diagnostic: recovery is not a second alias.
518
- const keyword = this.advance();
519
- this.diagnostics.push(recoveredDiagnostic("VEL5061", `Use '@${keyword.value}:'; it is a compiler-owned component name, which leaves '${keyword.value}' free for your own method`, keyword.span));
520
- const body = this.parseBlock();
521
- item = keyword.value === "mounted"
522
- ? { kind: "ExtensionStatement:web:mounted", body, span: span(itemStart, body.at(-1)?.span.end ?? itemStart) }
523
- : { kind: "ExtensionStatement:web:cleanup", body, span: span(itemStart, body.at(-1)?.span.end ?? itemStart) };
524
- }
525
- else if (this.check("identifier") && renderBlockSpellings.has(this.current().value) && this.peekKind(1) === "colon") {
526
- const keyword = this.advance();
527
- this.diagnostics.push(diagnostic("VEL5048", `Use 'return <...>'; a component returns its JSX directly and has no '${keyword.value}:' block`, keyword.span));
528
- this.skipMistypedDeclaration();
529
- }
530
- else {
531
- item = this.parseStatement();
532
- }
533
- if (item)
534
- body.push(item);
535
- if (this.previous().kind !== "dedent")
536
- this.expectStatementBoundary();
537
- this.consumeNewlines();
538
- }
539
- const close = this.expect("dedent", "Expected the end of component body");
540
- return { kind: "ExtensionStatement:web:component", exported, name: name.value, parameters, handleType, body, span: span(start, body.at(-1)?.span.end ?? close.span.end) };
541
- }
542
- }
543
- function shiftSourceSpan(sourceSpan, offset) {
544
- return offset === 0 ? sourceSpan : span(sourceSpan.start + offset, sourceSpan.end + offset);
545
- }
546
- function shiftExpressionSource(source, offset) {
547
- return offset === 0 ? source : { ...source, span: shiftSourceSpan(source.span, offset) };
548
- }
549
- function shiftJsxSyntax(syntax, offset) {
550
- if (offset === 0)
551
- return syntax;
552
- return {
553
- ...syntax,
554
- span: shiftSourceSpan(syntax.span, offset),
555
- tagSpan: shiftSourceSpan(syntax.tagSpan, offset),
556
- attributes: syntax.attributes.map((attribute) => ({
557
- ...attribute,
558
- span: shiftSourceSpan(attribute.span, offset),
559
- value: typeof attribute.value === "object" && attribute.value !== null
560
- ? shiftExpressionSource(attribute.value, offset)
561
- : attribute.value,
562
- })),
563
- children: syntax.children.map((child) => {
564
- if (child.kind === "WebJsxElementSyntax")
565
- return shiftJsxSyntax(child, offset);
566
- if (child.kind === "WebJsxExpressionSyntax") {
567
- return {
568
- ...child,
569
- span: shiftSourceSpan(child.span, offset),
570
- expression: shiftExpressionSource(child.expression, offset),
571
- };
572
- }
573
- return { ...child, span: shiftSourceSpan(child.span, offset) };
574
- }),
575
- };
576
- }
577
- function shiftLookSyntax(syntax, offset) {
578
- if (offset === 0)
579
- return syntax;
580
- return {
581
- ...syntax,
582
- span: shiftSourceSpan(syntax.span, offset),
583
- lines: syntax.lines.map((line) => ({
584
- ...line,
585
- start: line.start + offset,
586
- end: line.end + offset,
587
- })),
588
- };
589
- }
590
- function shiftKeyframesSyntax(syntax, offset) {
591
- if (offset === 0)
592
- return syntax;
593
- return {
594
- ...syntax,
595
- span: shiftSourceSpan(syntax.span, offset),
596
- lines: syntax.lines.map((line) => ({ ...line, start: line.start + offset, end: line.end + offset })),
597
- };
598
- }
599
- function jsxExpression(syntax, parseExpression, report) {
600
- return {
601
- kind: "ExtensionExpression:web:jsx",
602
- tag: syntax.tag,
603
- tagSpan: syntax.tagSpan,
604
- attributes: syntax.attributes.map((attribute) => ({
605
- name: attribute.name,
606
- value: typeof attribute.value === "object" && attribute.value !== null
607
- ? parseExpression(attribute.value)
608
- : attribute.value,
609
- span: attribute.span,
610
- })),
611
- children: syntax.children.map((child) => {
612
- if (child.kind === "WebJsxElementSyntax")
613
- return jsxExpression(child, parseExpression, report);
614
- if (child.kind === "WebJsxExpressionSyntax") {
615
- return { kind: "JSXExpressionChild", expression: parseExpression(child.expression), span: child.span };
616
- }
617
- // A bare (unbraced) 'for name in expr:' line written directly as JSX
618
- // content receives the same .map() guidance as its braced spelling;
619
- // there is no magic JSX control flow.
620
- const bareFor = /(?:^|\n)[ \t]*for\s+([A-Za-z_][A-Za-z0-9_]*)\s+in\s+([^:{<\n]+):/u.exec(child.value);
621
- if (bareFor) {
622
- const offset = child.span.start + (bareFor.index + bareFor[0].indexOf("for"));
623
- report(recoveredDiagnostic("VEL5049", `Use '{${bareFor[2].trim()}.map((${bareFor[1]}) => ...)}'; JSX has no 'for' blocks, so lists render with '.map(...)'`, span(offset, offset + (bareFor[0].length - bareFor[0].indexOf("for")))));
624
- }
625
- return { kind: "JSXText", value: child.value, span: child.span };
626
- }),
627
- span: syntax.span,
628
- };
629
- }
630
- class LookSourceParser {
631
- lines;
632
- blockSpan;
633
- parseExpression;
634
- report;
635
- index = 0;
636
- constructor(block, parseExpression, report) {
637
- this.blockSpan = block.span;
638
- this.parseExpression = parseExpression;
639
- this.report = report;
640
- this.lines = block.lines;
641
- }
642
- parse() {
643
- if (this.lines.length === 0) {
644
- this.report(diagnostic("VEL5038", "A Look block requires at least one entry", this.blockSpan));
645
- return [];
646
- }
647
- return this.parseEntries(this.lines[0].indent);
179
+ return parseLookExpression(this.host, token);
648
180
  }
649
- parseEntries(indent) {
650
- const entries = [];
651
- while (this.index < this.lines.length) {
652
- const line = this.lines[this.index];
653
- if (line.indent < indent)
654
- break;
655
- if (line.indent > indent) {
656
- this.report(diagnostic("VEL5038", "Unexpected Look indentation", this.lineSpan(line)));
657
- this.index += 1;
658
- continue;
659
- }
660
- this.index += 1;
661
- if (line.text.startsWith("if ") && line.text.endsWith(":")) {
662
- entries.push(this.parseIf(line, indent, "if "));
663
- continue;
664
- }
665
- if (line.text === "else:" || line.text.startsWith("else if ")) {
666
- this.report(diagnostic("VEL5038", "Look 'else' must immediately follow an 'if' at the same indentation", this.lineSpan(line)));
667
- continue;
668
- }
669
- // A kebab-case property receives camelCase guidance and recovers as the
670
- // camelCase entry, so semantic analysis still checks its value and every
671
- // other Look and JSX diagnostic co-reports in the same compile.
672
- const kebab = /^([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z][A-Za-z0-9]*)+)\s*=(.*)$/u.exec(line.text);
673
- const property = kebab
674
- ? null
675
- : /^([A-Za-z][A-Za-z0-9]*)\s*=\s*([\s\S]+)$/u.exec(line.text);
676
- if (kebab && kebab[2].trim().length === 0) {
677
- const camel = kebab[1].replace(/-+([A-Za-z])/gu, (_, letter) => letter.toUpperCase());
678
- this.report(diagnostic("VEL5038", `Use '${camel}'; Look properties use the DOM camelCase spelling`, this.lineSpan(line)));
679
- continue;
680
- }
681
- if (kebab || property) {
682
- const propertyName = kebab
683
- ? kebab[1].replace(/-+([A-Za-z])/gu, (_, letter) => letter.toUpperCase())
684
- : property[1];
685
- if (kebab) {
686
- this.report(recoveredDiagnostic("VEL5038", `Use '${propertyName}'; Look properties use the DOM camelCase spelling`, this.lineSpan(line)));
687
- }
688
- const assignment = line.text.indexOf("=");
689
- const afterAssignment = line.text.slice(assignment + 1);
690
- const valueText = afterAssignment.trim();
691
- const valueStart = line.start + assignment + 1 + (afterAssignment.length - afterAssignment.trimStart().length);
692
- if (/^(?:margin|padding|inset)/u.test(propertyName) && /^[+-]?\d[\w.%]*(?:\s+[+-]?\d[\w.%]*)+$/u.test(valueText)) {
693
- const builderArguments = valueText
694
- .split(/\s+/u)
695
- .map((token) => (/^[+-]?\d+(?:\.\d+)?$/u.test(token) ? `${token}px` : token))
696
- .join(", ");
697
- this.report(recoveredDiagnostic("VEL5038", `Use 'spacing(${builderArguments})'; Look multi-value shorthand is written with the spacing builder`, this.lineSpan(line)));
698
- entries.push({
699
- kind: "LookProperty",
700
- name: propertyName,
701
- value: { kind: "LiteralExpression", value: null, raw: "null", span: span(valueStart, valueStart + valueText.length) },
702
- span: this.lineSpan(line),
703
- });
704
- continue;
705
- }
706
- entries.push({
707
- kind: "LookProperty",
708
- name: propertyName,
709
- value: this.parseExpression(valueText, valueStart, /[\r\n]/u.test(valueText) ? line.openingIndent : undefined),
710
- span: this.lineSpan(line),
711
- });
712
- continue;
713
- }
714
- if (line.text.startsWith("...")) {
715
- const afterSpread = line.text.slice(3);
716
- const valueText = afterSpread.trim();
717
- if (!valueText) {
718
- this.report(diagnostic("VEL5038", "Look composition requires a value after '...'", this.lineSpan(line)));
719
- continue;
720
- }
721
- entries.push({
722
- kind: "LookSpread",
723
- value: this.parseExpression(valueText, line.start + 3 + (afterSpread.length - afterSpread.trimStart().length)),
724
- span: this.lineSpan(line),
725
- });
726
- continue;
727
- }
728
- const target = /^@([A-Za-z][A-Za-z0-9]*):$/u.exec(line.text)?.[1];
729
- if (!target) {
730
- this.report(diagnostic("VEL5038", "Look entries use 'property = value', 'if condition:', '@target:', or composition with '...'", this.lineSpan(line)));
731
- continue;
732
- }
733
- const next = this.lines[this.index];
734
- if (!next || next.indent <= line.indent) {
735
- this.report(diagnostic("VEL5038", `Look target '@${target}' requires an indented body`, this.lineSpan(line)));
736
- continue;
737
- }
738
- const children = this.parseEntries(next.indent);
739
- entries.push({ kind: "LookTarget", name: target, entries: children, span: span(line.start, children.at(-1)?.span.end ?? line.end) });
740
- }
741
- return entries;
742
- }
743
- parseIf(line, indent, prefix) {
744
- const conditionSource = line.text.slice(prefix.length, -1);
745
- const conditionText = conditionSource.trim();
746
- const conditionOffset = line.start + prefix.length + (conditionSource.length - conditionSource.trimStart().length);
747
- const condition = this.parseLookCondition(conditionText, conditionOffset);
748
- const next = this.lines[this.index];
749
- let thenEntries = [];
750
- if (!next || next.indent <= line.indent) {
751
- this.report(diagnostic("VEL5038", "A Look if branch requires an indented body", this.lineSpan(line)));
752
- }
753
- else {
754
- thenEntries = this.parseEntries(next.indent);
755
- }
756
- let elseEntries = [];
757
- const alternate = this.lines[this.index];
758
- if (alternate?.indent === indent && alternate.text.startsWith("else if ") && alternate.text.endsWith(":")) {
759
- this.index += 1;
760
- elseEntries = [this.parseIf(alternate, indent, "else if ")];
761
- }
762
- else if (alternate?.indent === indent && alternate.text === "else:") {
763
- this.index += 1;
764
- const elseBody = this.lines[this.index];
765
- if (!elseBody || elseBody.indent <= alternate.indent) {
766
- this.report(diagnostic("VEL5038", "A Look else branch requires an indented body", this.lineSpan(alternate)));
767
- }
768
- else {
769
- elseEntries = this.parseEntries(elseBody.indent);
770
- }
771
- }
772
- return {
773
- kind: "LookIf",
774
- condition,
775
- thenEntries,
776
- elseEntries,
777
- span: span(line.start, elseEntries.at(-1)?.span.end ?? thenEntries.at(-1)?.span.end ?? line.end),
778
- };
779
- }
780
- parseLookCondition(text, absoluteOffset) {
781
- const hooks = new Map();
782
- let rewritten = "";
783
- // Every rewrite here is length-preserving, because `hooks` is keyed by an
784
- // offset into `text` and `replaceLookHooks` looks those keys up against
785
- // spans parsed out of `rewritten`. `@name` becomes `_name` and a string
786
- // literal is copied verbatim, so the two offsets stay equal.
787
- for (let index = 0; index < text.length;) {
788
- // A string literal is Core's to define — prefixes, escapes, raw content
789
- // and every delimiter — so the `@` rewrite steps over whatever Core
790
- // scans rather than keeping a second spelling of string lexing here.
791
- const literal = scanStringLiteral(text, index);
792
- if (literal) {
793
- const end = Math.max(literal.end, index + 1);
794
- rewritten += text.slice(index, end);
795
- index = end;
796
- continue;
797
- }
798
- const match = /^@([A-Za-z][A-Za-z0-9]*)/u.exec(text.slice(index));
799
- if (match) {
800
- hooks.set(absoluteOffset + index, match[1]);
801
- rewritten += `_${match[1]}`;
802
- index += match[0].length;
803
- continue;
804
- }
805
- rewritten += text[index];
806
- index += 1;
807
- }
808
- const parsed = this.parseExpression(rewritten, absoluteOffset);
809
- return replaceLookHooks(parsed, hooks);
810
- }
811
- lineSpan(line) {
812
- return span(line.start, line.end);
813
- }
814
- }
815
- class KeyframesSourceParser {
816
- lines;
817
- blockSpan;
818
- parseExpression;
819
- report;
820
- seenOffsets = new Set();
821
- index = 0;
822
- previousGroupStart = -1;
823
- constructor(block, parseExpression, report) {
824
- this.lines = block.lines;
825
- this.blockSpan = block.span;
826
- this.parseExpression = parseExpression;
827
- this.report = report;
828
- }
829
- parse() {
830
- if (this.lines.length === 0) {
831
- this.report(diagnostic("VEL5060", "A keyframes block requires at least one stop", this.blockSpan));
832
- return [];
833
- }
834
- const indent = this.lines[0].indent;
835
- const stops = [];
836
- while (this.index < this.lines.length) {
837
- const line = this.lines[this.index];
838
- if (line.indent !== indent) {
839
- this.report(diagnostic("VEL5060", "Unexpected keyframes indentation; stops share one indentation level", this.lineSpan(line)));
840
- this.index += 1;
841
- continue;
842
- }
843
- this.index += 1;
844
- const label = /^(.+):$/u.exec(line.text)?.[1]?.trim();
845
- if (!label) {
846
- this.report(diagnostic("VEL5060", "A keyframe stop uses 'from:', 'to:', or a percentage such as '50%:'", this.lineSpan(line)));
847
- continue;
848
- }
849
- const offsets = this.parseOffsets(label, line);
850
- const next = this.lines[this.index];
851
- if (!next || next.indent <= line.indent) {
852
- this.report(diagnostic("VEL5060", `Keyframe stop '${label}' requires an indented property body`, this.lineSpan(line)));
853
- continue;
854
- }
855
- const entries = this.parseEntries(next.indent, line.indent);
856
- if (offsets.length > 0)
857
- stops.push({
858
- offsets,
859
- entries,
860
- span: span(line.start, entries.at(-1)?.span.end ?? line.end),
861
- });
862
- }
863
- // WB-I6: no summary. Every path above pushes a stop or says why that line is not one, on the line rather than the block.
864
- return stops;
865
- }
866
- parseOffsets(label, line) {
867
- const parts = label.split(",").map((part) => part.trim());
868
- const offsets = [];
869
- for (const part of parts) {
870
- let offset = part === "from" ? 0 : part === "to" ? 100 : null;
871
- const percentage = /^(\d+(?:\.\d+)?)%$/u.exec(part);
872
- if (percentage) {
873
- offset = Number(percentage[1]);
874
- if (offset === 0 || offset === 100) {
875
- this.report(diagnostic("VEL5060", `Use '${offset === 0 ? "from" : "to"}:'; ${offset}% has one canonical keyframe spelling`, this.lineSpan(line)));
876
- continue;
877
- }
878
- if (!(offset > 0 && offset < 100)) {
879
- this.report(diagnostic("VEL5060", `Keyframe percentage '${part}' must be greater than 0% and less than 100%`, this.lineSpan(line)));
880
- continue;
881
- }
882
- }
883
- if (offset === null) {
884
- this.report(diagnostic("VEL5060", `Unknown keyframe stop '${part}'; use from, to, or a percentage between them`, this.lineSpan(line)));
885
- continue;
886
- }
887
- if (this.seenOffsets.has(offset)) {
888
- this.report(diagnostic("VEL5060", `Keyframe stop '${part}' duplicates ${offset === 0 ? "from" : offset === 100 ? "to" : `${offset}%`}`, this.lineSpan(line)));
889
- continue;
890
- }
891
- this.seenOffsets.add(offset);
892
- offsets.push(offset);
893
- }
894
- const groupStart = offsets.length > 0 ? Math.min(...offsets) : this.previousGroupStart;
895
- if (groupStart < this.previousGroupStart) {
896
- this.report(diagnostic("VEL5060", "Keyframe stops must be declared in ascending order", this.lineSpan(line)));
897
- }
898
- else
899
- this.previousGroupStart = groupStart;
900
- return offsets;
901
- }
902
- parseEntries(indent, stopIndent) {
903
- const entries = [];
904
- while (this.index < this.lines.length) {
905
- const line = this.lines[this.index];
906
- if (line.indent <= stopIndent)
907
- break;
908
- this.index += 1;
909
- if (line.indent !== indent) {
910
- this.report(diagnostic("VEL5060", "Keyframe stop bodies cannot contain nested targets, conditions, or blocks", this.lineSpan(line)));
911
- continue;
912
- }
913
- if (line.text.startsWith("if ") || line.text.startsWith("@") || line.text.startsWith("...") || line.text === "look:") {
914
- this.report(diagnostic("VEL5060", "Keyframe stops contain only direct Look properties; conditions, targets, composition, and spreads are not allowed", this.lineSpan(line)));
915
- continue;
916
- }
917
- const kebab = /^([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z][A-Za-z0-9]*)+)\s*=\s*([\s\S]+)$/u.exec(line.text);
918
- const property = kebab ? null : /^([A-Za-z][A-Za-z0-9]*)\s*=\s*([\s\S]+)$/u.exec(line.text);
919
- if (!kebab && !property) {
920
- this.report(diagnostic("VEL5060", "A keyframe property is written as 'property = value'", this.lineSpan(line)));
921
- continue;
922
- }
923
- const name = kebab
924
- ? kebab[1].replace(/-+([A-Za-z])/gu, (_, letter) => letter.toUpperCase())
925
- : property[1];
926
- if (kebab)
927
- this.report(recoveredDiagnostic("VEL5038", `Use '${name}'; Look properties use the DOM camelCase spelling`, this.lineSpan(line)));
928
- const assignment = line.text.indexOf("=");
929
- const afterAssignment = line.text.slice(assignment + 1);
930
- const valueText = afterAssignment.trim();
931
- const valueStart = line.start + assignment + 1 + (afterAssignment.length - afterAssignment.trimStart().length);
932
- entries.push({
933
- kind: "LookProperty",
934
- name,
935
- value: this.parseExpression(valueText, valueStart, /[\r\n]/u.test(valueText) ? line.openingIndent : undefined),
936
- span: this.lineSpan(line),
937
- });
938
- }
939
- return entries;
940
- }
941
- lineSpan(line) {
942
- return span(line.start, line.end);
943
- }
944
- }
945
- function replaceLookHooks(expression, hooks) {
946
- if (expression.kind === "IdentifierExpression" && hooks.has(expression.span.start)) {
947
- const hook = { kind: "ExtensionExpression:web:look-hook", name: hooks.get(expression.span.start), span: expression.span };
948
- return hook;
949
- }
950
- const visit = (value) => {
951
- if (Array.isArray(value))
952
- return value.map(visit);
953
- if (!value || typeof value !== "object")
954
- return value;
955
- const record = value;
956
- if (record.kind === "IdentifierExpression" && typeof record.span === "object" && record.span) {
957
- const sourceSpan = record.span;
958
- const name = hooks.get(sourceSpan.start);
959
- if (name)
960
- return { kind: "ExtensionExpression:web:look-hook", name, span: sourceSpan };
961
- }
962
- return Object.fromEntries(Object.entries(record).map(([key, child]) => [key, key === "span" ? child : visit(child)]));
963
- };
964
- return visit(expression);
965
181
  }
966
182
  //# sourceMappingURL=parser.js.map