@torpor/view 0.0.14 → 0.1.1

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 (276) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +5 -5
  3. package/dist/compile.cjs +77 -32
  4. package/dist/compile.cjs.map +1 -1
  5. package/dist/compile.js +77 -32
  6. package/dist/compile.js.map +1 -1
  7. package/dist/index.cjs +31 -15
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +2 -4
  10. package/dist/index.d.ts +2 -4
  11. package/dist/index.js +31 -14
  12. package/dist/index.js.map +1 -1
  13. package/dist/metafile-cjs.json +1 -1
  14. package/dist/metafile-esm.json +1 -1
  15. package/package.json +4 -4
  16. package/scripts/copyToRepl.ts +1 -1
  17. package/src/compile/build/client/buildCode.ts +2 -11
  18. package/src/compile/build/client/buildComponentNode.ts +21 -5
  19. package/src/compile/build/client/buildElementNode.ts +27 -10
  20. package/src/compile/build/client/buildFragment.ts +37 -1
  21. package/src/compile/build/client/buildHtmlNode.ts +1 -0
  22. package/src/compile/build/client/buildSlotNode.ts +4 -1
  23. package/src/compile/build/server/buildServerComponentNode.ts +4 -1
  24. package/src/compile/build/server/buildServerElementNode.ts +5 -2
  25. package/src/compile/build/server/buildServerSlotNode.ts +4 -1
  26. package/src/compile/parse/parseCode.ts +20 -12
  27. package/src/index.ts +0 -2
  28. package/src/motion/animate.ts +10 -6
  29. package/src/render/$unwrap.ts +1 -1
  30. package/src/render/addFragment.ts +14 -4
  31. package/src/render/hydrate.ts +7 -0
  32. package/src/render/nodeNext.ts +14 -7
  33. package/src/render/runControlBranch.ts +11 -1
  34. package/src/render/runList.ts +8 -2
  35. package/test/attributes/attributes.test.ts +3 -0
  36. package/test/attributes/components/Attributes.torp +6 -1
  37. package/test/attributes/components/output/Attributes-client.ts +6 -2
  38. package/test/attributes/components/output/Attributes-server.ts +2 -1
  39. package/test/attributes/components/temp/{Attributes-client-c4812cb9307eac6ff44f90f3358a1811b52c7390.ts → Attributes-client-7b8f7b9996208e8d2d0a12d4f4a40a73df63d9d8.ts} +6 -2
  40. package/test/attributes/components/temp/{Attributes-server-a7ebed503f2169f7c900d4613531082f1023b086.ts → Attributes-server-a1289434a5a801be1d63bfc85b621579e06452f0.ts} +2 -1
  41. package/test/await/await.test.ts +2 -4
  42. package/test/await/components/output/Await-client.ts +4 -4
  43. package/test/await/components/temp/{Await-client-6ce3ac610ce79ae3b474c81964b715e6f3d966da.ts → Await-client-a7bf000bf2b59a3d5251a0fa18ec79890a52a547.ts} +4 -4
  44. package/test/bench/bench.bench.ts +5 -7
  45. package/test/bench/components/output/Bench-client.ts +208 -0
  46. package/test/bench/components/output/Bench-server.ts +130 -0
  47. package/test/bench/components/temp/{Bench-client-5fee290225ae4771aa847360006123671e8f17f1.ts → Bench-client-fca94b485c6263778751927240ab3ca4048d7a10.ts} +10 -10
  48. package/test/bind/bind.test.ts +3 -5
  49. package/test/bind/component.test.ts +45 -0
  50. package/test/bind/components/BindComponent.torp +29 -0
  51. package/test/bind/components/BindText.torp +2 -2
  52. package/test/bind/components/output/BindComponent-client.ts +87 -0
  53. package/test/bind/components/output/BindComponent-server.ts +52 -0
  54. package/test/bind/components/output/BindText-client.ts +3 -3
  55. package/test/bind/components/temp/BindComponent-client-a645474748485a878871c062ad69e6bcf35e6550.ts +87 -0
  56. package/test/bind/components/temp/BindComponent-server-c557e7aa944517ab87415b8bb35b41b0034254e7.ts +52 -0
  57. package/test/bind/components/temp/{BindText-client-9e2f542e13bcbf9a44eab2c5a79d67ddb128f928.ts → BindText-client-d5f7d721df55020ef337f660e5d278eb80fcc330.ts} +3 -3
  58. package/test/build-server/build-server-element-node.test.ts +1 -1
  59. package/test/buildOutputFiles.ts +6 -3
  60. package/test/child-component/components/output/ParentChild-client.ts +3 -2
  61. package/test/child-component/components/output/ParentChild-server.ts +1 -0
  62. package/test/child-component/components/temp/{ParentChild-client-8f00a3a31a7f666990765fc4755b62a39c855226.ts → ParentChild-client-eaefb0cbfd251badab3c57ae2037d973f785a1b8.ts} +3 -2
  63. package/test/child-component/components/temp/{ParentChild-server-9c791bdfa521c3f6549857a34a6cd414fa3c9e80.ts → ParentChild-server-8f2fc556f87867e6e3393e2e9e6d696cda107b69.ts} +1 -0
  64. package/test/class/components/output/Class-client.ts +7 -6
  65. package/test/class/components/output/Class-server.ts +1 -0
  66. package/test/class/components/temp/{Class-client-8bf02b6a0c85674d341617dfb755b285b1af89cf.ts → Class-client-dc885e1e391f38973bd9ea25b7475b07f7c23960.ts} +7 -6
  67. package/test/class/components/temp/{Class-server-dd71b6ec62f40231917a912819520fc614f017ae.ts → Class-server-d1a2463ba432e10c6f68bd59d1d31949c5a2044f.ts} +1 -0
  68. package/test/component/components/output/Component-client.ts +3 -2
  69. package/test/component/components/output/Component-server.ts +1 -1
  70. package/test/component/components/output/Header-client.ts +2 -1
  71. package/test/component/components/output/Header-server.ts +1 -0
  72. package/test/component/components/temp/{Component-client-32cd481f03e25d10b8e93947a2ff8773135b4975.ts → Component-client-a2fb6f30fc340bac909b64cb4f5ac68cde376de3.ts} +3 -2
  73. package/test/component/components/temp/{Component-server-11542f55405218c0b471172cac1d6a1a4edb5c09.ts → Component-server-40be12cd528ccf768f1ad40ef2a435ad2b2a2d71.ts} +1 -1
  74. package/test/component/components/temp/{Header-client-ac151eea86e3f6b058fab101661e13ae73864968.ts → Header-client-75cb4aa868295ac168c16ba7b275fbb5ab9256f9.ts} +2 -1
  75. package/test/component/components/temp/{Header-server-4f10f2332f3ca7a097517cfb938ab1e6f31d68ac.ts → Header-server-f73307874bec34a95dcd152d7f83a3ad13a0f4ac.ts} +1 -0
  76. package/test/effects/components/output/For-client.ts +3 -2
  77. package/test/effects/components/output/For-server.ts +1 -0
  78. package/test/effects/components/output/If-client.ts +4 -3
  79. package/test/effects/components/output/If-server.ts +1 -0
  80. package/test/effects/components/output/NestedIf-client.ts +4 -3
  81. package/test/effects/components/output/NestedIf-server.ts +1 -0
  82. package/test/effects/components/temp/{For-client-887b1758cf5c80f64708a47369e0e6ce44ffa20f.ts → For-client-d8d4e4424ec3238fe9f79cc907d673612b63bcdf.ts} +3 -2
  83. package/test/effects/components/temp/{For-server-8e1c7393b102c870167dd086cbee45c47e614dfa.ts → For-server-1f853c161bea31a1a5b11b00d7a021b70c778066.ts} +1 -0
  84. package/test/effects/components/temp/{If-client-bfc548ef699b91f33e604f066211ce6269cc9506.ts → If-client-bb06988422395321bf298a75bf5563c6f750f776.ts} +4 -3
  85. package/test/effects/components/temp/{If-server-8c213b1a82cd0a5c2bccfeb90ba8bf01fa4672d2.ts → If-server-97e3cbe059306fc607985dcd1b187df3a278d979.ts} +1 -0
  86. package/test/effects/components/temp/{NestedIf-client-9e9155398f240345af392194063adc6e42dda80c.ts → NestedIf-client-dc880c1ec92e716ec730dfa0bac0e3d9265bdf27.ts} +4 -3
  87. package/test/effects/components/temp/{NestedIf-server-5f11d075c2ff224ee5feb7f61aa41aea8af44313.ts → NestedIf-server-9f199a33bae5d5fe34920d6a419937537a2b18dc.ts} +1 -0
  88. package/test/events/components/output/Increment-client.ts +2 -2
  89. package/test/events/components/temp/{Increment-client-35c275d15e94e4aa7625af12c45210521995ead5.ts → Increment-client-41bd7bc91aac8c7a9bf73527c8f7ba1794b09ab3.ts} +2 -2
  90. package/test/events/events.test.ts +2 -3
  91. package/test/for/components/ForEscape.torp +1 -1
  92. package/test/for/components/output/For-client.ts +1 -1
  93. package/test/for/components/output/ForEscape-client.ts +4 -4
  94. package/test/for/components/output/ForIn-client.ts +3 -2
  95. package/test/for/components/output/ForIn-server.ts +1 -0
  96. package/test/for/components/output/ForOf-client.ts +3 -2
  97. package/test/for/components/output/ForOf-server.ts +1 -0
  98. package/test/for/components/temp/{For-client-1f6e475347f0474375f12a7c24b0b293e3942aa7.ts → For-client-0be5994e98c07a3455cd8a18a2b1b8ef3605aba6.ts} +1 -1
  99. package/test/for/components/temp/{ForEscape-client-80d3078cf15001f2ab37b0ec63cfce7267a8a166.ts → ForEscape-client-a973a145e28b32ca55fe3dca53d885fcf5e11c37.ts} +4 -4
  100. package/test/for/components/temp/{ForIn-client-c8a1ec4473d05475e11928d06d081ab671fd9f28.ts → ForIn-client-5cc25a98e10826e8477fe6f931d5d2bbd0b13c9f.ts} +3 -2
  101. package/test/for/components/temp/{ForIn-server-d92cf8d3c3e300b562308607caca2c9cab3cb30b.ts → ForIn-server-d68e82c048f7e0f1525f74724b91e5cd01cdc6a6.ts} +1 -0
  102. package/test/for/components/temp/{ForOf-client-069881d4d9e52bc091e7213154924f3e1626174a.ts → ForOf-client-70b0d59ff72f1cffe2e0ac8523dd7ccdca4ffe39.ts} +3 -2
  103. package/test/for/components/temp/{ForOf-server-3bfbb33bd589890d272bbcbea53d9598f53bfd70.ts → ForOf-server-ac08d98dd249a3e64a366b8488450e6ce0d18def.ts} +1 -0
  104. package/test/for/for-escape.test.ts +2 -3
  105. package/test/function/components/output/Function-client.ts +1 -1
  106. package/test/function/components/temp/{Function-client-b8931e51769f3f1e84d1491c6474f8551acf9f8d.ts → Function-client-b3ff029a821d47e1a2249a52dfb681f4b2e96232.ts} +1 -1
  107. package/test/function/function.test.ts +1 -2
  108. package/test/html/components/output/Html-client.ts +3 -1
  109. package/test/html/components/output/Html-server.ts +1 -0
  110. package/test/html/components/temp/{Html-client-5fc745d9980a42a70dcb3473b0fd976e69882c9e.ts → Html-client-766ba4a63f28f929ca16cd7d1640fe92f9b30792.ts} +3 -1
  111. package/test/html/components/temp/{Html-server-437ea390e58947213ede74755a012cdacceeebe7.ts → Html-server-0a41da8b81bf833f331f3becdbac14be7ddc6c8c.ts} +1 -0
  112. package/test/if/components/output/IfAfterIf-client.ts +5 -4
  113. package/test/if/components/output/IfAfterIf-server.ts +1 -0
  114. package/test/if/components/output/IfElse-client.ts +4 -3
  115. package/test/if/components/output/IfElse-server.ts +1 -0
  116. package/test/if/components/output/IfElseIf-client.ts +5 -4
  117. package/test/if/components/output/IfElseIf-server.ts +1 -0
  118. package/test/if/components/output/IfNested-client.ts +5 -4
  119. package/test/if/components/output/IfNested-server.ts +1 -0
  120. package/test/if/components/temp/{IfAfterIf-client-a489daa5b961ed9da71418fdbb1665b6ba39f1b7.ts → IfAfterIf-client-49b6b8c42b0a351561f9f5f22c2ae2e844bf3b58.ts} +5 -4
  121. package/test/if/components/temp/{IfAfterIf-server-243736e666e7d77d3f8553a1682734555f5ffd22.ts → IfAfterIf-server-eb67cf1fcf6711882266219940a47bd4a66497f3.ts} +1 -0
  122. package/test/if/components/temp/{IfElse-client-4c71642f412d5dd2f45729eafb7e974781dbe011.ts → IfElse-client-e236d4a2e99262ff09675bc519c07e2826c0d70b.ts} +4 -3
  123. package/test/if/components/temp/{IfElse-server-64c055182b327504ee71d1f1d44cfccf48be350d.ts → IfElse-server-c077e0e30f4d2adec41ab779198362d21542d055.ts} +1 -0
  124. package/test/if/components/temp/{IfElseIf-client-98d50d6f40c2385c4ef4fc02329f9d2e182c76c4.ts → IfElseIf-client-b92106861f0c7ec3d09e3a42bb93d8f5bd4e3bdf.ts} +5 -4
  125. package/test/if/components/temp/{IfElseIf-server-79788b13af905175f50bfa8658f2ff2af73db232.ts → IfElseIf-server-67991eee3f56dc687cda600511c31396438e0a77.ts} +1 -0
  126. package/test/{build-server/components/temp/IfNested-client-979db97eea5b4595550b9b46f297f3fbc31796d2.ts → if/components/temp/IfNested-client-f563ac1b16a2280e457557281c4925ed447a9cd7.ts} +5 -4
  127. package/test/{build-server/components/temp/IfNested-server-3c4e4b6f2162c0906cef685830219b9ea9ee985c.ts → if/components/temp/IfNested-server-0a2a9643324ab73b02b310cad61ba87dab146ab2.ts} +1 -0
  128. package/test/mount/components/Mount.torp +1 -1
  129. package/test/mount/components/output/Mount-client.ts +0 -2
  130. package/test/mount/components/output/Mount-server.ts +1 -1
  131. package/test/mount/components/temp/{Mount-client-49d8d6475336b5749fe8a17be847fd2b6cda5ccc.ts → Mount-client-36dbc95a61345a1b7dcf07e6b0c998af2afca691.ts} +0 -2
  132. package/test/mount/components/temp/{Mount-server-e3d689cf52a4477da9aa00e2c7604110710db991.ts → Mount-server-8bef1924a731f3db63cb827be3938fec685e53af.ts} +1 -1
  133. package/test/on-mount/components/output/OnMount-client.ts +0 -2
  134. package/test/on-mount/components/temp/{OnMount-client-b2dfd41a0aaeafb3b7af14793a03e0fc3bc43aaa.ts → OnMount-client-21708a638d2939ee8b017f5dc819cee4748bde87.ts} +0 -2
  135. package/test/party/13-emit-to-parent.test.ts +2 -3
  136. package/test/party/16-context.test.ts +1 -2
  137. package/test/party/17-input-text.test.ts +2 -3
  138. package/test/party/18-input-checkbox.test.ts +1 -2
  139. package/test/party/19-input-radio.test.ts +2 -3
  140. package/test/party/20-input-select.test.ts +3 -4
  141. package/test/party/7-event-click.test.ts +1 -3
  142. package/test/party/9-conditional.test.ts +2 -4
  143. package/test/party/components/ColorSelect.torp +1 -1
  144. package/test/party/components/InputFocused.torp +1 -1
  145. package/test/party/components/InputHello.torp +1 -1
  146. package/test/party/components/IsAvailable.torp +1 -1
  147. package/test/party/components/PickPill.torp +2 -2
  148. package/test/party/components/output/AnswerButton-client.ts +3 -2
  149. package/test/party/components/output/AnswerButton-server.ts +1 -0
  150. package/test/party/components/output/AnswerButtonApp-client.ts +4 -4
  151. package/test/party/components/output/AnswerButtonApp-server.ts +1 -1
  152. package/test/party/components/output/ColorSelect-client.ts +1 -1
  153. package/test/party/components/output/Colors-client.ts +1 -1
  154. package/test/party/components/output/Counter-client.ts +1 -1
  155. package/test/party/components/output/FunnyButton-client.ts +1 -1
  156. package/test/party/components/output/FunnyButtonApp-client.ts +2 -2
  157. package/test/party/components/output/FunnyButtonApp-server.ts +1 -1
  158. package/test/party/components/output/InputFocused-client.ts +0 -2
  159. package/test/party/components/output/InputFocused-server.ts +1 -1
  160. package/test/party/components/output/InputHello-client.ts +1 -1
  161. package/test/party/components/output/IsAvailable-client.ts +1 -1
  162. package/test/party/components/output/PickPill-client.ts +2 -2
  163. package/test/party/components/output/TrafficLight-client.ts +6 -6
  164. package/test/party/components/output/UserProfile-client.ts +5 -4
  165. package/test/party/components/output/UserProfile-server.ts +1 -0
  166. package/test/party/components/output/UserProfileApp-client.ts +3 -2
  167. package/test/party/components/output/UserProfileApp-server.ts +1 -1
  168. package/test/party/components/output/UserProfileContext-client.ts +3 -3
  169. package/test/party/components/output/UserProfileContextApp-client.ts +2 -2
  170. package/test/party/components/output/UserProfileContextApp-server.ts +1 -1
  171. package/test/party/components/temp/{AnswerButton-client-243454cd7bf84c648f244b3587e60d01a0401039.ts → AnswerButton-client-c3dc7edbfff2db1ea111086002147d771ee12193.ts} +3 -2
  172. package/test/party/components/temp/{AnswerButton-server-938e46c1d2510ea37454854ef527e4aa64eb8e52.ts → AnswerButton-server-427442fa8cfc0a18817c2d37f9cb5cbcb3a88a05.ts} +1 -0
  173. package/test/party/components/temp/{AnswerButtonApp-client-34311c2e7412807867fb6267af31a341f092bf31.ts → AnswerButtonApp-client-3e0b63a6bbc1dee3e6cb715c0297cc2ce58d3e4e.ts} +4 -4
  174. package/test/party/components/temp/{AnswerButtonApp-server-3f5071775abea24ee8830830072c6fe465fc4e56.ts → AnswerButtonApp-server-bbc8bb739d2060ccda77ce406aa6b15d8f90b91c.ts} +1 -1
  175. package/test/party/components/temp/{ColorSelect-client-f9fb9fa09f4729da3226d5aaf30e84cd3cd705b5.ts → ColorSelect-client-5ffa6f9d77de967993088c9a01b9f976f3333176.ts} +1 -1
  176. package/test/party/components/temp/{Colors-client-f124162d1dd8f0c3d37bf96c8e5f18599ae3a682.ts → Colors-client-3dafffc5c2a72037f4419fc45d08963872a29958.ts} +1 -1
  177. package/test/party/components/temp/{Counter-client-c349710110b4afbf9593f20cfc40c162ed836079.ts → Counter-client-4d553521fcfc6c9a4e05dce89e70b2ecf8785c4e.ts} +1 -1
  178. package/test/party/components/temp/{FunnyButton-client-2e6bec8259065b5bc9ae16d1ca93dd1bd0c56b7c.ts → FunnyButton-client-45f2c5dd56f2cdb80102193a978df6b0cec822ab.ts} +1 -1
  179. package/test/party/components/temp/{FunnyButtonApp-client-4ed0ebf5be13ddb62d9942bac7f4b8a885ec6c50.ts → FunnyButtonApp-client-ac7101af922dc6ba472b75a428f340caca6be74a.ts} +2 -2
  180. package/test/party/components/temp/{FunnyButtonApp-server-9748c9096848e643c91237c46ea09d23f1b3fa7c.ts → FunnyButtonApp-server-f1a98a946e0bb9d1bb76b01e26d6ead8f55d32bb.ts} +1 -1
  181. package/test/party/components/temp/{InputFocused-client-a543f82c8db205fb3e33c62de699cff43c668a68.ts → InputFocused-client-11a9cb08909b26945bbc2451ab4c1dac09e6d113.ts} +0 -2
  182. package/test/party/components/temp/{InputFocused-server-222f2fa7029d1cd6941a3777293a9b2d37b7295e.ts → InputFocused-server-82be1a63c80d1dd3f06efaf0bef349d58cf1d9af.ts} +1 -1
  183. package/test/party/components/temp/{InputHello-client-9953499de5778bb1d8aac7691129210a6098db07.ts → InputHello-client-b0453caeae0d43dc1878039c6b49318d12624d48.ts} +1 -1
  184. package/test/party/components/temp/{IsAvailable-client-929ed6c872e72b6c5243b882c6a9a0d13f6a7c47.ts → IsAvailable-client-d789346a68b26fd3f5d9f7aebc4c7bd181c47cad.ts} +1 -1
  185. package/test/party/components/temp/{PickPill-client-f9a50a75ce63ec2ee3ac34e378e20602b93629f7.ts → PickPill-client-b47d371ae5f41d87c96b3cdeed0a8bc06fbfa9c7.ts} +2 -2
  186. package/test/party/components/temp/{TrafficLight-client-81dd9bd9f3332533e01f6c9b271c15e5f76e1fdb.ts → TrafficLight-client-549f9fe3418c113fb240cfa17e09c42e1e7f346a.ts} +6 -6
  187. package/test/party/components/temp/{UserProfile-client-938bcef20a2555de22aca939022a32742ac5e729.ts → UserProfile-client-2c13aac1ad915e51dc557f9af765669e636c0fea.ts} +5 -4
  188. package/test/party/components/temp/{UserProfile-server-9df5c59b8797fa67c479bc76450104d45e6076f9.ts → UserProfile-server-cc1162a4297f6163bfb86e7a8aeea08e8c0625c6.ts} +1 -0
  189. package/test/party/components/temp/{UserProfileApp-client-af27173415d65647cab0f30a9a4967cef0307e25.ts → UserProfileApp-client-2be7a11319cca4d7bfa0f3d2205c441d7e203780.ts} +3 -2
  190. package/test/party/components/temp/{UserProfileApp-server-6a7dbe68ee8041785cef50fb15a9e5ba475e120f.ts → UserProfileApp-server-ab9ff66f787f775aa8571314fee8bcbef85489ef.ts} +1 -1
  191. package/test/party/components/temp/{UserProfileContext-client-93211cdba795b8baf926f60f5b17da90701743b3.ts → UserProfileContext-client-2aa36f436a5f20614d77f48963ec5ca442eebd90.ts} +3 -3
  192. package/test/party/components/temp/{UserProfileContextApp-client-7ea7d90a35894408757aef3e935d3a8badf93aea.ts → UserProfileContextApp-client-5099052fdcd8fb4c368c45090390947424a4e9b0.ts} +2 -2
  193. package/test/party/components/temp/{UserProfileContextApp-server-90290c770529d7211eabf29e7d0fe868aa856b52.ts → UserProfileContextApp-server-6be8dab2ed4f8911f26bef137e7dea164f2b894d.ts} +1 -1
  194. package/test/props/components/Reactive.torp +18 -0
  195. package/test/props/components/output/Reactive-client.ts +70 -0
  196. package/test/props/components/output/Reactive-server.ts +40 -0
  197. package/test/props/components/temp/Reactive-client-e3e5f6ee2f22c46337c6ab1df2f241133c7e2646.ts +70 -0
  198. package/test/props/components/temp/Reactive-server-0914d91287a1bc6d32d7cc56d086624762dbfc04.ts +40 -0
  199. package/test/props/reactive.test.ts +40 -0
  200. package/test/replace/components/output/Replace-client.ts +3 -2
  201. package/test/replace/components/output/Replace-server.ts +1 -0
  202. package/test/replace/components/temp/{Replace-client-ea4ddc73c71181fb6ef3c6f5bf61b0b9e017f00a.ts → Replace-client-3fdab63871fa4bb792317caefd3539b1888325b1.ts} +3 -2
  203. package/test/replace/components/temp/{Replace-server-30eb1ad796516fa4d310798a9c8a6811ec13cbd9.ts → Replace-server-857aca7a58deffc3a1c95ab9d31628b4051ca203.ts} +1 -0
  204. package/test/slots/components/output/Article-client.ts +2 -2
  205. package/test/slots/components/output/Basic-client.ts +1 -1
  206. package/test/slots/components/output/Basic-server.ts +1 -1
  207. package/test/slots/components/output/Let-client.ts +4 -3
  208. package/test/slots/components/output/Let-server.ts +2 -1
  209. package/test/slots/components/output/List-client.ts +1 -1
  210. package/test/slots/components/output/List-server.ts +1 -0
  211. package/test/slots/components/output/Named-client.ts +2 -2
  212. package/test/slots/components/output/Named-server.ts +1 -1
  213. package/test/slots/components/output/Unused-client.ts +1 -1
  214. package/test/slots/components/output/Unused-server.ts +1 -1
  215. package/test/slots/components/temp/{Article-client-741f94eab2e96655f4c9d69ccd7d9c7cc998f4ce.ts → Article-client-80848d6cadd8abac61865d14ddd35685844797e0.ts} +2 -2
  216. package/test/slots/components/temp/{Basic-client-c82bc3c5e10f2f2c1869347435262c8f3039e32f.ts → Basic-client-97b18686412869e8c814fd4542dfe826b1f7669f.ts} +1 -1
  217. package/test/slots/components/temp/{Basic-server-21fb4f8caa3bbcae219aaef388af8c26c46fa5ce.ts → Basic-server-248392e2cd147b537cd44e9a5a71885ef20102e4.ts} +1 -1
  218. package/test/slots/components/temp/{Let-client-c54696fd1b5b859a693744722957fa960f72fad6.ts → Let-client-be69c1f5aa32faf16239050d7836db46cfdcba2f.ts} +4 -3
  219. package/test/slots/components/temp/{Let-server-4d78ffc8741fa737f2cd915b36c0e6ec88a9146f.ts → Let-server-c3293219ed27aec61a6af36d451ddf6609c40027.ts} +2 -1
  220. package/test/slots/components/temp/{List-client-0659d999cfb48b865fe5b8aaaf2a4ddac3a8a8b6.ts → List-client-a829b33214de829de689a72d2e8703b2266ad7a4.ts} +1 -1
  221. package/test/slots/components/temp/{List-server-4b76485a259d054187edad8aa691d80ac918ad86.ts → List-server-06503834c9bfae9b8c2b61742568b020a3de961c.ts} +1 -0
  222. package/test/slots/components/temp/{Named-client-0626b2f0911f77f5edcfda82f9751bcbc37b7641.ts → Named-client-2d9d88f5fdf74d81c3ed1d769a476f4ac36c8b99.ts} +2 -2
  223. package/test/slots/components/temp/{Named-server-e802f901223d697c7fd0a1a234ce1930af1b4b3f.ts → Named-server-65a7bbf038111475c8f07dccb34040ab8c1392c4.ts} +1 -1
  224. package/test/slots/components/temp/{Unused-client-aa05465731e0d00e18189498f49c94f991e76f10.ts → Unused-client-1b16491cb800bbbbd871d9fbf5966c7275244019.ts} +1 -1
  225. package/test/slots/components/temp/{Unused-server-00b92d14665a565f0042b1f56231f28bc38a1252.ts → Unused-server-e5d1e85116bebc7e1a2882cb8f2c9ae7975a1de7.ts} +1 -1
  226. package/test/special-component/components/output/Component-client.ts +5 -4
  227. package/test/special-component/components/output/Component-server.ts +3 -2
  228. package/test/special-component/components/temp/{Component-client-3cc84b23def8830b8ef441bde0f03b3e9718196a.ts → Component-client-22f7e1d0955fb3d90939e956a1f0f77118c259b6.ts} +5 -4
  229. package/test/special-component/components/temp/{Component-server-5dc597784ea1fe45ac2cfb21305cf2187a3868e8.ts → Component-server-ce596cd84dc11f67c9f0588060a5b672eddf9715.ts} +3 -2
  230. package/test/special-element/components/output/Element-client.ts +2 -1
  231. package/test/special-element/components/output/Element-server.ts +1 -0
  232. package/test/special-element/components/temp/{Element-client-0ab8992a8e914c7e190cddac790585fef9be1c9f.ts → Element-client-21c92c04579b1d29e8813265a10897d069aad5c5.ts} +2 -1
  233. package/test/special-element/components/temp/{Element-server-ddb0170df4a3f369577ca7e9768e5fe799f14dfb.ts → Element-server-4bed9653529f39e72754126147fe8618f328853a.ts} +1 -0
  234. package/test/special-self/components/output/Self-client.ts +3 -2
  235. package/test/special-self/components/output/Self-server.ts +1 -0
  236. package/test/special-self/components/temp/{Self-client-3982f174954034643c4d17cfc66d7f10bf1d4df6.ts → Self-client-911c67194be1fdd997537310bcafe712161e8251.ts} +3 -2
  237. package/test/special-self/components/temp/{Self-server-97ffbdaae47f4463034e1d318a8073d078ff138f.ts → Self-server-49ba9be1f6255dd4daf490c640f5222a85960218.ts} +1 -0
  238. package/test/style/components/output/Style-client.ts +2 -1
  239. package/test/style/components/output/Style-server.ts +1 -0
  240. package/test/style/components/temp/{Style-client-085517e82bdd015c86f4c2577447c6a2791cf656.ts → Style-client-cbfac1e255c33a867ccbd5f9d1484a855d91a2d7.ts} +2 -1
  241. package/test/style/components/temp/{Style-server-b2615163fbafdcb2a5d4155120edf2f66ef3bba6.ts → Style-server-763d82ee2296be3f7620814aed950b505329c04c.ts} +1 -0
  242. package/test/svg/components/output/Shape-client.ts +4 -3
  243. package/test/svg/components/output/Shape-server.ts +1 -0
  244. package/test/svg/components/temp/{Shape-client-ea698ed1b7d0c3615adff35fa4956f22840f5fda.ts → Shape-client-cb98b36ab2a0d9f06b484a3c988fb43c3a71a4a0.ts} +4 -3
  245. package/test/svg/components/temp/{Shape-server-eb32049cfb7c058b2c4079c5f86ef00651ff6d69.ts → Shape-server-500d7fe4556026234ce5379b8e90595ddd094edc.ts} +1 -0
  246. package/test/switch/components/output/Switch-client.ts +5 -4
  247. package/test/switch/components/output/Switch-server.ts +1 -0
  248. package/test/switch/components/temp/{Switch-client-4c4adac7308795807517e66bc268a38f87df6bda.ts → Switch-client-9980de45e3346b79525c445edec7cbc3b375e8a4.ts} +5 -4
  249. package/test/switch/components/temp/{Switch-server-21fcac2b02c6071136bb0dc394babd27e454d2c9.ts → Switch-server-1c5690726f28008bb565966af69b6e4e019dea82.ts} +1 -0
  250. package/test/text/components/output/Text-client.ts +3 -2
  251. package/test/text/components/output/Text-server.ts +1 -0
  252. package/test/text/components/temp/{Text-client-9840092be1b4552ddce6e5efc2f1b395637a0a3d.ts → Text-client-63f1d3bf9b3bd145c7ce7058716640e27144055e.ts} +3 -2
  253. package/test/text/components/temp/{Text-server-6774aa9d4ccce941224ba0a8fb1d3e42eb31807e.ts → Text-server-9d3dd6f51e893e63c1999ceafce19ca1697d76ea.ts} +1 -0
  254. package/test/watch-array/components/output/Array-client.ts +4 -3
  255. package/test/watch-array/components/output/Array-server.ts +1 -0
  256. package/test/watch-array/components/output/ArrayEntries-client.ts +4 -3
  257. package/test/watch-array/components/output/ArrayEntries-server.ts +1 -0
  258. package/test/watch-array/components/output/ArrayIndexes-client.ts +4 -3
  259. package/test/watch-array/components/output/ArrayIndexes-server.ts +1 -0
  260. package/test/watch-array/components/output/ArrayUnkeyed-client.ts +4 -3
  261. package/test/watch-array/components/output/ArrayUnkeyed-server.ts +1 -0
  262. package/test/watch-array/components/temp/{Array-client-b29a42f88c2a2f37b3a86bdb9535e20a487887e1.ts → Array-client-ffd82188a45877d74053c82fb3616e4aed0fc2ad.ts} +4 -3
  263. package/test/watch-array/components/temp/{Array-server-ced54fc7e63196f9c24c2e55b27bfe6cf7821dcb.ts → Array-server-3715e815af887e8487b214e7de324850fe99e312.ts} +1 -0
  264. package/test/watch-array/components/temp/{ArrayEntries-client-6289890758b1f6b462a74417e0b9e2e46d946ccd.ts → ArrayEntries-client-b6e982660628576061dbb44539e970b9549bdcfa.ts} +4 -3
  265. package/test/watch-array/components/temp/{ArrayEntries-server-0429a18ffbe39afe87612889a3010c3fcaa601dc.ts → ArrayEntries-server-85cbe80fbb7f6885287825e2cc0dfc1cb9835d79.ts} +1 -0
  266. package/test/watch-array/components/temp/{ArrayIndexes-client-fe4b1c49f442b3e5411f331c52ae6c5ab38f2b2d.ts → ArrayIndexes-client-39337aa43bd8c10a53db836c3be233d48dd39b91.ts} +4 -3
  267. package/test/watch-array/components/temp/{ArrayIndexes-server-20c01f10832f035b007f85a423778934d51a703a.ts → ArrayIndexes-server-ec623c724af0c7212675d61ea68e9dee87f96caa.ts} +1 -0
  268. package/test/watch-array/components/temp/{ArrayUnkeyed-client-3fffe2ce749ee84e0730130bfd730d432e3c4e12.ts → ArrayUnkeyed-client-7606f9a6a221c8cf6a8b51e4895d10a3d7bccc11.ts} +4 -3
  269. package/test/watch-array/components/temp/{ArrayUnkeyed-server-0ab804a5e7bf5b47594d85adab79735e89a3939d.ts → ArrayUnkeyed-server-db21cd576a4c70973abe10f6c8ef5cecb3ead86c.ts} +1 -0
  270. package/test/watch-object/components/output/Watched-client.ts +2 -1
  271. package/test/watch-object/components/output/Watched-server.ts +1 -0
  272. package/test/watch-object/components/temp/{Watched-client-cb293038cd0a39043d05639c624a17d512419c25.ts → Watched-client-bbffbf2cede1a5c071b770670eb39c0f0be725af.ts} +2 -1
  273. package/test/watch-object/components/temp/{Watched-server-4884585cf4f50493a70631909f2230153531208e.ts → Watched-server-0252addaf92fb0f0c1e2937f42918a1c9f31d196.ts} +1 -0
  274. package/src/render/flushMountEffects.ts +0 -8
  275. package/test/if/components/temp/IfNested-client-979db97eea5b4595550b9b46f297f3fbc31796d2.ts +0 -90
  276. package/test/if/components/temp/IfNested-server-3c4e4b6f2162c0906cef685830219b9ea9ee985c.ts +0 -30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torpor/view",
3
- "version": "0.0.14",
3
+ "version": "0.1.1",
4
4
  "description": "Torpor's view library, for writing and mounting components",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -35,15 +35,15 @@
35
35
  "@testing-library/user-event": "^14.6.1",
36
36
  "@trivago/prettier-plugin-sort-imports": "^5.2.2",
37
37
  "@types/jsdom": "^21.1.7",
38
- "@types/node": "^22.15.17",
38
+ "@types/node": "^22.15.29",
39
39
  "fast-glob": "^3.3.3",
40
40
  "jsdom": "^26.1.0",
41
41
  "sucrase": "^3.35.0",
42
42
  "tinybench": "^4.0.1",
43
- "tsup": "^8.4.0",
43
+ "tsup": "^8.5.0",
44
44
  "tsx": "^4.19.4",
45
45
  "typescript": "^5.8.3",
46
- "vitest": "^3.1.3"
46
+ "vitest": "^3.2.1"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsc --noEmit && tsup && tsx ./scripts/copyToRepl.ts",
@@ -27,7 +27,7 @@ async function run() {
27
27
  .replaceAll(/^import ".\/chunk-(.+?).js";/gms, "")
28
28
  .replaceAll(/\/\/# sourceMappingURL=(.+?).js.map/g, "");
29
29
 
30
- const destFile = "../../site/src/components/repl/view.txt";
30
+ const destFile = "../../site/src/views/repl/view.txt";
31
31
  await fs.writeFile(destFile, source);
32
32
  }
33
33
 
@@ -10,7 +10,6 @@ const importsMap: Record<string, string> = {
10
10
  $unwrap: 'import { $unwrap } from "${folder}";',
11
11
  $run: 'import { $run } from "${folder}";',
12
12
  $mount: 'import { $mount } from "${folder}";',
13
- t_flush: 'import { t_flush } from "${folder}";',
14
13
  t_range: 'import { t_range } from "${folder}";',
15
14
  t_push_range: 'import { t_push_range } from "${folder}";',
16
15
  t_pop_range: 'import { t_pop_range } from "${folder}";',
@@ -96,7 +95,8 @@ function buildTemplate(template: Template, imports: Set<string>, b: Builder) {
96
95
 
97
96
  // Make sure we've got $props if we're going to be using it
98
97
  if (current.props?.length) {
99
- b.append(`$props ??= {};`);
98
+ imports.add("$watch");
99
+ b.append(`$props ??= $watch({});`);
100
100
  }
101
101
 
102
102
  // Redefine $context so that any newly added properties will only be passed to children
@@ -139,15 +139,6 @@ function buildTemplate(template: Template, imports: Set<string>, b: Builder) {
139
139
  currentIndex += 1;
140
140
  current = template.components[currentIndex];
141
141
 
142
- // TODO: Only if the component has $mount
143
- // Flush any $mount calls that were encountered -- move this to
144
- // addFragment, because we also have to flush events and animations
145
- if (imports.has("$mount")) {
146
- imports.add("t_flush");
147
- b.append("");
148
- b.append("t_flush();");
149
- }
150
-
151
142
  marker = i + "/* @end */".length;
152
143
  }
153
144
  }
@@ -25,17 +25,33 @@ export default function buildComponentNode(
25
25
  const componentHasProps = node.attributes.length || root;
26
26
  const propsName = componentHasProps ? nextVarName("props", status) : "undefined";
27
27
  if (componentHasProps) {
28
- // TODO: defaults etc props
29
- //status.imports.add("$watch");
30
- //b.append(`const ${propsName} = $watch({});`);
31
- b.append(`const ${propsName}: any = {};`);
28
+ // NOTE: The $props object is wrapped in a proxy so that components can
29
+ // be updated if they refer to a $props.property in e.g. an element's
30
+ // attribute
31
+ // TODO: default props etc
32
+ status.imports.add("$watch");
33
+ b.append(`const ${propsName}: any = $watch({});`);
34
+
32
35
  for (let { name, value } of node.attributes) {
33
36
  if (name === "self" && node.tagName === ":component") {
34
37
  // Ignore this special attribute
35
38
  } else if (name.startsWith("{") && name.endsWith("}")) {
36
39
  // It's a shortcut attribute
40
+ // It could be e.g. {width} or it could be {$state.width}, but
41
+ // in either case we set the value of the width property
37
42
  name = name.substring(1, name.length - 1);
38
- buildRun("setProp", `${propsName}["${name}"] = ${name};`, status, b);
43
+ const propName = name.split(".").at(-1);
44
+ buildRun("setProp", `${propsName}["${propName}"] = ${name};`, status, b);
45
+ } else if (name.startsWith("&") && value != null && isFullyReactive(value)) {
46
+ // It's a bound property
47
+ // Add two $runs -- one to update the props, and one to update the value
48
+ // The component author will need to make sure $props.x is updated
49
+ // TODO: Maybe allow adding a Bindable<T> type to the $props interface??
50
+ // e.g. Component($props: { text: Bindable<string> })
51
+ name = name.substring(1);
52
+ value = value.substring(1, value.length - 1);
53
+ buildRun("setProp", `${propsName}["${name}"] = ${value};`, status, b);
54
+ buildRun("setBinding", `${value} = ${propsName}["${name}"];`, status, b);
39
55
  } else if (value != null) {
40
56
  let fullyReactive = isFullyReactive(value);
41
57
  let partlyReactive = isReactive(value);
@@ -139,8 +139,11 @@ function buildHeadNode(node: ElementNode, status: BuildStatus, b: Builder) {
139
139
  for (let { name, value } of node.attributes) {
140
140
  if (name.startsWith("{") && name.endsWith("}")) {
141
141
  // It's a shortcut attribute
142
+ // It could be e.g. {width} or it could be {$state.width}, but
143
+ // in either case we set the value of the width property
142
144
  name = name.substring(1, name.length - 1);
143
- buildRun("setAttribute", `t_head_el.setAttribute("${name}", ${name});`, status, b);
145
+ const propName = name.split(".").at(-1);
146
+ buildRun("setAttribute", `t_head_el.setAttribute("${propName}", ${name});`, status, b);
144
147
  } else if (value != null && isReactive(value)) {
145
148
  // It's a reactive attribute
146
149
  if (isFullyReactive(value)) {
@@ -170,13 +173,30 @@ function buildElementAttributes(
170
173
  // TODO: Flatten this out
171
174
  // TODO: Add an error if any reactive attributes are used non-reactively
172
175
 
176
+ // Do &ref first, just in case any other attributes depend on it being set
177
+ // e.g. if you have a `style` attribute that depends on the element's size
178
+ const refAttribute = node.attributes.find((a) => a.name === "&ref");
179
+ if (refAttribute) {
180
+ let value = refAttribute.value;
181
+ if (value != null && isFullyReactive(value)) {
182
+ // Bind the DOM element to a user-defined variable
183
+ value = value.substring(1, value.length - 1);
184
+ b.append(`${value} = ${varName};`);
185
+ }
186
+ }
187
+
173
188
  for (let { name, value } of node.attributes) {
174
189
  if (name === "self" && node.tagName === ":element") {
175
190
  // Ignore this special attribute
191
+ } else if (name === "&ref") {
192
+ // Ignore this one, it should have been done already, above
176
193
  } else if (name.startsWith("{") && name.endsWith("}")) {
177
194
  // It's a shortcut attribute
195
+ // It could be e.g. {width} or it could be {$state.width}, but
196
+ // in either case we set the value of the width property
178
197
  name = name.substring(1, name.length - 1);
179
- buildRun("setAttribute", `${varName}.setAttribute("${name}", ${name});`, status, b);
198
+ const propName = name.split(".").at(-1);
199
+ buildRun("setAttribute", `${varName}.setAttribute("${propName}", ${name});`, status, b);
180
200
  } else if (value != null && isReactive(value)) {
181
201
  // It's a reactive attribute
182
202
  if (isFullyReactive(value)) {
@@ -185,17 +205,14 @@ function buildElementAttributes(
185
205
  value = `\`${trimQuotes(value).replaceAll("{", "${")}\``;
186
206
  }
187
207
 
188
- if (name === ":self") {
189
- // Bind the DOM element to a user-defined variable
190
- b.append(`${value} = ${varName};`);
191
- } else if (name === ":group") {
208
+ if (name === "&group") {
192
209
  buildBindGroupAttribute(node, varName, value, status, b);
193
- } else if (name === ":value" || name === ":checked") {
210
+ } else if (name === "&value" || name === "&checked") {
194
211
  buildBindAttribute(node, varName, name, value, status, b);
195
212
  } else if (name === ":onmount") {
196
- // The :onmount event is faked by us by creating a $run. This also
197
- // means that you can have unmount functionality by returning a
198
- // cleanup function
213
+ // The :onmount event is faked by us by creating a $mount. This
214
+ // also means that you can have unmount functionality by
215
+ // returning a cleanup function
199
216
  buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
200
217
  } else if (name.startsWith("on")) {
201
218
  buildEventAttribute(varName, name, value, status, b);
@@ -696,12 +696,38 @@ function getFragmentVarPath(
696
696
  if (name !== "?") {
697
697
  // Add the path, so that we can shorten subsequent paths e.g.
698
698
  // const div = t_next(t_child(root));
699
- // const p = t_next(t_next(t_next(t_child(root)))) => t_next(t_next(div))
699
+ // const p = t_next(t_next(t_next(t_child(root))))
700
+ // => const p = t_next(t_next(div))
700
701
  varPaths.set(varPath, name);
701
702
  // Add the name, so that we are always using the last declared name
702
703
  varPaths.set(name, name);
703
704
  }
704
705
 
706
+ // Shorten `t_next(t_next(x))` to `t_next(x, 2)`
707
+ while (varPath.includes("t_next(t_next(")) {
708
+ const match = varPath.match(/(t_next\(){2,}/)![0];
709
+ const count = match.length / "t_next(".length;
710
+ const pos = varPath.indexOf("t_next(t_next(");
711
+ const head = varPath.substring(0, pos);
712
+ let level = 0;
713
+ let end = 0;
714
+ for (let i = pos + match.length; i < varPath.length; i++) {
715
+ if (varPath[i] === "(") {
716
+ level++;
717
+ } else if (varPath[i] === ")") {
718
+ if (level === 0) {
719
+ end = i;
720
+ break;
721
+ } else {
722
+ level--;
723
+ }
724
+ }
725
+ }
726
+ const tail = varPath.substring(end + count);
727
+ const param = varPath.substring(pos + match.length, end);
728
+ varPath = `${head}t_next(${param}, ${count})${tail}`;
729
+ }
730
+
705
731
  return varPath;
706
732
  }
707
733
 
@@ -721,7 +747,17 @@ function getFragmentVarPathPart(
721
747
  for (let [i, child] of path.children.entries()) {
722
748
  if (i > 0) {
723
749
  status.imports.add("t_next");
750
+ //if (varPath.startsWith("t_next")) {
751
+ // const match = varPath.match(/t_next\(.+?, (\d+)\)/);
752
+ // if (match) {
753
+ // const count = parseInt(match[1]) + 1;
754
+ // varPath = varPath.replace(/t_next\((.+), \d+\)/, `t_next($1, ${count})`);
755
+ // } else {
756
+ // varPath = varPath.replace(/t_next\((.+)\)/, `t_next($1, 2)`);
757
+ // }
758
+ //} else {
724
759
  varPath = `t_next(${varPath})`;
760
+ //}
725
761
  }
726
762
 
727
763
  if (i === path.children.length - 1 && child.children.length) {
@@ -36,6 +36,7 @@ function buildHtmlBranch(
36
36
  ) {
37
37
  status.imports.add("t_run_branch");
38
38
 
39
+ b.append(`${node.statement};`);
39
40
  b.append(`t_run_branch(${rangeName}, -1, () => {`);
40
41
 
41
42
  const templateName = nextVarName("template", status);
@@ -27,8 +27,11 @@ export default function buildSlotNode(node: ElementNode, status: BuildStatus, b:
27
27
  for (let { name, value } of slotAttributes) {
28
28
  if (name.startsWith("{") && name.endsWith("}")) {
29
29
  // It's a shortcut attribute
30
+ // It could be e.g. {width} or it could be {$state.width}, but
31
+ // in either case we set the value of the width property
30
32
  name = name.substring(1, name.length - 1);
31
- buildRun("setProp", `${propsName}["${name}"] = ${name};`, status, b);
33
+ const propName = name.split(".").at(-1);
34
+ buildRun("setProp", `${propsName}["${propName}"] = ${name};`, status, b);
32
35
  } else if (value != null) {
33
36
  let fullyReactive = isFullyReactive(value);
34
37
  let partlyReactive = isReactive(value);
@@ -29,8 +29,11 @@ export default function buildServerComponentNode(
29
29
  for (let { name, value } of node.attributes) {
30
30
  if (name.startsWith("{") && name.endsWith("}")) {
31
31
  // It's a shortcut attribute
32
+ // It could be e.g. {width} or it could be {$state.width}, but
33
+ // in either case we set the value of the width property
32
34
  name = name.substring(1, name.length - 1);
33
- b.append(`${propsName}["${name}"] = ${name};`);
35
+ const propName = name.split(".").at(-1);
36
+ b.append(`${propsName}["${propName}"] = ${name};`);
34
37
  } else if (value != null) {
35
38
  let fullyReactive = isFullyReactive(value);
36
39
  let partlyReactive = isReactive(value);
@@ -63,14 +63,17 @@ function buildElementAttributes(node: ElementNode, status: BuildServerStatus) {
63
63
  );
64
64
  } else if (name.startsWith("{") && name.endsWith("}")) {
65
65
  // It's a shortcut attribute
66
+ // It could be e.g. {width} or it could be {$state.width}, but
67
+ // in either case we set the value of the width property
66
68
  name = name.substring(1, name.length - 1);
69
+ const propName = name.split(".").at(-1);
67
70
  status.imports.add("t_attr");
68
- attributes.push(`${name}="\${t_attr(${name})}"`);
71
+ attributes.push(`${propName}="\${t_attr(${name})}"`);
69
72
  } else if (value != null && isFullyReactive(value)) {
70
73
  // It's a reactive attribute
71
74
  value = value.substring(1, value.length - 1);
72
75
 
73
- if (name === ":self" || name === ":value" || name === ":checked" || name === ":group") {
76
+ if (name === "&ref" || name === "&value" || name === "&checked" || name === "&group") {
74
77
  let defaultValue = "";
75
78
  let typeAttribute = node.attributes.find((a) => a.name === "type");
76
79
  if (typeAttribute && typeAttribute.value) {
@@ -41,8 +41,11 @@ export default function buildServerSlotNode(
41
41
  for (let { name, value } of slotAttributes) {
42
42
  if (name.startsWith("{") && name.endsWith("}")) {
43
43
  // It's a shortcut attribute
44
+ // It could be e.g. {width} or it could be {$state.width}, but
45
+ // in either case we set the value of the width property
44
46
  name = name.substring(1, name.length - 1);
45
- b.append(`${propsName}["${name}"] = ${name};`);
47
+ const propName = name.split(".").at(-1);
48
+ b.append(`${propsName}["${propName}"] = ${name};`);
46
49
  } else if (value != null) {
47
50
  let fullyReactive = isFullyReactive(value);
48
51
  let partlyReactive = isReactive(value);
@@ -35,23 +35,31 @@ export default function parseCode(source: string): ParseResult {
35
35
  }
36
36
  } else if (accept("function", status)) {
37
37
  // If the function name starts with a capital, parse it as a component
38
- consumeSpace(status);
39
- if (/[A-Z]/.test(source[status.i])) {
40
- parseComponentStart(status);
38
+ if (consumeSpace(status)) {
39
+ if (/[A-Z]/.test(source[status.i])) {
40
+ parseComponentStart(status);
41
+ } else {
42
+ // Unconsume the last space
43
+ status.i--;
44
+ }
41
45
  }
42
46
  } else if (accept("const", status)) {
43
47
  // If it's a function and its name starts with a capital, parse it as a component
44
- consumeSpace(status);
45
- if (/[A-Z]/.test(source[status.i])) {
46
- const start = status.i;
47
- consumeAlphaNumeric(status);
48
- consumeSpace(status);
49
- if (accept("=", status)) {
48
+ if (consumeSpace(status)) {
49
+ if (/[A-Z]/.test(source[status.i])) {
50
+ const start = status.i;
51
+ consumeAlphaNumeric(status);
50
52
  consumeSpace(status);
51
- if (accept("(", status, false)) {
52
- status.i = start;
53
- parseComponentStart(status);
53
+ if (accept("=", status)) {
54
+ consumeSpace(status);
55
+ if (accept("(", status, false)) {
56
+ status.i = start;
57
+ parseComponentStart(status);
58
+ }
54
59
  }
60
+ } else {
61
+ // Unconsume the last space
62
+ status.i--;
55
63
  }
56
64
  }
57
65
  } else if (accept("export", status)) {
package/src/index.ts CHANGED
@@ -6,7 +6,6 @@ import t_animate from "./render/addAnimation";
6
6
  import t_event from "./render/addEvent";
7
7
  import t_add_fragment from "./render/addFragment";
8
8
  import t_apply_props from "./render/applyProps";
9
- import t_flush from "./render/flushMountEffects";
10
9
  import t_fmt from "./render/formatText";
11
10
  import t_class from "./render/getClasses";
12
11
  import t_fragment from "./render/getFragment";
@@ -44,7 +43,6 @@ export {
44
43
  t_event,
45
44
  t_add_fragment,
46
45
  t_apply_props,
47
- t_flush,
48
46
  t_fmt,
49
47
  t_class,
50
48
  t_fragment,
@@ -18,13 +18,13 @@ export default async function animate(
18
18
  // - store the animation
19
19
  // - reverse it if it's running and the range appears/disappears
20
20
  // - transfer the animation to the new element
21
-
22
21
  let animationOptions = Object.assign(
23
22
  {
24
23
  direction: enter ? "normal" : "reverse",
25
- // TODO: Should probably have a global setting for this somewhere
24
+ // TODO: Should probably have a global setting for these somewhere
26
25
  // And respect browser reduce motion settings
27
- duration: 1000,
26
+ duration: 300,
27
+ easing: "ease-in-out",
28
28
  fill: "forwards",
29
29
  },
30
30
  options,
@@ -32,9 +32,13 @@ export default async function animate(
32
32
 
33
33
  const animation = el.animate(keyframes, animationOptions);
34
34
 
35
- // HACK: I'm not entirely sure why this is needed, but without it,
36
- // animations never start
37
- animation.timeline = el.ownerDocument.timeline;
35
+ // HACK: I'm not entirely sure why we need to sent the animation's timeline,
36
+ // but without it, animations never start
37
+ // HACK: We also need to make sure the element has been added to the DOM
38
+ // with a timeout
39
+ setTimeout(() => {
40
+ animation.timeline = el.ownerDocument.timeline;
41
+ }, 1);
38
42
 
39
43
  // Add the animation to the active range, so that the range won't be cleared
40
44
  // until the animation is completed
@@ -6,5 +6,5 @@ import { proxyDataSymbol } from "../watch/symbols";
6
6
  * @param object The proxy object
7
7
  */
8
8
  export default function $unwrap<T extends Record<PropertyKey, any>>(object: T): T {
9
- return object[proxyDataSymbol].target || object;
9
+ return object && object[proxyDataSymbol] ? object[proxyDataSymbol].target || object : object;
10
10
  }
@@ -40,12 +40,20 @@ export default function addFragment(
40
40
  parent.insertBefore(fragment, before);
41
41
  }
42
42
 
43
- // If we're adding this fragment to the DOM, we can now add our stashed
44
- // events and play our stashed animations
43
+ // If we're adding this fragment to the DOM, we can now run any $mount
44
+ // effects, add our stashed events and play our stashed animations
45
45
  if (!parentIsFragment) {
46
- // Set the active range for each event and animation so it will get
47
- // attached to the right one and set it back afterwards
46
+ // Only run $mount effects if not hydrating (if hydrating, they will get
47
+ // run at the end when everything is hooked up)
48
+ if (!context.hydrationNode) {
49
+ for (let effect of context.mountEffects) {
50
+ $run(effect);
51
+ }
52
+ context.mountEffects.length = 0;
53
+ }
48
54
 
55
+ // Set the active range for each event so it will get attached to the
56
+ // right one and set it back afterwards
49
57
  for (let event of context.stashedEvents) {
50
58
  context.activeRange = event.range;
51
59
  $run(function addFragmentEvent() {
@@ -57,6 +65,8 @@ export default function addFragment(
57
65
  }
58
66
  context.stashedEvents.length = 0;
59
67
 
68
+ // Set the active range for each animation so it will get attached to
69
+ // the right one and set it back afterwards
60
70
  for (let animation of context.stashedAnimations) {
61
71
  context.activeRange = animation.range;
62
72
  $run(function addFragmentAnimation() {
@@ -1,5 +1,6 @@
1
1
  import { type Component } from "../types/Component";
2
2
  import { type SlotRender } from "../types/SlotRender";
3
+ import $run from "./$run";
3
4
  import context from "./context";
4
5
 
5
6
  /**
@@ -22,4 +23,10 @@ export default function hydrate(
22
23
  component(parent, null, props, undefined, slots);
23
24
 
24
25
  context.hydrationNode = null;
26
+
27
+ // Now that we've hydrated, we can run $mount effects
28
+ for (let effect of context.mountEffects) {
29
+ $run(effect);
30
+ }
31
+ context.mountEffects.length = 0;
25
32
  }
@@ -2,14 +2,23 @@ import context from "./context";
2
2
  import { HYDRATION_BRANCH } from "./hydrationMarkers";
3
3
  import isComment from "./isComment";
4
4
 
5
- // TODO: Should have t_next(node, 3) to get the sibling 3 nodes along
6
-
7
5
  /**
8
6
  * Gets the next sibling of a node
9
7
  */
10
- export default function nodeNext(node: Node): Node {
11
- let nextNode = node.nextSibling;
8
+ export default function nodeNext(node: Node, count?: number): Node {
9
+ let nextNode = checkHydrationNode(node.nextSibling);
12
10
 
11
+ if (count) {
12
+ for (let i = 1; i < count; i++) {
13
+ nextNode = checkHydrationNode(nextNode!.nextSibling);
14
+ }
15
+ }
16
+
17
+ // NOTE: We know nextNode is not null as it is being called from generated code
18
+ return nextNode!;
19
+ }
20
+
21
+ function checkHydrationNode(nextNode: ChildNode | null) {
13
22
  if (context.hydrationNode) {
14
23
  // Remove hydration comments that are inserted at the start of branches
15
24
  // They are just used to split up text nodes that would otherwise be joined in HTML
@@ -20,7 +29,5 @@ export default function nodeNext(node: Node): Node {
20
29
  }
21
30
  context.hydrationNode = nextNode;
22
31
  }
23
-
24
- // NOTE: We know this is not null as it is being called from generated code
25
- return nextNode!;
32
+ return nextNode;
26
33
  }
@@ -1,5 +1,6 @@
1
1
  import { type Range } from "../types/Range";
2
2
  import clearRange from "./clearRange";
3
+ import context from "./context";
3
4
  import newRange from "./newRange";
4
5
  import popRange from "./popRange";
5
6
  import pushRange from "./pushRange";
@@ -9,7 +10,6 @@ export default function runControlBranch(range: Range, index: number, create: ()
9
10
  if (index >= 0 && range.index === index) {
10
11
  return;
11
12
  }
12
-
13
13
  if (range.children?.length) {
14
14
  // Branching ranges have exactly one child
15
15
  clearRange(range.children[0]);
@@ -18,9 +18,19 @@ export default function runControlBranch(range: Range, index: number, create: ()
18
18
 
19
19
  const oldRange = pushRange(newRange(), true);
20
20
 
21
+ // Do NOT re-run the entire control for properties accessed while updating
22
+ // its branches. E.g. we want to re-run the control for `@if ($state.x > 5)`
23
+ // (in runControl) but not for `<span>{$state.text}</span>` (in
24
+ // runControlBranch)
25
+ const oldEffect = context.activeEffect;
26
+ context.activeEffect = null;
27
+
21
28
  // Run the create function
22
29
  create();
23
30
 
31
+ // Set the active effect back for the next branch
32
+ context.activeEffect = oldEffect;
33
+
24
34
  popRange(oldRange);
25
35
 
26
36
  // Set the index on the branching range
@@ -23,13 +23,19 @@ export default function runList(
23
23
 
24
24
  // Run the list in an effect
25
25
  $run(function runList() {
26
- // Push and pop the control statement on subsequent runs, so that new item
27
- // ranges will be added to its children
26
+ // Push and pop the control statement on subsequent runs, so that new
27
+ // item ranges will be added to its children
28
28
  const oldBranchRange = pushRange(range);
29
29
 
30
30
  // Build the array of items with keys and data
31
31
  const newItems = buildItems();
32
32
 
33
+ // Do NOT re-run the list for properties accessed while updating its
34
+ // items. E.g. we want to re-run the list for `@for (item of $items)`
35
+ // (in buildItems, above) but not for `<span>{item.id}</span>` (in
36
+ // runListItems, below)
37
+ context.activeEffect = null;
38
+
33
39
  // Run the function that updates the list's items
34
40
  runListItems(range, parent, anchor, listItems, newItems, create);
35
41
 
@@ -24,6 +24,7 @@ test("attributes -- mounted", async () => {
24
24
  thing: "thing1",
25
25
  dataThing: "thing2",
26
26
  description: "a person",
27
+ attr: "val",
27
28
  });
28
29
 
29
30
  const container = document.createElement("div");
@@ -38,6 +39,7 @@ test("attributes -- hydrated", async () => {
38
39
  thing: "thing1",
39
40
  dataThing: "thing2",
40
41
  description: "a person",
42
+ attr: "val",
41
43
  });
42
44
 
43
45
  const container = document.createElement("div");
@@ -56,4 +58,5 @@ function check(container: HTMLElement, _: State) {
56
58
  "caption",
57
59
  "this attribute is for a person",
58
60
  );
61
+ expect(queryByText(container, "Hello!")).toHaveAttribute("attr", "val");
59
62
  }
@@ -1,6 +1,11 @@
1
1
  export default function Attributes() {
2
2
  @render {
3
- <div thing={$props.thing} data-thing={$props.dataThing} caption="this attribute is for {$props.description}">
3
+ <div
4
+ thing={$props.thing}
5
+ data-thing={$props.dataThing}
6
+ caption="this attribute is for {$props.description}"
7
+ {$props.attr}
8
+ >
4
9
  Hello!
5
10
  </div>
6
11
  }
@@ -1,4 +1,5 @@
1
1
  import $run from "../../../../src/render/$run";
2
+ import $watch from "../../../../src/render/$watch";
2
3
  import { type SlotRender } from "../../../../src/types/SlotRender";
3
4
  import t_add_fragment from "../../../../src/render/addFragment";
4
5
  import t_attribute from "../../../../src/render/setAttribute";
@@ -15,12 +16,12 @@ export default function Attributes(
15
16
  // @ts-ignore
16
17
  $slots?: Record<string, SlotRender>
17
18
  ) {
18
- $props ??= {};
19
+ $props ??= $watch({});
19
20
 
20
21
  /* User interface */
21
22
  const t_fragments: DocumentFragment[] = [];
22
23
 
23
- const t_fragment_0 = t_fragment($parent.ownerDocument!, t_fragments, 0, `<div data-thing=""> Hello! </div>`);
24
+ const t_fragment_0 = t_fragment($parent.ownerDocument!, t_fragments, 0, `<div data-thing="" {$props.attr}> Hello! </div>`);
24
25
  // @ts-ignore
25
26
  const t_div_1 = t_root(t_fragment_0) as HTMLDivElement;
26
27
  $run(function setAttribute() {
@@ -32,6 +33,9 @@ export default function Attributes(
32
33
  $run(function setAttribute() {
33
34
  t_attribute(t_div_1, "caption", `this attribute is for ${$props.description}`);
34
35
  });
36
+ $run(function setAttribute() {
37
+ t_div_1.setAttribute("attr", $props.attr);
38
+ });
35
39
  t_add_fragment(t_fragment_0, $parent, $anchor);
36
40
  t_next(t_div_1);
37
41
 
@@ -8,10 +8,11 @@ export default function Attributes(
8
8
  // @ts-ignore
9
9
  $slots?: Record<string, ServerSlotRender>
10
10
  ) {
11
+ $props ??= {};
11
12
 
12
13
  /* User interface */
13
14
  let $output = "";
14
- $output += `<div ${$props.thing ? `thing="${t_attr($props.thing)}"` : ''} ${$props.dataThing ? `data-thing="${t_attr($props.dataThing)}"` : ''} caption="this attribute is for ${t_attr($props.description)}"> Hello! </div>`;
15
+ $output += `<div ${$props.thing ? `thing="${t_attr($props.thing)}"` : ''} ${$props.dataThing ? `data-thing="${t_attr($props.dataThing)}"` : ''} caption="this attribute is for ${t_attr($props.description)}" attr="${t_attr($props.attr)}"> Hello! </div>`;
15
16
 
16
17
  return $output;
17
18
  }