@torpor/view 0.0.13 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +5 -5
  3. package/dist/compile.cjs +83 -31
  4. package/dist/compile.cjs.map +1 -1
  5. package/dist/compile.js +83 -31
  6. package/dist/compile.js.map +1 -1
  7. package/dist/index.cjs +19 -12
  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 +19 -11
  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 +14 -8
  20. package/src/compile/build/client/buildFragment.ts +37 -1
  21. package/src/compile/build/client/buildFragmentText.ts +5 -0
  22. package/src/compile/build/client/buildHtmlNode.ts +1 -0
  23. package/src/compile/build/client/buildSlotNode.ts +4 -1
  24. package/src/compile/build/server/buildServerCode.ts +5 -0
  25. package/src/compile/build/server/buildServerComponentNode.ts +4 -1
  26. package/src/compile/build/server/buildServerElementNode.ts +11 -2
  27. package/src/compile/build/server/buildServerSlotNode.ts +4 -1
  28. package/src/compile/parse/parseCode.ts +20 -12
  29. package/src/index.ts +0 -2
  30. package/src/render/$unwrap.ts +1 -1
  31. package/src/render/addFragment.ts +7 -2
  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/class.test.ts +6 -1
  65. package/test/class/components/Class.torp +5 -1
  66. package/test/class/components/output/Class-client.ts +8 -7
  67. package/test/class/components/output/Class-server.ts +2 -1
  68. package/test/class/components/temp/{Class-client-8c3f9e0257cc2b687e9bb7106cccbab94a4b5adf.ts → Class-client-dc885e1e391f38973bd9ea25b7475b07f7c23960.ts} +8 -7
  69. package/test/class/components/temp/Class-server-d1a2463ba432e10c6f68bd59d1d31949c5a2044f.ts +18 -0
  70. package/test/component/components/output/Component-client.ts +3 -2
  71. package/test/component/components/output/Component-server.ts +1 -1
  72. package/test/component/components/output/Header-client.ts +2 -1
  73. package/test/component/components/output/Header-server.ts +1 -0
  74. package/test/component/components/temp/{Component-client-32cd481f03e25d10b8e93947a2ff8773135b4975.ts → Component-client-a2fb6f30fc340bac909b64cb4f5ac68cde376de3.ts} +3 -2
  75. package/test/component/components/temp/{Component-server-11542f55405218c0b471172cac1d6a1a4edb5c09.ts → Component-server-40be12cd528ccf768f1ad40ef2a435ad2b2a2d71.ts} +1 -1
  76. package/test/component/components/temp/{Header-client-ac151eea86e3f6b058fab101661e13ae73864968.ts → Header-client-75cb4aa868295ac168c16ba7b275fbb5ab9256f9.ts} +2 -1
  77. package/test/component/components/temp/{Header-server-4f10f2332f3ca7a097517cfb938ab1e6f31d68ac.ts → Header-server-f73307874bec34a95dcd152d7f83a3ad13a0f4ac.ts} +1 -0
  78. package/test/effects/components/output/For-client.ts +3 -2
  79. package/test/effects/components/output/For-server.ts +1 -0
  80. package/test/effects/components/output/If-client.ts +4 -3
  81. package/test/effects/components/output/If-server.ts +1 -0
  82. package/test/effects/components/output/NestedIf-client.ts +4 -3
  83. package/test/effects/components/output/NestedIf-server.ts +1 -0
  84. package/test/effects/components/temp/{For-client-887b1758cf5c80f64708a47369e0e6ce44ffa20f.ts → For-client-d8d4e4424ec3238fe9f79cc907d673612b63bcdf.ts} +3 -2
  85. package/test/effects/components/temp/{For-server-8e1c7393b102c870167dd086cbee45c47e614dfa.ts → For-server-1f853c161bea31a1a5b11b00d7a021b70c778066.ts} +1 -0
  86. package/test/effects/components/temp/{If-client-bfc548ef699b91f33e604f066211ce6269cc9506.ts → If-client-bb06988422395321bf298a75bf5563c6f750f776.ts} +4 -3
  87. package/test/effects/components/temp/{If-server-8c213b1a82cd0a5c2bccfeb90ba8bf01fa4672d2.ts → If-server-97e3cbe059306fc607985dcd1b187df3a278d979.ts} +1 -0
  88. package/test/effects/components/temp/{NestedIf-client-9e9155398f240345af392194063adc6e42dda80c.ts → NestedIf-client-dc880c1ec92e716ec730dfa0bac0e3d9265bdf27.ts} +4 -3
  89. package/test/effects/components/temp/{NestedIf-server-5f11d075c2ff224ee5feb7f61aa41aea8af44313.ts → NestedIf-server-9f199a33bae5d5fe34920d6a419937537a2b18dc.ts} +1 -0
  90. package/test/events/components/output/Increment-client.ts +2 -2
  91. package/test/events/components/temp/{Increment-client-35c275d15e94e4aa7625af12c45210521995ead5.ts → Increment-client-41bd7bc91aac8c7a9bf73527c8f7ba1794b09ab3.ts} +2 -2
  92. package/test/events/events.test.ts +2 -3
  93. package/test/for/components/ForEscape.torp +1 -1
  94. package/test/for/components/output/For-client.ts +1 -1
  95. package/test/for/components/output/ForEscape-client.ts +4 -4
  96. package/test/for/components/output/ForIn-client.ts +3 -2
  97. package/test/for/components/output/ForIn-server.ts +1 -0
  98. package/test/for/components/output/ForOf-client.ts +3 -2
  99. package/test/for/components/output/ForOf-server.ts +1 -0
  100. package/test/for/components/temp/{For-client-1f6e475347f0474375f12a7c24b0b293e3942aa7.ts → For-client-0be5994e98c07a3455cd8a18a2b1b8ef3605aba6.ts} +1 -1
  101. package/test/for/components/temp/{ForEscape-client-80d3078cf15001f2ab37b0ec63cfce7267a8a166.ts → ForEscape-client-a973a145e28b32ca55fe3dca53d885fcf5e11c37.ts} +4 -4
  102. package/test/for/components/temp/{ForIn-client-c8a1ec4473d05475e11928d06d081ab671fd9f28.ts → ForIn-client-5cc25a98e10826e8477fe6f931d5d2bbd0b13c9f.ts} +3 -2
  103. package/test/for/components/temp/{ForIn-server-d92cf8d3c3e300b562308607caca2c9cab3cb30b.ts → ForIn-server-d68e82c048f7e0f1525f74724b91e5cd01cdc6a6.ts} +1 -0
  104. package/test/for/components/temp/{ForOf-client-069881d4d9e52bc091e7213154924f3e1626174a.ts → ForOf-client-70b0d59ff72f1cffe2e0ac8523dd7ccdca4ffe39.ts} +3 -2
  105. package/test/for/components/temp/{ForOf-server-3bfbb33bd589890d272bbcbea53d9598f53bfd70.ts → ForOf-server-ac08d98dd249a3e64a366b8488450e6ce0d18def.ts} +1 -0
  106. package/test/for/for-escape.test.ts +2 -3
  107. package/test/function/components/output/Function-client.ts +1 -1
  108. package/test/function/components/temp/{Function-client-b8931e51769f3f1e84d1491c6474f8551acf9f8d.ts → Function-client-b3ff029a821d47e1a2249a52dfb681f4b2e96232.ts} +1 -1
  109. package/test/function/function.test.ts +1 -2
  110. package/test/html/components/output/Html-client.ts +3 -1
  111. package/test/html/components/output/Html-server.ts +1 -0
  112. package/test/html/components/temp/{Html-client-5fc745d9980a42a70dcb3473b0fd976e69882c9e.ts → Html-client-766ba4a63f28f929ca16cd7d1640fe92f9b30792.ts} +3 -1
  113. package/test/html/components/temp/{Html-server-437ea390e58947213ede74755a012cdacceeebe7.ts → Html-server-0a41da8b81bf833f331f3becdbac14be7ddc6c8c.ts} +1 -0
  114. package/test/if/components/output/IfAfterIf-client.ts +5 -4
  115. package/test/if/components/output/IfAfterIf-server.ts +1 -0
  116. package/test/if/components/output/IfElse-client.ts +4 -3
  117. package/test/if/components/output/IfElse-server.ts +1 -0
  118. package/test/if/components/output/IfElseIf-client.ts +5 -4
  119. package/test/if/components/output/IfElseIf-server.ts +1 -0
  120. package/test/if/components/output/IfNested-client.ts +5 -4
  121. package/test/if/components/output/IfNested-server.ts +1 -0
  122. package/test/if/components/temp/{IfAfterIf-client-a489daa5b961ed9da71418fdbb1665b6ba39f1b7.ts → IfAfterIf-client-49b6b8c42b0a351561f9f5f22c2ae2e844bf3b58.ts} +5 -4
  123. package/test/if/components/temp/{IfAfterIf-server-243736e666e7d77d3f8553a1682734555f5ffd22.ts → IfAfterIf-server-eb67cf1fcf6711882266219940a47bd4a66497f3.ts} +1 -0
  124. package/test/if/components/temp/{IfElse-client-4c71642f412d5dd2f45729eafb7e974781dbe011.ts → IfElse-client-e236d4a2e99262ff09675bc519c07e2826c0d70b.ts} +4 -3
  125. package/test/if/components/temp/{IfElse-server-64c055182b327504ee71d1f1d44cfccf48be350d.ts → IfElse-server-c077e0e30f4d2adec41ab779198362d21542d055.ts} +1 -0
  126. package/test/if/components/temp/{IfElseIf-client-98d50d6f40c2385c4ef4fc02329f9d2e182c76c4.ts → IfElseIf-client-b92106861f0c7ec3d09e3a42bb93d8f5bd4e3bdf.ts} +5 -4
  127. package/test/if/components/temp/{IfElseIf-server-79788b13af905175f50bfa8658f2ff2af73db232.ts → IfElseIf-server-67991eee3f56dc687cda600511c31396438e0a77.ts} +1 -0
  128. package/test/{build-server/components/temp/IfNested-client-979db97eea5b4595550b9b46f297f3fbc31796d2.ts → if/components/temp/IfNested-client-f563ac1b16a2280e457557281c4925ed447a9cd7.ts} +5 -4
  129. package/test/{build-server/components/temp/IfNested-server-3c4e4b6f2162c0906cef685830219b9ea9ee985c.ts → if/components/temp/IfNested-server-0a2a9643324ab73b02b310cad61ba87dab146ab2.ts} +1 -0
  130. package/test/mount/components/Mount.torp +1 -1
  131. package/test/mount/components/output/Mount-client.ts +0 -2
  132. package/test/mount/components/output/Mount-server.ts +1 -1
  133. package/test/mount/components/temp/{Mount-client-49d8d6475336b5749fe8a17be847fd2b6cda5ccc.ts → Mount-client-36dbc95a61345a1b7dcf07e6b0c998af2afca691.ts} +0 -2
  134. package/test/mount/components/temp/{Mount-server-e3d689cf52a4477da9aa00e2c7604110710db991.ts → Mount-server-8bef1924a731f3db63cb827be3938fec685e53af.ts} +1 -1
  135. package/test/on-mount/components/output/OnMount-client.ts +0 -2
  136. package/test/on-mount/components/temp/{OnMount-client-b2dfd41a0aaeafb3b7af14793a03e0fc3bc43aaa.ts → OnMount-client-21708a638d2939ee8b017f5dc819cee4748bde87.ts} +0 -2
  137. package/test/party/13-emit-to-parent.test.ts +2 -3
  138. package/test/party/16-context.test.ts +1 -2
  139. package/test/party/17-input-text.test.ts +2 -3
  140. package/test/party/18-input-checkbox.test.ts +1 -2
  141. package/test/party/19-input-radio.test.ts +2 -3
  142. package/test/party/20-input-select.test.ts +3 -4
  143. package/test/party/7-event-click.test.ts +1 -3
  144. package/test/party/9-conditional.test.ts +2 -4
  145. package/test/party/components/ColorSelect.torp +1 -1
  146. package/test/party/components/InputFocused.torp +1 -1
  147. package/test/party/components/InputHello.torp +1 -1
  148. package/test/party/components/IsAvailable.torp +1 -1
  149. package/test/party/components/PickPill.torp +2 -2
  150. package/test/party/components/output/AnswerButton-client.ts +3 -2
  151. package/test/party/components/output/AnswerButton-server.ts +1 -0
  152. package/test/party/components/output/AnswerButtonApp-client.ts +4 -4
  153. package/test/party/components/output/AnswerButtonApp-server.ts +1 -1
  154. package/test/party/components/output/ColorSelect-client.ts +1 -1
  155. package/test/party/components/output/Colors-client.ts +1 -1
  156. package/test/party/components/output/Counter-client.ts +1 -1
  157. package/test/party/components/output/FunnyButton-client.ts +1 -1
  158. package/test/party/components/output/FunnyButtonApp-client.ts +2 -2
  159. package/test/party/components/output/FunnyButtonApp-server.ts +1 -1
  160. package/test/party/components/output/InputFocused-client.ts +0 -2
  161. package/test/party/components/output/InputFocused-server.ts +1 -1
  162. package/test/party/components/output/InputHello-client.ts +1 -1
  163. package/test/party/components/output/IsAvailable-client.ts +1 -1
  164. package/test/party/components/output/PickPill-client.ts +2 -2
  165. package/test/party/components/output/TrafficLight-client.ts +6 -6
  166. package/test/party/components/output/UserProfile-client.ts +5 -4
  167. package/test/party/components/output/UserProfile-server.ts +1 -0
  168. package/test/party/components/output/UserProfileApp-client.ts +3 -2
  169. package/test/party/components/output/UserProfileApp-server.ts +1 -1
  170. package/test/party/components/output/UserProfileContext-client.ts +3 -3
  171. package/test/party/components/output/UserProfileContextApp-client.ts +2 -2
  172. package/test/party/components/output/UserProfileContextApp-server.ts +1 -1
  173. package/test/party/components/temp/{AnswerButton-client-243454cd7bf84c648f244b3587e60d01a0401039.ts → AnswerButton-client-c3dc7edbfff2db1ea111086002147d771ee12193.ts} +3 -2
  174. package/test/party/components/temp/{AnswerButton-server-938e46c1d2510ea37454854ef527e4aa64eb8e52.ts → AnswerButton-server-427442fa8cfc0a18817c2d37f9cb5cbcb3a88a05.ts} +1 -0
  175. package/test/party/components/temp/{AnswerButtonApp-client-34311c2e7412807867fb6267af31a341f092bf31.ts → AnswerButtonApp-client-3e0b63a6bbc1dee3e6cb715c0297cc2ce58d3e4e.ts} +4 -4
  176. package/test/party/components/temp/{AnswerButtonApp-server-3f5071775abea24ee8830830072c6fe465fc4e56.ts → AnswerButtonApp-server-bbc8bb739d2060ccda77ce406aa6b15d8f90b91c.ts} +1 -1
  177. package/test/party/components/temp/{ColorSelect-client-f9fb9fa09f4729da3226d5aaf30e84cd3cd705b5.ts → ColorSelect-client-5ffa6f9d77de967993088c9a01b9f976f3333176.ts} +1 -1
  178. package/test/party/components/temp/{Colors-client-f124162d1dd8f0c3d37bf96c8e5f18599ae3a682.ts → Colors-client-3dafffc5c2a72037f4419fc45d08963872a29958.ts} +1 -1
  179. package/test/party/components/temp/{Counter-client-c349710110b4afbf9593f20cfc40c162ed836079.ts → Counter-client-4d553521fcfc6c9a4e05dce89e70b2ecf8785c4e.ts} +1 -1
  180. package/test/party/components/temp/{FunnyButton-client-2e6bec8259065b5bc9ae16d1ca93dd1bd0c56b7c.ts → FunnyButton-client-45f2c5dd56f2cdb80102193a978df6b0cec822ab.ts} +1 -1
  181. package/test/party/components/temp/{FunnyButtonApp-client-4ed0ebf5be13ddb62d9942bac7f4b8a885ec6c50.ts → FunnyButtonApp-client-ac7101af922dc6ba472b75a428f340caca6be74a.ts} +2 -2
  182. package/test/party/components/temp/{FunnyButtonApp-server-9748c9096848e643c91237c46ea09d23f1b3fa7c.ts → FunnyButtonApp-server-f1a98a946e0bb9d1bb76b01e26d6ead8f55d32bb.ts} +1 -1
  183. package/test/party/components/temp/{InputFocused-client-a543f82c8db205fb3e33c62de699cff43c668a68.ts → InputFocused-client-11a9cb08909b26945bbc2451ab4c1dac09e6d113.ts} +0 -2
  184. package/test/party/components/temp/{InputFocused-server-222f2fa7029d1cd6941a3777293a9b2d37b7295e.ts → InputFocused-server-82be1a63c80d1dd3f06efaf0bef349d58cf1d9af.ts} +1 -1
  185. package/test/party/components/temp/{InputHello-client-9953499de5778bb1d8aac7691129210a6098db07.ts → InputHello-client-b0453caeae0d43dc1878039c6b49318d12624d48.ts} +1 -1
  186. package/test/party/components/temp/{IsAvailable-client-929ed6c872e72b6c5243b882c6a9a0d13f6a7c47.ts → IsAvailable-client-d789346a68b26fd3f5d9f7aebc4c7bd181c47cad.ts} +1 -1
  187. package/test/party/components/temp/{PickPill-client-f9a50a75ce63ec2ee3ac34e378e20602b93629f7.ts → PickPill-client-b47d371ae5f41d87c96b3cdeed0a8bc06fbfa9c7.ts} +2 -2
  188. package/test/party/components/temp/{TrafficLight-client-81dd9bd9f3332533e01f6c9b271c15e5f76e1fdb.ts → TrafficLight-client-549f9fe3418c113fb240cfa17e09c42e1e7f346a.ts} +6 -6
  189. package/test/party/components/temp/{UserProfile-client-938bcef20a2555de22aca939022a32742ac5e729.ts → UserProfile-client-2c13aac1ad915e51dc557f9af765669e636c0fea.ts} +5 -4
  190. package/test/party/components/temp/{UserProfile-server-9df5c59b8797fa67c479bc76450104d45e6076f9.ts → UserProfile-server-cc1162a4297f6163bfb86e7a8aeea08e8c0625c6.ts} +1 -0
  191. package/test/party/components/temp/{UserProfileApp-client-af27173415d65647cab0f30a9a4967cef0307e25.ts → UserProfileApp-client-2be7a11319cca4d7bfa0f3d2205c441d7e203780.ts} +3 -2
  192. package/test/party/components/temp/{UserProfileApp-server-6a7dbe68ee8041785cef50fb15a9e5ba475e120f.ts → UserProfileApp-server-ab9ff66f787f775aa8571314fee8bcbef85489ef.ts} +1 -1
  193. package/test/party/components/temp/{UserProfileContext-client-93211cdba795b8baf926f60f5b17da90701743b3.ts → UserProfileContext-client-2aa36f436a5f20614d77f48963ec5ca442eebd90.ts} +3 -3
  194. package/test/party/components/temp/{UserProfileContextApp-client-7ea7d90a35894408757aef3e935d3a8badf93aea.ts → UserProfileContextApp-client-5099052fdcd8fb4c368c45090390947424a4e9b0.ts} +2 -2
  195. package/test/party/components/temp/{UserProfileContextApp-server-90290c770529d7211eabf29e7d0fe868aa856b52.ts → UserProfileContextApp-server-6be8dab2ed4f8911f26bef137e7dea164f2b894d.ts} +1 -1
  196. package/test/props/components/Reactive.torp +18 -0
  197. package/test/props/components/output/Reactive-client.ts +70 -0
  198. package/test/props/components/output/Reactive-server.ts +40 -0
  199. package/test/props/components/temp/Reactive-client-e3e5f6ee2f22c46337c6ab1df2f241133c7e2646.ts +70 -0
  200. package/test/props/components/temp/Reactive-server-0914d91287a1bc6d32d7cc56d086624762dbfc04.ts +40 -0
  201. package/test/props/reactive.test.ts +51 -0
  202. package/test/replace/components/output/Replace-client.ts +3 -2
  203. package/test/replace/components/output/Replace-server.ts +1 -0
  204. package/test/replace/components/temp/{Replace-client-ea4ddc73c71181fb6ef3c6f5bf61b0b9e017f00a.ts → Replace-client-3fdab63871fa4bb792317caefd3539b1888325b1.ts} +3 -2
  205. package/test/replace/components/temp/{Replace-server-30eb1ad796516fa4d310798a9c8a6811ec13cbd9.ts → Replace-server-857aca7a58deffc3a1c95ab9d31628b4051ca203.ts} +1 -0
  206. package/test/slots/components/output/Article-client.ts +2 -2
  207. package/test/slots/components/output/Basic-client.ts +1 -1
  208. package/test/slots/components/output/Basic-server.ts +1 -1
  209. package/test/slots/components/output/Let-client.ts +4 -3
  210. package/test/slots/components/output/Let-server.ts +2 -1
  211. package/test/slots/components/output/List-client.ts +1 -1
  212. package/test/slots/components/output/List-server.ts +1 -0
  213. package/test/slots/components/output/Named-client.ts +2 -2
  214. package/test/slots/components/output/Named-server.ts +1 -1
  215. package/test/slots/components/output/Unused-client.ts +1 -1
  216. package/test/slots/components/output/Unused-server.ts +1 -1
  217. package/test/slots/components/temp/{Article-client-741f94eab2e96655f4c9d69ccd7d9c7cc998f4ce.ts → Article-client-80848d6cadd8abac61865d14ddd35685844797e0.ts} +2 -2
  218. package/test/slots/components/temp/{Basic-client-c82bc3c5e10f2f2c1869347435262c8f3039e32f.ts → Basic-client-97b18686412869e8c814fd4542dfe826b1f7669f.ts} +1 -1
  219. package/test/slots/components/temp/{Basic-server-21fb4f8caa3bbcae219aaef388af8c26c46fa5ce.ts → Basic-server-248392e2cd147b537cd44e9a5a71885ef20102e4.ts} +1 -1
  220. package/test/slots/components/temp/{Let-client-c54696fd1b5b859a693744722957fa960f72fad6.ts → Let-client-be69c1f5aa32faf16239050d7836db46cfdcba2f.ts} +4 -3
  221. package/test/slots/components/temp/{Let-server-4d78ffc8741fa737f2cd915b36c0e6ec88a9146f.ts → Let-server-c3293219ed27aec61a6af36d451ddf6609c40027.ts} +2 -1
  222. package/test/slots/components/temp/{List-client-0659d999cfb48b865fe5b8aaaf2a4ddac3a8a8b6.ts → List-client-a829b33214de829de689a72d2e8703b2266ad7a4.ts} +1 -1
  223. package/test/slots/components/temp/{List-server-4b76485a259d054187edad8aa691d80ac918ad86.ts → List-server-06503834c9bfae9b8c2b61742568b020a3de961c.ts} +1 -0
  224. package/test/slots/components/temp/{Named-client-0626b2f0911f77f5edcfda82f9751bcbc37b7641.ts → Named-client-2d9d88f5fdf74d81c3ed1d769a476f4ac36c8b99.ts} +2 -2
  225. package/test/slots/components/temp/{Named-server-e802f901223d697c7fd0a1a234ce1930af1b4b3f.ts → Named-server-65a7bbf038111475c8f07dccb34040ab8c1392c4.ts} +1 -1
  226. package/test/slots/components/temp/{Unused-client-aa05465731e0d00e18189498f49c94f991e76f10.ts → Unused-client-1b16491cb800bbbbd871d9fbf5966c7275244019.ts} +1 -1
  227. package/test/slots/components/temp/{Unused-server-00b92d14665a565f0042b1f56231f28bc38a1252.ts → Unused-server-e5d1e85116bebc7e1a2882cb8f2c9ae7975a1de7.ts} +1 -1
  228. package/test/special-component/components/output/Component-client.ts +5 -4
  229. package/test/special-component/components/output/Component-server.ts +3 -2
  230. package/test/special-component/components/temp/{Component-client-3cc84b23def8830b8ef441bde0f03b3e9718196a.ts → Component-client-22f7e1d0955fb3d90939e956a1f0f77118c259b6.ts} +5 -4
  231. package/test/special-component/components/temp/{Component-server-5dc597784ea1fe45ac2cfb21305cf2187a3868e8.ts → Component-server-ce596cd84dc11f67c9f0588060a5b672eddf9715.ts} +3 -2
  232. package/test/special-element/components/output/Element-client.ts +2 -1
  233. package/test/special-element/components/output/Element-server.ts +1 -0
  234. package/test/special-element/components/temp/{Element-client-0ab8992a8e914c7e190cddac790585fef9be1c9f.ts → Element-client-21c92c04579b1d29e8813265a10897d069aad5c5.ts} +2 -1
  235. package/test/special-element/components/temp/{Element-server-ddb0170df4a3f369577ca7e9768e5fe799f14dfb.ts → Element-server-4bed9653529f39e72754126147fe8618f328853a.ts} +1 -0
  236. package/test/special-self/components/output/Self-client.ts +3 -2
  237. package/test/special-self/components/output/Self-server.ts +1 -0
  238. package/test/special-self/components/temp/{Self-client-3982f174954034643c4d17cfc66d7f10bf1d4df6.ts → Self-client-911c67194be1fdd997537310bcafe712161e8251.ts} +3 -2
  239. package/test/special-self/components/temp/{Self-server-97ffbdaae47f4463034e1d318a8073d078ff138f.ts → Self-server-49ba9be1f6255dd4daf490c640f5222a85960218.ts} +1 -0
  240. package/test/style/components/output/Style-client.ts +2 -1
  241. package/test/style/components/output/Style-server.ts +1 -0
  242. package/test/style/components/temp/{Style-client-085517e82bdd015c86f4c2577447c6a2791cf656.ts → Style-client-cbfac1e255c33a867ccbd5f9d1484a855d91a2d7.ts} +2 -1
  243. package/test/style/components/temp/{Style-server-b2615163fbafdcb2a5d4155120edf2f66ef3bba6.ts → Style-server-763d82ee2296be3f7620814aed950b505329c04c.ts} +1 -0
  244. package/test/svg/components/output/Shape-client.ts +4 -3
  245. package/test/svg/components/output/Shape-server.ts +1 -0
  246. package/test/svg/components/temp/{Shape-client-ea698ed1b7d0c3615adff35fa4956f22840f5fda.ts → Shape-client-cb98b36ab2a0d9f06b484a3c988fb43c3a71a4a0.ts} +4 -3
  247. package/test/svg/components/temp/{Shape-server-eb32049cfb7c058b2c4079c5f86ef00651ff6d69.ts → Shape-server-500d7fe4556026234ce5379b8e90595ddd094edc.ts} +1 -0
  248. package/test/switch/components/output/Switch-client.ts +5 -4
  249. package/test/switch/components/output/Switch-server.ts +1 -0
  250. package/test/switch/components/temp/{Switch-client-4c4adac7308795807517e66bc268a38f87df6bda.ts → Switch-client-9980de45e3346b79525c445edec7cbc3b375e8a4.ts} +5 -4
  251. package/test/switch/components/temp/{Switch-server-21fcac2b02c6071136bb0dc394babd27e454d2c9.ts → Switch-server-1c5690726f28008bb565966af69b6e4e019dea82.ts} +1 -0
  252. package/test/text/components/output/Text-client.ts +3 -2
  253. package/test/text/components/output/Text-server.ts +1 -0
  254. package/test/text/components/temp/{Text-client-9840092be1b4552ddce6e5efc2f1b395637a0a3d.ts → Text-client-63f1d3bf9b3bd145c7ce7058716640e27144055e.ts} +3 -2
  255. package/test/text/components/temp/{Text-server-6774aa9d4ccce941224ba0a8fb1d3e42eb31807e.ts → Text-server-9d3dd6f51e893e63c1999ceafce19ca1697d76ea.ts} +1 -0
  256. package/test/watch-array/components/output/Array-client.ts +4 -3
  257. package/test/watch-array/components/output/Array-server.ts +1 -0
  258. package/test/watch-array/components/output/ArrayEntries-client.ts +4 -3
  259. package/test/watch-array/components/output/ArrayEntries-server.ts +1 -0
  260. package/test/watch-array/components/output/ArrayIndexes-client.ts +4 -3
  261. package/test/watch-array/components/output/ArrayIndexes-server.ts +1 -0
  262. package/test/watch-array/components/output/ArrayUnkeyed-client.ts +4 -3
  263. package/test/watch-array/components/output/ArrayUnkeyed-server.ts +1 -0
  264. package/test/watch-array/components/temp/{Array-client-b29a42f88c2a2f37b3a86bdb9535e20a487887e1.ts → Array-client-ffd82188a45877d74053c82fb3616e4aed0fc2ad.ts} +4 -3
  265. package/test/watch-array/components/temp/{Array-server-ced54fc7e63196f9c24c2e55b27bfe6cf7821dcb.ts → Array-server-3715e815af887e8487b214e7de324850fe99e312.ts} +1 -0
  266. package/test/watch-array/components/temp/{ArrayEntries-client-6289890758b1f6b462a74417e0b9e2e46d946ccd.ts → ArrayEntries-client-b6e982660628576061dbb44539e970b9549bdcfa.ts} +4 -3
  267. package/test/watch-array/components/temp/{ArrayEntries-server-0429a18ffbe39afe87612889a3010c3fcaa601dc.ts → ArrayEntries-server-85cbe80fbb7f6885287825e2cc0dfc1cb9835d79.ts} +1 -0
  268. package/test/watch-array/components/temp/{ArrayIndexes-client-fe4b1c49f442b3e5411f331c52ae6c5ab38f2b2d.ts → ArrayIndexes-client-39337aa43bd8c10a53db836c3be233d48dd39b91.ts} +4 -3
  269. package/test/watch-array/components/temp/{ArrayIndexes-server-20c01f10832f035b007f85a423778934d51a703a.ts → ArrayIndexes-server-ec623c724af0c7212675d61ea68e9dee87f96caa.ts} +1 -0
  270. package/test/watch-array/components/temp/{ArrayUnkeyed-client-3fffe2ce749ee84e0730130bfd730d432e3c4e12.ts → ArrayUnkeyed-client-7606f9a6a221c8cf6a8b51e4895d10a3d7bccc11.ts} +4 -3
  271. package/test/watch-array/components/temp/{ArrayUnkeyed-server-0ab804a5e7bf5b47594d85adab79735e89a3939d.ts → ArrayUnkeyed-server-db21cd576a4c70973abe10f6c8ef5cecb3ead86c.ts} +1 -0
  272. package/test/watch-object/components/output/Watched-client.ts +2 -1
  273. package/test/watch-object/components/output/Watched-server.ts +1 -0
  274. package/test/watch-object/components/temp/{Watched-client-cb293038cd0a39043d05639c624a17d512419c25.ts → Watched-client-bbffbf2cede1a5c071b770670eb39c0f0be725af.ts} +2 -1
  275. package/test/watch-object/components/temp/{Watched-server-4884585cf4f50493a70631909f2230153531208e.ts → Watched-server-0252addaf92fb0f0c1e2937f42918a1c9f31d196.ts} +1 -0
  276. package/src/render/flushMountEffects.ts +0 -8
  277. package/test/class/components/temp/Class-server-2218c3284845fdab80d1a41ebace9a9e2ddd6fc1.ts +0 -17
  278. package/test/if/components/temp/IfNested-client-979db97eea5b4595550b9b46f297f3fbc31796d2.ts +0 -90
  279. 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.13",
3
+ "version": "0.1.0",
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.21",
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.1.4"
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)) {
@@ -175,8 +178,11 @@ function buildElementAttributes(
175
178
  // Ignore this special attribute
176
179
  } else if (name.startsWith("{") && name.endsWith("}")) {
177
180
  // It's a shortcut attribute
181
+ // It could be e.g. {width} or it could be {$state.width}, but
182
+ // in either case we set the value of the width property
178
183
  name = name.substring(1, name.length - 1);
179
- buildRun("setAttribute", `${varName}.setAttribute("${name}", ${name});`, status, b);
184
+ const propName = name.split(".").at(-1);
185
+ buildRun("setAttribute", `${varName}.setAttribute("${propName}", ${name});`, status, b);
180
186
  } else if (value != null && isReactive(value)) {
181
187
  // It's a reactive attribute
182
188
  if (isFullyReactive(value)) {
@@ -185,17 +191,17 @@ function buildElementAttributes(
185
191
  value = `\`${trimQuotes(value).replaceAll("{", "${")}\``;
186
192
  }
187
193
 
188
- if (name === ":self") {
194
+ if (name === "&ref") {
189
195
  // Bind the DOM element to a user-defined variable
190
196
  b.append(`${value} = ${varName};`);
191
- } else if (name === ":group") {
197
+ } else if (name === "&group") {
192
198
  buildBindGroupAttribute(node, varName, value, status, b);
193
- } else if (name === ":value" || name === ":checked") {
199
+ } else if (name === "&value" || name === "&checked") {
194
200
  buildBindAttribute(node, varName, name, value, status, b);
195
201
  } 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
202
+ // The :onmount event is faked by us by creating a $mount. This
203
+ // also means that you can have unmount functionality by
204
+ // returning a cleanup function
199
205
  buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
200
206
  } else if (name.startsWith("on")) {
201
207
  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) {
@@ -146,6 +146,7 @@ function buildElementFragmentText(
146
146
  fragments: Fragment[],
147
147
  currentFragment: Fragment,
148
148
  ) {
149
+ let needsClass = node.scopeStyles;
149
150
  currentFragment.text += `<${node.tagName}`;
150
151
  let attributesText = node.attributes
151
152
  .filter((a) => !a.name.startsWith("on") && !a.name.includes(":"))
@@ -157,6 +158,7 @@ function buildElementFragmentText(
157
158
  return `${a.name}=""`;
158
159
  }
159
160
  } else if (a.name === "class" && node.scopeStyles) {
161
+ needsClass = false;
160
162
  let value = a.value
161
163
  ? `"${trimQuotes(a.value)} torp-${status.styleHash}"`
162
164
  : `"torp-${status.styleHash}"`;
@@ -170,6 +172,9 @@ function buildElementFragmentText(
170
172
  if (attributesText) {
171
173
  currentFragment.text += " " + attributesText;
172
174
  }
175
+ if (needsClass) {
176
+ currentFragment.text += ` class="torp-${status.styleHash}"`;
177
+ }
173
178
  currentFragment.text += ">";
174
179
  for (let child of node.children) {
175
180
  buildNodeFragmentText(child, status, fragments, currentFragment);
@@ -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);
@@ -73,6 +73,11 @@ function buildServerTemplate(
73
73
  } else if (script.substring(i, i + "/* @start */".length) === "/* @start */") {
74
74
  b.append(script.substring(marker, i));
75
75
 
76
+ // Make sure we've got $props if we're going to be using it
77
+ if (current.props?.length) {
78
+ b.append(`$props ??= {};`);
79
+ }
80
+
76
81
  // Redefine $context so that any newly added properties will only be passed to children
77
82
  if (current.contextProps?.length) {
78
83
  b.append(`$context = Object.assign({}, $context);`);
@@ -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);
@@ -40,6 +40,7 @@ export default function buildServerElementNode(
40
40
  }
41
41
 
42
42
  function buildElementAttributes(node: ElementNode, status: BuildServerStatus) {
43
+ let needsClass = node.scopeStyles;
43
44
  let attributes: string[] = [];
44
45
  for (let { name, value } of node.attributes) {
45
46
  if (name === "self" && node.tagName === ":element") {
@@ -62,14 +63,17 @@ function buildElementAttributes(node: ElementNode, status: BuildServerStatus) {
62
63
  );
63
64
  } else if (name.startsWith("{") && name.endsWith("}")) {
64
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
65
68
  name = name.substring(1, name.length - 1);
69
+ const propName = name.split(".").at(-1);
66
70
  status.imports.add("t_attr");
67
- attributes.push(`${name}="\${t_attr(${name})}"`);
71
+ attributes.push(`${propName}="\${t_attr(${name})}"`);
68
72
  } else if (value != null && isFullyReactive(value)) {
69
73
  // It's a reactive attribute
70
74
  value = value.substring(1, value.length - 1);
71
75
 
72
- if (name === ":self" || name === ":value" || name === ":checked" || name === ":group") {
76
+ if (name === "&ref" || name === "&value" || name === "&checked" || name === "&group") {
73
77
  let defaultValue = "";
74
78
  let typeAttribute = node.attributes.find((a) => a.name === "type");
75
79
  if (typeAttribute && typeAttribute.value) {
@@ -96,6 +100,7 @@ function buildElementAttributes(node: ElementNode, status: BuildServerStatus) {
96
100
  status.imports.add("t_class");
97
101
  if (node.scopeStyles) {
98
102
  value += `, "torp-${status.styleHash}"`;
103
+ needsClass = false;
99
104
  }
100
105
  attributes.push(`class="\${t_class(${value})}"`);
101
106
  } else if (name === ":style") {
@@ -118,6 +123,7 @@ function buildElementAttributes(node: ElementNode, status: BuildServerStatus) {
118
123
  ? `"${trimQuotes(value)} torp-${status.styleHash}"`
119
124
  : `"torp-${status.styleHash}"`;
120
125
  attributes.push(`${name}="${trimQuotes(value).replaceAll('"', "&quot;")}"`);
126
+ needsClass = false;
121
127
  } else if (value != null) {
122
128
  // Just set the attribute
123
129
  // TODO: Probably check if it's a boolean attribute e.g. disabled
@@ -126,5 +132,8 @@ function buildElementAttributes(node: ElementNode, status: BuildServerStatus) {
126
132
  attributes.push(`${name}`);
127
133
  }
128
134
  }
135
+ if (needsClass) {
136
+ attributes.push(`class="torp-${status.styleHash}"`);
137
+ }
129
138
  return attributes.join(" ");
130
139
  }
@@ -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,
@@ -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,17 @@ 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
+ // functions, add our stashed events and play our stashed animations
45
45
  if (!parentIsFragment) {
46
46
  // Set the active range for each event and animation so it will get
47
47
  // attached to the right one and set it back afterwards
48
48
 
49
+ for (let effect of context.mountEffects) {
50
+ $run(effect);
51
+ }
52
+ context.mountEffects.length = 0;
53
+
49
54
  for (let event of context.stashedEvents) {
50
55
  context.activeRange = event.range;
51
56
  $run(function addFragmentEvent() {
@@ -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
  }