ripple 0.3.72 → 0.3.76

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 (172) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/package.json +3 -3
  3. package/src/jsx-runtime.d.ts +4 -10
  4. package/src/runtime/dynamic-client.js +33 -0
  5. package/src/runtime/dynamic-server.js +80 -0
  6. package/src/runtime/index-client.js +5 -13
  7. package/src/runtime/index-server.js +2 -0
  8. package/src/runtime/internal/client/blocks.js +6 -27
  9. package/src/runtime/internal/client/composite.js +11 -6
  10. package/src/runtime/internal/client/for.js +80 -5
  11. package/src/runtime/internal/client/index.js +0 -2
  12. package/src/runtime/internal/client/render.js +5 -2
  13. package/src/runtime/internal/client/types.d.ts +0 -10
  14. package/src/runtime/internal/server/index.js +8 -1
  15. package/tests/client/__snapshots__/computed-properties.test.tsrx.snap +8 -0
  16. package/tests/client/__snapshots__/for.test.tsrx.snap +22 -0
  17. package/tests/client/__snapshots__/html.test.tsrx.snap +4 -0
  18. package/tests/client/array/array.copy-within.test.tsrx +19 -19
  19. package/tests/client/array/array.derived.test.tsrx +97 -109
  20. package/tests/client/array/array.iteration.test.tsrx +28 -28
  21. package/tests/client/array/array.mutations.test.tsrx +68 -68
  22. package/tests/client/array/array.static.test.tsrx +82 -92
  23. package/tests/client/array/array.to-methods.test.tsrx +15 -15
  24. package/tests/client/async-suspend.test.tsrx +180 -179
  25. package/tests/client/basic/__snapshots__/basic.attributes.test.tsrx.snap +2 -0
  26. package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +4 -0
  27. package/tests/client/basic/basic.attributes.test.tsrx +273 -317
  28. package/tests/client/basic/basic.collections.test.tsrx +55 -61
  29. package/tests/client/basic/basic.components.test.tsrx +198 -220
  30. package/tests/client/basic/basic.errors.test.tsrx +70 -76
  31. package/tests/client/basic/basic.events.test.tsrx +80 -85
  32. package/tests/client/basic/basic.get-set.test.tsrx +54 -64
  33. package/tests/client/basic/basic.hmr.test.tsrx +15 -19
  34. package/tests/client/basic/basic.reactivity.test.tsrx +121 -135
  35. package/tests/client/basic/basic.rendering.test.tsrx +273 -178
  36. package/tests/client/basic/basic.styling.test.tsrx +16 -14
  37. package/tests/client/basic/basic.utilities.test.tsrx +8 -10
  38. package/tests/client/boundaries.test.tsrx +18 -18
  39. package/tests/client/compiler/compiler.assignments.test.tsrx +77 -76
  40. package/tests/client/compiler/compiler.attributes.test.tsrx +18 -14
  41. package/tests/client/compiler/compiler.basic.test.tsrx +357 -288
  42. package/tests/client/compiler/compiler.regex.test.tsrx +40 -44
  43. package/tests/client/compiler/compiler.tracked-access.test.tsrx +57 -38
  44. package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
  45. package/tests/client/compiler/compiler.typescript.test.tsrx +4 -3
  46. package/tests/client/composite/composite.dynamic-components.test.tsrx +62 -47
  47. package/tests/client/composite/composite.generics.test.tsrx +165 -167
  48. package/tests/client/composite/composite.props.test.tsrx +66 -74
  49. package/tests/client/composite/composite.reactivity.test.tsrx +132 -166
  50. package/tests/client/composite/composite.render.test.tsrx +92 -101
  51. package/tests/client/computed-properties.test.tsrx +14 -18
  52. package/tests/client/context.test.tsrx +14 -18
  53. package/tests/client/css/global-additional-cases.test.tsrx +493 -439
  54. package/tests/client/css/global-advanced-selectors.test.tsrx +169 -153
  55. package/tests/client/css/global-at-rules.test.tsrx +71 -66
  56. package/tests/client/css/global-basic.test.tsrx +105 -98
  57. package/tests/client/css/global-classes-ids.test.tsrx +128 -114
  58. package/tests/client/css/global-combinators.test.tsrx +83 -78
  59. package/tests/client/css/global-complex-nesting.test.tsrx +134 -120
  60. package/tests/client/css/global-edge-cases.test.tsrx +138 -120
  61. package/tests/client/css/global-keyframes.test.tsrx +108 -96
  62. package/tests/client/css/global-nested.test.tsrx +88 -78
  63. package/tests/client/css/global-pseudo.test.tsrx +104 -98
  64. package/tests/client/css/global-scoping.test.tsrx +145 -125
  65. package/tests/client/css/style-identifier.test.tsrx +65 -72
  66. package/tests/client/date.test.tsrx +83 -83
  67. package/tests/client/dynamic-elements.test.tsrx +318 -299
  68. package/tests/client/events.test.tsrx +252 -266
  69. package/tests/client/for.test.tsrx +120 -127
  70. package/tests/client/head.test.tsrx +74 -48
  71. package/tests/client/html.test.tsrx +37 -49
  72. package/tests/client/input-value.test.tsrx +1125 -1354
  73. package/tests/client/lazy-array.test.tsrx +10 -16
  74. package/tests/client/lazy-destructuring.test.tsrx +169 -221
  75. package/tests/client/map.test.tsrx +39 -41
  76. package/tests/client/media-query.test.tsrx +15 -19
  77. package/tests/client/object.test.tsrx +46 -56
  78. package/tests/client/portal.test.tsrx +31 -37
  79. package/tests/client/ref.test.tsrx +173 -193
  80. package/tests/client/return.test.tsrx +62 -37
  81. package/tests/client/set.test.tsrx +33 -33
  82. package/tests/client/svg.test.tsrx +197 -216
  83. package/tests/client/switch.test.tsrx +201 -191
  84. package/tests/client/track-async-hydration.test.tsrx +14 -18
  85. package/tests/client/tracked-index-access.test.tsrx +18 -28
  86. package/tests/client/try.test.tsrx +494 -619
  87. package/tests/client/tsx.test.tsrx +286 -292
  88. package/tests/client/typescript-generics.test.tsrx +121 -129
  89. package/tests/client/url/url.derived.test.tsrx +21 -25
  90. package/tests/client/url/url.parsing.test.tsrx +35 -35
  91. package/tests/client/url/url.partial-removal.test.tsrx +32 -32
  92. package/tests/client/url/url.reactivity.test.tsrx +68 -72
  93. package/tests/client/url/url.serialization.test.tsrx +8 -8
  94. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +21 -27
  95. package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +16 -16
  96. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +37 -37
  97. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +56 -60
  98. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +32 -34
  99. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +9 -9
  100. package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +10 -10
  101. package/tests/hydration/compiled/client/basic.js +390 -319
  102. package/tests/hydration/compiled/client/composite.js +52 -44
  103. package/tests/hydration/compiled/client/for.js +734 -604
  104. package/tests/hydration/compiled/client/head.js +183 -103
  105. package/tests/hydration/compiled/client/html.js +93 -86
  106. package/tests/hydration/compiled/client/if-children.js +95 -71
  107. package/tests/hydration/compiled/client/if.js +113 -89
  108. package/tests/hydration/compiled/client/mixed-control-flow.js +225 -209
  109. package/tests/hydration/compiled/client/nested-control-flow.js +94 -98
  110. package/tests/hydration/compiled/client/reactivity.js +26 -24
  111. package/tests/hydration/compiled/client/return.js +8 -42
  112. package/tests/hydration/compiled/client/switch.js +208 -173
  113. package/tests/hydration/compiled/client/track-async-serialization.js +176 -128
  114. package/tests/hydration/compiled/client/try.js +29 -21
  115. package/tests/hydration/compiled/server/basic.js +210 -221
  116. package/tests/hydration/compiled/server/composite.js +13 -14
  117. package/tests/hydration/compiled/server/for.js +427 -444
  118. package/tests/hydration/compiled/server/head.js +199 -189
  119. package/tests/hydration/compiled/server/html.js +33 -41
  120. package/tests/hydration/compiled/server/if-children.js +114 -117
  121. package/tests/hydration/compiled/server/if.js +77 -83
  122. package/tests/hydration/compiled/server/mixed-control-flow.js +145 -150
  123. package/tests/hydration/compiled/server/nested-control-flow.js +10 -0
  124. package/tests/hydration/compiled/server/reactivity.js +24 -22
  125. package/tests/hydration/compiled/server/return.js +6 -18
  126. package/tests/hydration/compiled/server/switch.js +179 -176
  127. package/tests/hydration/compiled/server/track-async-serialization.js +88 -70
  128. package/tests/hydration/compiled/server/try.js +31 -35
  129. package/tests/hydration/components/basic.tsrx +216 -258
  130. package/tests/hydration/components/composite.tsrx +32 -42
  131. package/tests/hydration/components/events.tsrx +81 -101
  132. package/tests/hydration/components/for.tsrx +270 -336
  133. package/tests/hydration/components/head.tsrx +43 -39
  134. package/tests/hydration/components/hmr.tsrx +16 -22
  135. package/tests/hydration/components/html-in-template.tsrx +15 -21
  136. package/tests/hydration/components/html.tsrx +442 -526
  137. package/tests/hydration/components/if-children.tsrx +107 -125
  138. package/tests/hydration/components/if.tsrx +68 -90
  139. package/tests/hydration/components/mixed-control-flow.tsrx +65 -72
  140. package/tests/hydration/components/nested-control-flow.tsrx +202 -216
  141. package/tests/hydration/components/portal.tsrx +33 -41
  142. package/tests/hydration/components/reactivity.tsrx +26 -34
  143. package/tests/hydration/components/return.tsrx +4 -6
  144. package/tests/hydration/components/switch.tsrx +73 -78
  145. package/tests/hydration/components/track-async-serialization.tsrx +83 -93
  146. package/tests/hydration/components/try.tsrx +37 -51
  147. package/tests/hydration/switch.test.js +8 -8
  148. package/tests/server/await.test.tsrx +3 -3
  149. package/tests/server/basic.attributes.test.tsrx +117 -162
  150. package/tests/server/basic.components.test.tsrx +164 -194
  151. package/tests/server/basic.test.tsrx +299 -199
  152. package/tests/server/compiler.test.tsrx +142 -72
  153. package/tests/server/composite.props.test.tsrx +54 -58
  154. package/tests/server/composite.test.tsrx +165 -167
  155. package/tests/server/context.test.tsrx +13 -17
  156. package/tests/server/dynamic-elements.test.tsrx +147 -148
  157. package/tests/server/for.test.tsrx +115 -84
  158. package/tests/server/head.test.tsrx +54 -31
  159. package/tests/server/html-nesting-validation.test.tsrx +16 -8
  160. package/tests/server/if.test.tsrx +49 -59
  161. package/tests/server/lazy-destructuring.test.tsrx +288 -366
  162. package/tests/server/return.test.tsrx +58 -36
  163. package/tests/server/streaming-ssr.test.tsrx +4 -4
  164. package/tests/server/style-identifier.test.tsrx +61 -69
  165. package/tests/server/switch.test.tsrx +89 -97
  166. package/tests/server/track-async-serialization.test.tsrx +85 -103
  167. package/tests/server/try.test.tsrx +275 -360
  168. package/tests/utils/ref-types.test.js +72 -0
  169. package/tests/utils/vite-plugin-config.test.js +41 -74
  170. package/types/index.d.ts +29 -4
  171. package/src/runtime/internal/client/compat.js +0 -40
  172. package/tests/utils/compiler-compat-config.test.js +0 -38
package/CHANGELOG.md CHANGED
@@ -1,5 +1,121 @@
1
1
  # ripple
2
2
 
3
+ ## 0.3.76
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1229](https://github.com/Ripple-TS/ripple/pull/1229)
8
+ [`6fd49c9`](https://github.com/Ripple-TS/ripple/commit/6fd49c9dd737e889844e254763f66e13ea4a7241)
9
+ Thanks [@leonidaz](https://github.com/leonidaz)! - Replace the removed `<@...>`
10
+ dynamic tag syntax with runtime `Dynamic` helpers. Ripple now exports `Dynamic`
11
+ and reuses its composite runtime path for dynamic elements/components, while
12
+ React, Preact, Solid, and Vue expose target-specific `Dynamic` helpers with
13
+ typed `is` props.
14
+
15
+ React, Preact, Solid, and Vue now mark imported runtime `Dynamic` elements
16
+ during shared JSX analysis so scoped CSS classes are applied through aliases
17
+ without treating local components named `Dynamic` as runtime elements.
18
+
19
+ Dynamic component prop forwarding now uses a shared core runtime helper that
20
+ excludes the internal `is` prop without snapshotting getter-backed reactive
21
+ props.
22
+
23
+ The TSRX parser, transforms, analyzers, prettier support, and related tests no
24
+ longer recognize dynamic tag syntax. Stale JSX identifier `tracked` plumbing
25
+ from that parser path has also been removed.
26
+
27
+ - Updated dependencies
28
+ [[`6fd49c9`](https://github.com/Ripple-TS/ripple/commit/6fd49c9dd737e889844e254763f66e13ea4a7241)]:
29
+ - @tsrx/core@0.1.24
30
+ - @tsrx/ripple@0.1.24
31
+
32
+ ## 0.3.75
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies
37
+ [[`9eb4819`](https://github.com/Ripple-TS/ripple/commit/9eb4819cede6da7e93cbcd2bdf284bcb42d40464),
38
+ [`88a254c`](https://github.com/Ripple-TS/ripple/commit/88a254c69953a5ace33bc10047f11052ec598672),
39
+ [`ba3a7f6`](https://github.com/Ripple-TS/ripple/commit/ba3a7f6485ea163e60cc0750a8e8b06b50728009),
40
+ [`ac6f358`](https://github.com/Ripple-TS/ripple/commit/ac6f3582ca0b2814004439c882d6aa735c8afe50),
41
+ [`4c5f992`](https://github.com/Ripple-TS/ripple/commit/4c5f992b9a11e1f26abee476a6add89f959169bc),
42
+ [`78ffa8d`](https://github.com/Ripple-TS/ripple/commit/78ffa8d90fd01e85bf34e5c6adef0e51caae8da7),
43
+ [`16560cb`](https://github.com/Ripple-TS/ripple/commit/16560cb466430bdbe8749d9491bc79e69e58d02c),
44
+ [`186b3b2`](https://github.com/Ripple-TS/ripple/commit/186b3b2557761ff06c9056bf2e0b7ab8c7692477),
45
+ [`4be6e54`](https://github.com/Ripple-TS/ripple/commit/4be6e54bbfee20927adca473648a94aa173d7d77),
46
+ [`2b67f83`](https://github.com/Ripple-TS/ripple/commit/2b67f83d7ed7eab7a39bc33524fcf73f737d977e),
47
+ [`9918c52`](https://github.com/Ripple-TS/ripple/commit/9918c52e954f2b8e1a994892e7c555e8277f2d59),
48
+ [`e8493be`](https://github.com/Ripple-TS/ripple/commit/e8493be0b3489f402105297251e1919c103c2360),
49
+ [`c424675`](https://github.com/Ripple-TS/ripple/commit/c424675102a9edd4f1e356fb6db30124a9c2d885)]:
50
+ - @tsrx/core@0.1.23
51
+ - @tsrx/ripple@0.1.23
52
+
53
+ ## 0.3.74
54
+
55
+ ### Patch Changes
56
+
57
+ - [#1199](https://github.com/Ripple-TS/ripple/pull/1199)
58
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649)
59
+ Thanks [@trueadm](https://github.com/trueadm)! - Expose Ripple's `RefValue` type
60
+ from the shared TSRX ref runtime declarations.
61
+
62
+ - [#1199](https://github.com/Ripple-TS/ripple/pull/1199)
63
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649)
64
+ Thanks [@trueadm](https://github.com/trueadm)! - Add `@empty { ... }` fallbacks
65
+ for TSRX `@for` loops, require prefixed template continuation clauses such as
66
+ `@else`, `@empty`, `@pending`, `@catch`, `@case`, and `@default`, and reject
67
+ direct `continue`, `break`, and `return` statements inside `@for` loop bodies
68
+ and `@if` template branches.
69
+
70
+ - [#1199](https://github.com/Ripple-TS/ripple/pull/1199)
71
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649)
72
+ Thanks [@trueadm](https://github.com/trueadm)! - Allow JSX and shared ref helper
73
+ types to accept arrays of ref functions.
74
+
75
+ - Updated dependencies
76
+ [[`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
77
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
78
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
79
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
80
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
81
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
82
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
83
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
84
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
85
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
86
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
87
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
88
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
89
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
90
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
91
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
92
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
93
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
94
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
95
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
96
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
97
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
98
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649),
99
+ [`5d33325`](https://github.com/Ripple-TS/ripple/commit/5d3332564109d228af5e02c0f68ca4a318766649)]:
100
+ - @tsrx/ripple@0.1.22
101
+ - @tsrx/core@0.1.22
102
+
103
+ ## 0.3.73
104
+
105
+ ### Patch Changes
106
+
107
+ - [#1198](https://github.com/Ripple-TS/ripple/pull/1198)
108
+ [`1de66b8`](https://github.com/Ripple-TS/ripple/commit/1de66b8f851849597b6078dab7af2699e49b0e21)
109
+ Thanks [@trueadm](https://github.com/trueadm)! - Remove the unused namespaced
110
+ TSX island feature and React bridge package.
111
+
112
+ - Updated dependencies
113
+ [[`e738e11`](https://github.com/Ripple-TS/ripple/commit/e738e1153694f56f35cfcab8982d897d7199d85a),
114
+ [`1de66b8`](https://github.com/Ripple-TS/ripple/commit/1de66b8f851849597b6078dab7af2699e49b0e21),
115
+ [`e00f596`](https://github.com/Ripple-TS/ripple/commit/e00f5961d5668c054435c8a366ef2a6da6e4a381)]:
116
+ - @tsrx/ripple@0.1.21
117
+ - @tsrx/core@0.1.21
118
+
3
119
  ## 0.3.72
4
120
 
5
121
  ### Patch Changes
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Ripple is an elegant TypeScript UI framework",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.3.72",
6
+ "version": "0.3.76",
7
7
  "type": "module",
8
8
  "module": "src/runtime/index-client.js",
9
9
  "main": "src/runtime/index-client.js",
@@ -74,8 +74,8 @@
74
74
  "clsx": "^2.1.1",
75
75
  "devalue": "^5.8.1",
76
76
  "esm-env": "^1.2.2",
77
- "@tsrx/core": "0.1.20",
78
- "@tsrx/ripple": "0.1.20"
77
+ "@tsrx/core": "0.1.24",
78
+ "@tsrx/ripple": "0.1.24"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@types/estree": "^1.0.8",
@@ -1,5 +1,6 @@
1
- import type { AddEventObject, FragmentProps, RefKey, TSRXElement } from '#public';
1
+ import type { AddEventObject, FragmentProps, RefKey, RefValue, TSRXElement } from '#public';
2
2
  import type { Nullable } from '#helpers';
3
+ export type { RefValue } from '#public';
3
4
 
4
5
  /**
5
6
  * Ripple JSX Runtime Type Definitions
@@ -7,8 +8,8 @@ import type { Nullable } from '#helpers';
7
8
  * renderable TSRX values when used in expression positions.
8
9
  */
9
10
 
10
- // Ripple components don't return JSX elements - they're imperative
11
- export type ComponentType<P = {}> = (props: P) => void;
11
+ // Ripple components are usually imperative, but helpers can return TSRX values.
12
+ export type ComponentType<P = {}> = (props: P) => void | TSRXElement;
12
13
 
13
14
  /**
14
15
  * Create a JSX element (for elements with children)
@@ -67,13 +68,6 @@ type EventHandlerValue<Target extends globalThis.EventTarget, EventType extends
67
68
  | EventHandler<Target, EventType>
68
69
  | EventHandlerObject<Target, EventType>;
69
70
 
70
- type RefValue<Target extends globalThis.Element> =
71
- | ((node: Target) => void | (() => void))
72
- | { value: Target | null }
73
- | Target
74
- | null
75
- | undefined;
76
-
77
71
  type RefAttribute<Target extends globalThis.Element> = {
78
72
  [Key in RefKey]?: RefValue<Target>;
79
73
  };
@@ -0,0 +1,33 @@
1
+ /** @import { Block } from '#client' */
2
+
3
+ import { composite } from './internal/client/composite.js';
4
+ import { with_block } from './internal/client/runtime.js';
5
+ import { tsrx_element } from './element.js';
6
+
7
+ /**
8
+ * @typedef {Function | string | null | undefined | false} DynamicTarget
9
+ * @typedef {{ is?: DynamicTarget, [key: string]: any }} DynamicProps
10
+ */
11
+
12
+ /**
13
+ * @param {DynamicProps} props
14
+ * @returns {import('./element.js').TSRXElement}
15
+ */
16
+ export function Dynamic(props) {
17
+ return tsrx_element(
18
+ /**
19
+ * @param {Node} anchor
20
+ * @param {Block | null} block
21
+ */
22
+ (anchor, block) => {
23
+ const render_dynamic = () =>
24
+ composite(() => /** @type {DynamicTarget} */ (props?.is), anchor, props || {}, 'is');
25
+
26
+ if (block !== null) {
27
+ with_block(block, render_dynamic);
28
+ } else {
29
+ render_dynamic();
30
+ }
31
+ },
32
+ );
33
+ }
@@ -0,0 +1,80 @@
1
+ import { is_void_element } from '@tsrx/core/runtime/html';
2
+ import { exclude_prop_from_object } from '@tsrx/core/runtime/language-helpers';
3
+ import {
4
+ escape,
5
+ get,
6
+ is_tsrx_element,
7
+ output_push,
8
+ render_component,
9
+ render_tsrx_element,
10
+ spread_attrs,
11
+ spread_inner_html,
12
+ } from './internal/server/index.js';
13
+ import { tsrx_element } from './element.js';
14
+
15
+ /**
16
+ * @param {any} value
17
+ * @returns {void}
18
+ */
19
+ function render_child(value) {
20
+ value = get(value);
21
+
22
+ if (is_tsrx_element(value)) {
23
+ render_tsrx_element(value);
24
+ } else if (Array.isArray(value)) {
25
+ for (const item of value) {
26
+ render_child(item);
27
+ }
28
+ } else if (value != null) {
29
+ output_push(escape(value));
30
+ }
31
+ }
32
+
33
+ /**
34
+ * @param {string} tag
35
+ * @param {Record<string, any>} props
36
+ * @returns {void}
37
+ */
38
+ function render_element(tag, props) {
39
+ output_push(`<${tag}`);
40
+ output_push(spread_attrs(props, undefined, 'is'));
41
+
42
+ if (is_void_element(tag)) {
43
+ output_push(' />');
44
+ return;
45
+ }
46
+
47
+ output_push('>');
48
+
49
+ const inner_html = spread_inner_html(props);
50
+ if (inner_html !== undefined) {
51
+ output_push(inner_html);
52
+ } else {
53
+ render_child(props.children);
54
+ }
55
+
56
+ output_push(`</${tag}>`);
57
+ }
58
+
59
+ /**
60
+ * @param {{ is?: Function | string | null | undefined | false, [key: string]: any }} props
61
+ * @returns {import('./element.js').TSRXElement}
62
+ */
63
+ export function Dynamic(props) {
64
+ return tsrx_element(() => {
65
+ const component = get(props?.is);
66
+ if (component == null || component === false) {
67
+ return;
68
+ }
69
+
70
+ const dynamic_props = props || {};
71
+
72
+ if (typeof component === 'function') {
73
+ render_component(component, exclude_prop_from_object(dynamic_props, 'is'));
74
+ } else if (is_tsrx_element(component)) {
75
+ throw new TypeError('Invalid component type: received a TSRXElement value.');
76
+ } else {
77
+ render_element(String(component), dynamic_props);
78
+ }
79
+ });
80
+ }
@@ -1,4 +1,4 @@
1
- /** @import { CompatOptions, RootBoundaryOptions } from '#client' */
1
+ /** @import { RootBoundaryOptions } from '#client' */
2
2
 
3
3
  import { destroy_block, root } from './internal/client/blocks.js';
4
4
  import { handle_root_events } from './internal/client/events.js';
@@ -29,14 +29,6 @@ export {
29
29
  SUSPENSE_REJECTED,
30
30
  } from './internal/client/constants.js';
31
31
 
32
- /**
33
- * @returns {CompatOptions | undefined}
34
- */
35
- function get_default_compat() {
36
- return /** @type {typeof globalThis & { __RIPPLE_COMPAT__?: CompatOptions }} */ (globalThis)
37
- .__RIPPLE_COMPAT__;
38
- }
39
-
40
32
  /**
41
33
  * @param {Node} anchor
42
34
  * @param {(anchor: Node) => void} render_content
@@ -74,7 +66,6 @@ export function mount(component, options) {
74
66
  init_operations();
75
67
  remove_ssr_css();
76
68
 
77
- const compat = get_default_compat();
78
69
  const props = options.props || {};
79
70
  const target = options.target;
80
71
  const anchor = create_anchor();
@@ -96,7 +87,7 @@ export function mount(component, options) {
96
87
  },
97
88
  options.rootBoundary,
98
89
  );
99
- }, compat);
90
+ });
100
91
 
101
92
  return () => {
102
93
  cleanup_events();
@@ -113,7 +104,6 @@ export function hydrate(component, options) {
113
104
  init_operations();
114
105
  remove_ssr_css();
115
106
 
116
- const compat = get_default_compat();
117
107
  const props = options.props || {};
118
108
  const target = options.target;
119
109
  const was_hydrating = hydrating;
@@ -142,7 +132,7 @@ export function hydrate(component, options) {
142
132
  },
143
133
  options.rootBoundary,
144
134
  );
145
- }, compat);
135
+ });
146
136
  } catch (e) {
147
137
  throw e;
148
138
  } finally {
@@ -193,6 +183,8 @@ export { user_effect as effect } from './internal/client/blocks.js';
193
183
 
194
184
  export { Portal } from './internal/client/portal.js';
195
185
 
186
+ export { Dynamic } from './dynamic-client.js';
187
+
196
188
  export { ref_prop as createRefKey } from './internal/client/runtime.js';
197
189
 
198
190
  export { isRefProp } from '@tsrx/core/runtime/ref';
@@ -68,6 +68,8 @@ export const bindNode = noop;
68
68
  export const bindOffsetWidth = noop;
69
69
  export const bindOffsetHeight = noop;
70
70
 
71
+ export { Dynamic } from './dynamic-server.js';
72
+
71
73
  /**
72
74
  * Portal component noop for server-side rendering.
73
75
  * Portals are client-only and do not render on the server.
@@ -1,4 +1,4 @@
1
- /** @import { Block, Derived, CompatOptions, Component } from '#client' */
1
+ /** @import { Block, Derived, Component } from '#client' */
2
2
 
3
3
  import {
4
4
  BLOCK_HAS_RUN,
@@ -85,9 +85,10 @@ export function render(fn, state, flags = 0) {
85
85
  * @param {any} element
86
86
  * @param {any} fn
87
87
  * @param {number} [flags]
88
+ * @param {string} [exclude_prop]
88
89
  */
89
- export function render_spread(element, fn, flags = 0) {
90
- return block(RENDER_BLOCK | flags, apply_element_spread(element, fn));
90
+ export function render_spread(element, fn, flags = 0, exclude_prop) {
91
+ return block(RENDER_BLOCK | flags, apply_element_spread(element, fn, exclude_prop));
91
92
  }
92
93
 
93
94
  /**
@@ -172,32 +173,10 @@ export function ref(element, get_fn, set_fn) {
172
173
 
173
174
  /**
174
175
  * @param {() => (void | (() => void))} fn
175
- * @param {CompatOptions} [compat]
176
176
  * @returns {Block}
177
177
  */
178
- export function root(fn, compat) {
179
- var target_fn = fn;
180
-
181
- if (compat != null) {
182
- /** @type {Array<void | (() => void)>} */
183
- var unmounts = [];
184
- for (var key in compat) {
185
- var api = compat[key];
186
- unmounts.push(api.createRoot());
187
- }
188
- target_fn = () => {
189
- var component_unmount = fn();
190
-
191
- return () => {
192
- component_unmount?.();
193
- for (var unmount of unmounts) {
194
- unmount?.();
195
- }
196
- };
197
- };
198
- }
199
-
200
- return block(ROOT_BLOCK, target_fn, { compat, start: null, end: null }, create_component_ctx());
178
+ export function root(fn) {
179
+ return block(ROOT_BLOCK, fn, { start: null, end: null }, create_component_ctx());
201
180
  }
202
181
 
203
182
  /**
@@ -1,5 +1,6 @@
1
1
  /** @import { Block } from '#client' */
2
2
 
3
+ import { exclude_prop_from_object } from '@tsrx/core/runtime/language-helpers';
3
4
  import { branch, destroy_block, render, render_spread } from './blocks.js';
4
5
  import { COMPOSITE_BLOCK, DEFAULT_NAMESPACE, NAMESPACE_URI } from './constants.js';
5
6
  import { hydrate_next, hydrating } from './hydration.js';
@@ -9,13 +10,14 @@ import { is_tsrx_element } from '../../element.js';
9
10
  import { render_component } from './component.js';
10
11
 
11
12
  /**
12
- * @typedef {((anchor: Node, props: Record<string, any>, block: Block | null) => void)} ComponentFunction
13
- * @param {() => ComponentFunction | keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap | keyof MathMLElementTagNameMap} get_component
13
+ * @typedef {Function | string | null | undefined | false} CompositeTarget
14
+ * @param {() => CompositeTarget} get_component
14
15
  * @param {Node} node
15
16
  * @param {Record<string, any>} props
17
+ * @param {string} [exclude_prop]
16
18
  * @returns {void}
17
19
  */
18
- export function composite(get_component, node, props) {
20
+ export function composite(get_component, node, props, exclude_prop) {
19
21
  if (hydrating) {
20
22
  // During hydration, `node` may already point at the first real SSR node
21
23
  // (e.g. layout children). Only skip forward when we are on an empty
@@ -42,7 +44,10 @@ export function composite(get_component, node, props) {
42
44
  if (typeof component === 'function') {
43
45
  // Handle as regular component
44
46
  b = branch(() => {
45
- render_component(component, anchor, props);
47
+ const component_props = exclude_prop
48
+ ? exclude_prop_from_object(props, exclude_prop)
49
+ : props;
50
+ render_component(component, anchor, component_props);
46
51
  });
47
52
  } else if (is_tsrx_element(component)) {
48
53
  throw new TypeError('Invalid component type: received a TSRXElement value.');
@@ -69,7 +74,7 @@ export function composite(get_component, node, props) {
69
74
  };
70
75
  }
71
76
 
72
- render_spread(element, () => props || {});
77
+ render_spread(element, () => props || {}, 0, exclude_prop);
73
78
 
74
79
  if (is_tsrx_element(props?.children)) {
75
80
  var child_anchor = document.createComment('');
@@ -84,7 +89,7 @@ export function composite(get_component, node, props) {
84
89
  };
85
90
 
86
91
  if (ns !== active_namespace) {
87
- // support top-level dynamic element svg/math <@tag />
92
+ // support top-level dynamic element svg/math tags
88
93
  b = branch(() => with_ns(ns, run));
89
94
  } else {
90
95
  b = branch(run);
@@ -57,6 +57,17 @@ function create_item(anchor, value, index, render_fn, is_indexed, is_keyed) {
57
57
  return b;
58
58
  }
59
59
 
60
+ /**
61
+ * @param {Node} anchor
62
+ * @param {(anchor: Node) => void} render_empty
63
+ * @returns {Block}
64
+ */
65
+ function create_empty(anchor, render_empty) {
66
+ return branch(() => {
67
+ render_empty(anchor);
68
+ });
69
+ }
70
+
60
71
  /**
61
72
  * @param {Block} block
62
73
  * @param {Element} anchor
@@ -104,9 +115,10 @@ function collection_to_array(collection) {
104
115
  * @param {() => V[] | Iterable<V>} get_collection
105
116
  * @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
106
117
  * @param {number} flags
118
+ * @param {(anchor: Node) => void} [render_empty]
107
119
  * @returns {void}
108
120
  */
109
- export function for_block(node, get_collection, render_fn, flags) {
121
+ export function for_block(node, get_collection, render_fn, flags, render_empty) {
110
122
  var is_controlled = (flags & IS_CONTROLLED) !== 0;
111
123
  var is_indexed = (flags & IS_INDEXED) !== 0;
112
124
  var anchor = /** @type {Element | Text} */ (node);
@@ -131,7 +143,7 @@ export function for_block(node, get_collection, render_fn, flags) {
131
143
  var array = collection_to_array(collection);
132
144
 
133
145
  untrack(() => {
134
- reconcile_by_ref(anchor, block, array, render_fn, is_controlled, is_indexed);
146
+ reconcile_by_ref(anchor, block, array, render_fn, is_controlled, is_indexed, render_empty);
135
147
  });
136
148
 
137
149
  if (hydrating) {
@@ -151,9 +163,10 @@ export function for_block(node, get_collection, render_fn, flags) {
151
163
  * @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
152
164
  * @param {number} flags
153
165
  * @param {(item: V) => K} [get_key]
166
+ * @param {(anchor: Node) => void} [render_empty]
154
167
  * @returns {void}
155
168
  */
156
- export function for_block_keyed(node, get_collection, render_fn, flags, get_key) {
169
+ export function for_block_keyed(node, get_collection, render_fn, flags, get_key, render_empty) {
157
170
  var is_controlled = (flags & IS_CONTROLLED) !== 0;
158
171
  var is_indexed = (flags & IS_INDEXED) !== 0;
159
172
  var anchor = /** @type {Element | Text} */ (node);
@@ -188,6 +201,7 @@ export function for_block_keyed(node, get_collection, render_fn, flags, get_key)
188
201
  is_controlled,
189
202
  is_indexed,
190
203
  /** @type {(item: V) => K} */ (get_key),
204
+ render_empty,
191
205
  );
192
206
  });
193
207
  },
@@ -211,6 +225,7 @@ function reconcile_fast_clear(anchor, block, array) {
211
225
  parent_node.append(anchor);
212
226
  state.array = array;
213
227
  state.blocks = [];
228
+ state.empty = null;
214
229
  }
215
230
 
216
231
  /**
@@ -241,9 +256,19 @@ function update_value(block, value) {
241
256
  * @param {boolean} is_controlled
242
257
  * @param {boolean} is_indexed
243
258
  * @param {(item: V) => K} get_key
259
+ * @param {(anchor: Node) => void} [render_empty]
244
260
  * @returns {void}
245
261
  */
246
- function reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed, get_key) {
262
+ function reconcile_by_key(
263
+ anchor,
264
+ block,
265
+ b,
266
+ render_fn,
267
+ is_controlled,
268
+ is_indexed,
269
+ get_key,
270
+ render_empty,
271
+ ) {
247
272
  var state = block.s;
248
273
 
249
274
  // Variables used in conditional branches - declare with initial values
@@ -271,6 +296,7 @@ function reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed
271
296
  array: [],
272
297
  blocks: [],
273
298
  keys: null,
299
+ empty: null,
274
300
  };
275
301
  }
276
302
 
@@ -279,6 +305,30 @@ function reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed
279
305
  var b_length = b.length;
280
306
  var j = 0;
281
307
 
308
+ if (b_length === 0) {
309
+ if (a_length > 0) {
310
+ if (is_controlled) {
311
+ reconcile_fast_clear(anchor, block, b);
312
+ } else {
313
+ for (; j < a_length; j++) {
314
+ destroy_block(state.blocks[j]);
315
+ }
316
+ state.array = b;
317
+ state.blocks = [];
318
+ state.keys = [];
319
+ }
320
+ }
321
+ if (render_empty && state.empty === null) {
322
+ state.empty = create_empty(anchor, render_empty);
323
+ }
324
+ return;
325
+ }
326
+
327
+ if (state.empty !== null) {
328
+ destroy_block(state.empty);
329
+ state.empty = null;
330
+ }
331
+
282
332
  // Fast-path for clear
283
333
  if (is_controlled && b_length === 0) {
284
334
  if (a_length > 0) {
@@ -516,9 +566,10 @@ function reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed
516
566
  * @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
517
567
  * @param {boolean} is_controlled
518
568
  * @param {boolean} is_indexed
569
+ * @param {(anchor: Node) => void} [render_empty]
519
570
  * @returns {void}
520
571
  */
521
- function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed) {
572
+ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed, render_empty) {
522
573
  var state = block.s;
523
574
 
524
575
  // Variables used in conditional branches - declare with initial values
@@ -546,6 +597,7 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
546
597
  array: [],
547
598
  blocks: [],
548
599
  keys: null,
600
+ empty: null,
549
601
  };
550
602
  }
551
603
 
@@ -554,6 +606,29 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
554
606
  var b_length = b.length;
555
607
  var j = 0;
556
608
 
609
+ if (b_length === 0) {
610
+ if (a_length > 0) {
611
+ if (is_controlled) {
612
+ reconcile_fast_clear(anchor, block, b);
613
+ } else {
614
+ for (; j < a_length; j++) {
615
+ destroy_block(state.blocks[j]);
616
+ }
617
+ state.array = b;
618
+ state.blocks = [];
619
+ }
620
+ }
621
+ if (render_empty && state.empty === null) {
622
+ state.empty = create_empty(anchor, render_empty);
623
+ }
624
+ return;
625
+ }
626
+
627
+ if (state.empty !== null) {
628
+ destroy_block(state.empty);
629
+ state.empty = null;
630
+ }
631
+
557
632
  // Fast-path for clear
558
633
  if (is_controlled && b_length === 0) {
559
634
  if (a_length > 0) {
@@ -127,8 +127,6 @@ export { expression } from './expression.js';
127
127
 
128
128
  export { rpc } from './rpc.js';
129
129
 
130
- export { tsx_compat } from './compat.js';
131
-
132
130
  export { render_tsrx_element } from './component.js';
133
131
 
134
132
  export { TRY_BLOCK, HMR } from './constants.js';
@@ -253,9 +253,10 @@ export function set_selected(element, selected) {
253
253
  /**
254
254
  * @param {Element} element
255
255
  * @param {() => Record<string | symbol, any>} fn
256
+ * @param {string} [exclude_prop]
256
257
  * @returns {() => void}
257
258
  */
258
- export function apply_element_spread(element, fn) {
259
+ export function apply_element_spread(element, fn, exclude_prop) {
259
260
  /** @type {Record<string | symbol, any>} */
260
261
  var prev = {};
261
262
  /** @type {Record<string | symbol, Block | undefined>} */
@@ -304,6 +305,8 @@ export function apply_element_spread(element, fn) {
304
305
  var current_ref_props = {};
305
306
 
306
307
  for (const key in next) {
308
+ if (key === exclude_prop) continue;
309
+
307
310
  const ref_fn = next[key];
308
311
  if (!is_ref_prop(ref_fn)) {
309
312
  continue;
@@ -352,7 +355,7 @@ export function apply_element_spread(element, fn) {
352
355
  /** @type {typeof prev} */
353
356
  const current = {};
354
357
  for (const key in next) {
355
- if (key === 'children') continue;
358
+ if (key === 'children' || key === exclude_prop) continue;
356
359
 
357
360
  let value = next[key];
358
361
  if (is_ref_prop(value)) {