ripple 0.3.12 → 0.3.14

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 (217) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/package.json +11 -30
  3. package/src/compiler/types/import.d.ts +0 -12
  4. package/src/helpers.d.ts +2 -0
  5. package/src/runtime/array.js +38 -38
  6. package/src/runtime/create-subscriber.js +2 -2
  7. package/src/runtime/index-client.js +15 -13
  8. package/src/runtime/index-server.js +18 -11
  9. package/src/runtime/internal/client/bindings.js +4 -6
  10. package/src/runtime/internal/client/blocks.js +19 -23
  11. package/src/runtime/internal/client/constants.js +20 -9
  12. package/src/runtime/internal/client/events.js +8 -3
  13. package/src/runtime/internal/client/hmr.js +5 -17
  14. package/src/runtime/internal/client/index.js +14 -4
  15. package/src/runtime/internal/client/runtime.js +436 -173
  16. package/src/runtime/internal/client/try.js +334 -156
  17. package/src/runtime/internal/client/types.d.ts +26 -0
  18. package/src/runtime/internal/server/blocks.js +181 -0
  19. package/src/runtime/internal/server/constants.js +7 -0
  20. package/src/runtime/internal/server/index.js +774 -150
  21. package/src/runtime/internal/server/types.d.ts +35 -0
  22. package/src/runtime/media-query.js +34 -33
  23. package/src/runtime/object.js +7 -10
  24. package/src/runtime/proxy.js +2 -3
  25. package/src/runtime/reactive-value.js +23 -21
  26. package/src/server/index.js +1 -1
  27. package/src/utils/ast.js +1 -1
  28. package/src/utils/async.js +35 -0
  29. package/src/utils/attributes.js +43 -0
  30. package/src/utils/builders.js +5 -3
  31. package/tests/client/__snapshots__/computed-properties.test.rsrx.snap +49 -0
  32. package/tests/client/__snapshots__/for.test.rsrx.snap +319 -0
  33. package/tests/client/__snapshots__/html.test.rsrx.snap +40 -0
  34. package/tests/client/_etc.test.rsrx +7 -0
  35. package/tests/client/array/{array.static.test.ripple → array.static.test.rsrx} +18 -20
  36. package/tests/client/async-suspend.test.rsrx +662 -0
  37. package/tests/client/basic/__snapshots__/basic.attributes.test.rsrx.snap +60 -0
  38. package/tests/client/basic/__snapshots__/basic.rendering.test.rsrx.snap +59 -0
  39. package/tests/client/basic/{basic.errors.test.ripple → basic.errors.test.rsrx} +3 -3
  40. package/tests/client/basic/{basic.styling.test.ripple → basic.styling.test.rsrx} +1 -1
  41. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +12 -0
  42. package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +46 -0
  43. package/tests/client/compiler/{compiler.assignments.test.ripple → compiler.assignments.test.rsrx} +1 -1
  44. package/tests/client/compiler/{compiler.attributes.test.ripple → compiler.attributes.test.rsrx} +1 -1
  45. package/tests/client/compiler/{compiler.basic.test.ripple → compiler.basic.test.rsrx} +13 -13
  46. package/tests/client/compiler/{compiler.tracked-access.test.ripple → compiler.tracked-access.test.rsrx} +1 -1
  47. package/tests/client/compiler/{compiler.try-in-function.test.ripple → compiler.try-in-function.test.rsrx} +9 -7
  48. package/tests/client/compiler/{compiler.typescript.test.ripple → compiler.typescript.test.rsrx} +1 -1
  49. package/tests/client/composite/__snapshots__/composite.render.test.rsrx.snap +37 -0
  50. package/tests/client/css/{global-additional-cases.test.ripple → global-additional-cases.test.rsrx} +1 -1
  51. package/tests/client/css/{global-advanced-selectors.test.ripple → global-advanced-selectors.test.rsrx} +1 -1
  52. package/tests/client/css/{global-at-rules.test.ripple → global-at-rules.test.rsrx} +1 -1
  53. package/tests/client/css/{global-basic.test.ripple → global-basic.test.rsrx} +1 -1
  54. package/tests/client/css/{global-classes-ids.test.ripple → global-classes-ids.test.rsrx} +1 -1
  55. package/tests/client/css/{global-combinators.test.ripple → global-combinators.test.rsrx} +1 -1
  56. package/tests/client/css/{global-complex-nesting.test.ripple → global-complex-nesting.test.rsrx} +1 -1
  57. package/tests/client/css/{global-edge-cases.test.ripple → global-edge-cases.test.rsrx} +1 -1
  58. package/tests/client/css/{global-keyframes.test.ripple → global-keyframes.test.rsrx} +1 -1
  59. package/tests/client/css/{global-nested.test.ripple → global-nested.test.rsrx} +1 -1
  60. package/tests/client/css/{global-pseudo.test.ripple → global-pseudo.test.rsrx} +1 -1
  61. package/tests/client/css/{global-scoping.test.ripple → global-scoping.test.rsrx} +1 -1
  62. package/tests/client/css/{style-identifier.test.ripple → style-identifier.test.rsrx} +1 -1
  63. package/tests/client/{function-overload.test.ripple → function-overload.test.rsrx} +1 -1
  64. package/tests/client/{return.test.ripple → return.test.rsrx} +1 -1
  65. package/tests/client/try.test.rsrx +1702 -0
  66. package/tests/hydration/build-components.js +6 -4
  67. package/tests/hydration/compiled/client/head.js +11 -11
  68. package/tests/hydration/compiled/client/mixed-control-flow.js +55 -70
  69. package/tests/hydration/compiled/client/nested-control-flow.js +72 -88
  70. package/tests/hydration/compiled/client/try.js +42 -54
  71. package/tests/hydration/compiled/server/basic.js +491 -369
  72. package/tests/hydration/compiled/server/composite.js +153 -128
  73. package/tests/hydration/compiled/server/events.js +166 -145
  74. package/tests/hydration/compiled/server/for.js +821 -677
  75. package/tests/hydration/compiled/server/head.js +200 -165
  76. package/tests/hydration/compiled/server/hmr.js +62 -54
  77. package/tests/hydration/compiled/server/html-in-template.js +64 -55
  78. package/tests/hydration/compiled/server/html.js +1477 -1360
  79. package/tests/hydration/compiled/server/if-children.js +448 -408
  80. package/tests/hydration/compiled/server/if.js +204 -171
  81. package/tests/hydration/compiled/server/mixed-control-flow.js +237 -195
  82. package/tests/hydration/compiled/server/nested-control-flow.js +533 -467
  83. package/tests/hydration/compiled/server/portal.js +94 -107
  84. package/tests/hydration/compiled/server/reactivity.js +87 -64
  85. package/tests/hydration/compiled/server/return.js +1424 -1174
  86. package/tests/hydration/compiled/server/switch.js +268 -238
  87. package/tests/hydration/compiled/server/try.js +98 -87
  88. package/tests/hydration/components/{mixed-control-flow.ripple → mixed-control-flow.rsrx} +2 -2
  89. package/tests/hydration/components/{try.ripple → try.rsrx} +4 -2
  90. package/tests/hydration/mixed-control-flow.test.js +14 -0
  91. package/tests/hydration/nested-control-flow.test.js +50 -48
  92. package/tests/hydration/try.test.js +25 -0
  93. package/tests/server/__snapshots__/compiler.test.ripple.snap +0 -32
  94. package/tests/server/__snapshots__/compiler.test.rsrx.snap +95 -0
  95. package/tests/server/{compiler.test.ripple → compiler.test.rsrx} +0 -17
  96. package/tests/server/{html-nesting-validation.test.ripple → html-nesting-validation.test.rsrx} +3 -3
  97. package/tests/server/streaming-ssr.test.rsrx +115 -0
  98. package/tests/server/{style-identifier.test.ripple → style-identifier.test.rsrx} +1 -1
  99. package/tests/server/try.test.rsrx +503 -0
  100. package/tests/setup-server.js +1 -1
  101. package/tests/utils/compiler-compat-config.test.js +4 -4
  102. package/tests/utils/vite-plugin-config.test.js +1 -1
  103. package/tests/utils/vite-plugin-hmr.test.js +5 -5
  104. package/tsconfig.json +2 -0
  105. package/types/index.d.ts +13 -23
  106. package/types/server.d.ts +43 -16
  107. package/src/compiler/comment-utils.js +0 -91
  108. package/src/compiler/errors.js +0 -77
  109. package/src/compiler/identifier-utils.js +0 -80
  110. package/src/compiler/index.d.ts +0 -127
  111. package/src/compiler/index.js +0 -89
  112. package/src/compiler/phases/1-parse/index.js +0 -2964
  113. package/src/compiler/phases/1-parse/style.js +0 -704
  114. package/src/compiler/phases/2-analyze/css-analyze.js +0 -160
  115. package/src/compiler/phases/2-analyze/index.js +0 -2238
  116. package/src/compiler/phases/2-analyze/prune.js +0 -1131
  117. package/src/compiler/phases/2-analyze/validation.js +0 -168
  118. package/src/compiler/phases/3-transform/client/index.js +0 -5301
  119. package/src/compiler/phases/3-transform/segments.js +0 -2129
  120. package/src/compiler/phases/3-transform/server/index.js +0 -1899
  121. package/src/compiler/phases/3-transform/stylesheet.js +0 -545
  122. package/src/compiler/scope.js +0 -476
  123. package/src/compiler/source-map-utils.js +0 -358
  124. package/src/compiler/types/acorn.d.ts +0 -11
  125. package/src/compiler/types/estree-jsx.d.ts +0 -11
  126. package/src/compiler/types/estree.d.ts +0 -11
  127. package/src/compiler/types/index.d.ts +0 -1404
  128. package/src/compiler/types/parse.d.ts +0 -1721
  129. package/src/compiler/utils.js +0 -1263
  130. package/tests/client/_etc.test.ripple +0 -5
  131. package/tests/client/async-suspend.test.ripple +0 -94
  132. package/tests/client/try.test.ripple +0 -196
  133. package/tests/server/streaming-ssr.test.ripple +0 -68
  134. package/tests/server/try.test.ripple +0 -82
  135. /package/tests/client/array/{array.copy-within.test.ripple → array.copy-within.test.rsrx} +0 -0
  136. /package/tests/client/array/{array.derived.test.ripple → array.derived.test.rsrx} +0 -0
  137. /package/tests/client/array/{array.iteration.test.ripple → array.iteration.test.rsrx} +0 -0
  138. /package/tests/client/array/{array.mutations.test.ripple → array.mutations.test.rsrx} +0 -0
  139. /package/tests/client/array/{array.to-methods.test.ripple → array.to-methods.test.rsrx} +0 -0
  140. /package/tests/client/basic/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
  141. /package/tests/client/basic/{basic.collections.test.ripple → basic.collections.test.rsrx} +0 -0
  142. /package/tests/client/basic/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
  143. /package/tests/client/basic/{basic.events.test.ripple → basic.events.test.rsrx} +0 -0
  144. /package/tests/client/basic/{basic.get-set.test.ripple → basic.get-set.test.rsrx} +0 -0
  145. /package/tests/client/basic/{basic.hmr.test.ripple → basic.hmr.test.rsrx} +0 -0
  146. /package/tests/client/basic/{basic.reactivity.test.ripple → basic.reactivity.test.rsrx} +0 -0
  147. /package/tests/client/basic/{basic.rendering.test.ripple → basic.rendering.test.rsrx} +0 -0
  148. /package/tests/client/basic/{basic.utilities.test.ripple → basic.utilities.test.rsrx} +0 -0
  149. /package/tests/client/{boundaries.test.ripple → boundaries.test.rsrx} +0 -0
  150. /package/tests/client/compiler/{compiler.regex.test.ripple → compiler.regex.test.rsrx} +0 -0
  151. /package/tests/client/composite/{composite.dynamic-components.test.ripple → composite.dynamic-components.test.rsrx} +0 -0
  152. /package/tests/client/composite/{composite.generics.test.ripple → composite.generics.test.rsrx} +0 -0
  153. /package/tests/client/composite/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
  154. /package/tests/client/composite/{composite.reactivity.test.ripple → composite.reactivity.test.rsrx} +0 -0
  155. /package/tests/client/composite/{composite.render.test.ripple → composite.render.test.rsrx} +0 -0
  156. /package/tests/client/{computed-properties.test.ripple → computed-properties.test.rsrx} +0 -0
  157. /package/tests/client/{context.test.ripple → context.test.rsrx} +0 -0
  158. /package/tests/client/{date.test.ripple → date.test.rsrx} +0 -0
  159. /package/tests/client/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
  160. /package/tests/client/{events.test.ripple → events.test.rsrx} +0 -0
  161. /package/tests/client/{for.test.ripple → for.test.rsrx} +0 -0
  162. /package/tests/client/{function-overload-import.ripple → function-overload-import.rsrx} +0 -0
  163. /package/tests/client/{head.test.ripple → head.test.rsrx} +0 -0
  164. /package/tests/client/{html.test.ripple → html.test.rsrx} +0 -0
  165. /package/tests/client/{input-value.test.ripple → input-value.test.rsrx} +0 -0
  166. /package/tests/client/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
  167. /package/tests/client/{map.test.ripple → map.test.rsrx} +0 -0
  168. /package/tests/client/{media-query.test.ripple → media-query.test.rsrx} +0 -0
  169. /package/tests/client/{object.test.ripple → object.test.rsrx} +0 -0
  170. /package/tests/client/{portal.test.ripple → portal.test.rsrx} +0 -0
  171. /package/tests/client/{ref.test.ripple → ref.test.rsrx} +0 -0
  172. /package/tests/client/{set.test.ripple → set.test.rsrx} +0 -0
  173. /package/tests/client/{svg.test.ripple → svg.test.rsrx} +0 -0
  174. /package/tests/client/{switch.test.ripple → switch.test.rsrx} +0 -0
  175. /package/tests/client/{tsx.test.ripple → tsx.test.rsrx} +0 -0
  176. /package/tests/client/{typescript-generics.test.ripple → typescript-generics.test.rsrx} +0 -0
  177. /package/tests/client/url/{url.derived.test.ripple → url.derived.test.rsrx} +0 -0
  178. /package/tests/client/url/{url.parsing.test.ripple → url.parsing.test.rsrx} +0 -0
  179. /package/tests/client/url/{url.partial-removal.test.ripple → url.partial-removal.test.rsrx} +0 -0
  180. /package/tests/client/url/{url.reactivity.test.ripple → url.reactivity.test.rsrx} +0 -0
  181. /package/tests/client/url/{url.serialization.test.ripple → url.serialization.test.rsrx} +0 -0
  182. /package/tests/client/url-search-params/{url-search-params.derived.test.ripple → url-search-params.derived.test.rsrx} +0 -0
  183. /package/tests/client/url-search-params/{url-search-params.initialization.test.ripple → url-search-params.initialization.test.rsrx} +0 -0
  184. /package/tests/client/url-search-params/{url-search-params.iteration.test.ripple → url-search-params.iteration.test.rsrx} +0 -0
  185. /package/tests/client/url-search-params/{url-search-params.mutation.test.ripple → url-search-params.mutation.test.rsrx} +0 -0
  186. /package/tests/client/url-search-params/{url-search-params.retrieval.test.ripple → url-search-params.retrieval.test.rsrx} +0 -0
  187. /package/tests/client/url-search-params/{url-search-params.serialization.test.ripple → url-search-params.serialization.test.rsrx} +0 -0
  188. /package/tests/client/url-search-params/{url-search-params.tracked-url.test.ripple → url-search-params.tracked-url.test.rsrx} +0 -0
  189. /package/tests/hydration/components/{basic.ripple → basic.rsrx} +0 -0
  190. /package/tests/hydration/components/{composite.ripple → composite.rsrx} +0 -0
  191. /package/tests/hydration/components/{events.ripple → events.rsrx} +0 -0
  192. /package/tests/hydration/components/{for.ripple → for.rsrx} +0 -0
  193. /package/tests/hydration/components/{head.ripple → head.rsrx} +0 -0
  194. /package/tests/hydration/components/{hmr.ripple → hmr.rsrx} +0 -0
  195. /package/tests/hydration/components/{html-in-template.ripple → html-in-template.rsrx} +0 -0
  196. /package/tests/hydration/components/{html.ripple → html.rsrx} +0 -0
  197. /package/tests/hydration/components/{if-children.ripple → if-children.rsrx} +0 -0
  198. /package/tests/hydration/components/{if.ripple → if.rsrx} +0 -0
  199. /package/tests/hydration/components/{nested-control-flow.ripple → nested-control-flow.rsrx} +0 -0
  200. /package/tests/hydration/components/{portal.ripple → portal.rsrx} +0 -0
  201. /package/tests/hydration/components/{reactivity.ripple → reactivity.rsrx} +0 -0
  202. /package/tests/hydration/components/{return.ripple → return.rsrx} +0 -0
  203. /package/tests/hydration/components/{switch.ripple → switch.rsrx} +0 -0
  204. /package/tests/server/{await.test.ripple → await.test.rsrx} +0 -0
  205. /package/tests/server/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
  206. /package/tests/server/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
  207. /package/tests/server/{basic.test.ripple → basic.test.rsrx} +0 -0
  208. /package/tests/server/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
  209. /package/tests/server/{composite.test.ripple → composite.test.rsrx} +0 -0
  210. /package/tests/server/{context.test.ripple → context.test.rsrx} +0 -0
  211. /package/tests/server/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
  212. /package/tests/server/{for.test.ripple → for.test.rsrx} +0 -0
  213. /package/tests/server/{head.test.ripple → head.test.rsrx} +0 -0
  214. /package/tests/server/{if.test.ripple → if.test.rsrx} +0 -0
  215. /package/tests/server/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
  216. /package/tests/server/{return.test.ripple → return.test.rsrx} +0 -0
  217. /package/tests/server/{switch.test.ripple → switch.test.rsrx} +0 -0
@@ -1,1263 +0,0 @@
1
- /**
2
- @import * as AST from 'estree';
3
- @import { CommonContext, NameSpace, ScopeInterface, Binding } from '#compiler';
4
- */
5
-
6
- import { build_assignment_value, extract_paths } from '../utils/ast.js';
7
- import * as b from '../utils/builders.js';
8
- import { is_capture_event, is_non_delegated, normalize_event_name } from '../utils/events.js';
9
-
10
- export { hash } from '../utils/hashing.js';
11
-
12
- const VOID_ELEMENT_NAMES = [
13
- 'area',
14
- 'base',
15
- 'br',
16
- 'col',
17
- 'command',
18
- 'embed',
19
- 'hr',
20
- 'img',
21
- 'input',
22
- 'keygen',
23
- 'link',
24
- 'meta',
25
- 'param',
26
- 'source',
27
- 'track',
28
- 'wbr',
29
- ];
30
-
31
- /**
32
- * Returns `true` if `name` is of a void element
33
- * @param {string} name
34
- */
35
- /**
36
- * Returns true if name is a void element
37
- * @param {string} name
38
- * @returns {boolean}
39
- */
40
- export function is_void_element(name) {
41
- return VOID_ELEMENT_NAMES.includes(name) || name.toLowerCase() === '!doctype';
42
- }
43
-
44
- const RESERVED_WORDS = [
45
- 'arguments',
46
- 'await',
47
- 'break',
48
- 'case',
49
- 'catch',
50
- 'class',
51
- 'const',
52
- 'continue',
53
- 'debugger',
54
- 'default',
55
- 'delete',
56
- 'do',
57
- 'else',
58
- 'enum',
59
- 'eval',
60
- 'export',
61
- 'extends',
62
- 'false',
63
- 'finally',
64
- 'for',
65
- 'function',
66
- 'if',
67
- 'implements',
68
- 'import',
69
- 'in',
70
- 'instanceof',
71
- 'interface',
72
- 'let',
73
- 'new',
74
- 'null',
75
- 'package',
76
- 'private',
77
- 'protected',
78
- 'public',
79
- 'return',
80
- 'static',
81
- 'super',
82
- 'switch',
83
- 'this',
84
- 'throw',
85
- 'true',
86
- 'try',
87
- 'typeof',
88
- 'var',
89
- 'void',
90
- 'while',
91
- 'with',
92
- 'yield',
93
- ];
94
-
95
- /**
96
- * Returns true if word is a reserved JS keyword
97
- * @param {string} word
98
- * @returns {boolean}
99
- */
100
- export function is_reserved(word) {
101
- return RESERVED_WORDS.includes(word);
102
- }
103
-
104
- /**
105
- * Attributes that are boolean, i.e. they are present or not present.
106
- */
107
- const DOM_BOOLEAN_ATTRIBUTES = [
108
- 'allowfullscreen',
109
- 'async',
110
- 'autofocus',
111
- 'autoplay',
112
- 'checked',
113
- 'controls',
114
- 'default',
115
- 'disabled',
116
- 'formnovalidate',
117
- 'hidden',
118
- 'indeterminate',
119
- 'inert',
120
- 'ismap',
121
- 'loop',
122
- 'multiple',
123
- 'muted',
124
- 'nomodule',
125
- 'novalidate',
126
- 'open',
127
- 'playsinline',
128
- 'readonly',
129
- 'required',
130
- 'reversed',
131
- 'seamless',
132
- 'selected',
133
- 'webkitdirectory',
134
- 'defer',
135
- 'disablepictureinpicture',
136
- 'disableremoteplayback',
137
- ];
138
-
139
- /**
140
- * Returns true if name is a boolean DOM attribute
141
- * @param {string} name
142
- * @returns {boolean}
143
- */
144
- export function is_boolean_attribute(name) {
145
- return DOM_BOOLEAN_ATTRIBUTES.includes(name);
146
- }
147
-
148
- const DOM_PROPERTIES = [
149
- ...DOM_BOOLEAN_ATTRIBUTES,
150
- 'formNoValidate',
151
- 'isMap',
152
- 'noModule',
153
- 'playsInline',
154
- 'readOnly',
155
- 'value',
156
- 'volume',
157
- 'defaultValue',
158
- 'defaultChecked',
159
- 'srcObject',
160
- 'noValidate',
161
- 'allowFullscreen',
162
- 'disablePictureInPicture',
163
- 'disableRemotePlayback',
164
- ];
165
-
166
- /** @type {Record<string, string>} */
167
- const RIPPLE_IMPORT_CALL_NAME = {
168
- RippleURL: 'ripple_url',
169
- RippleURLSearchParams: 'ripple_url_search_params',
170
- RippleDate: 'ripple_date',
171
- RippleMap: 'ripple_map',
172
- RippleSet: 'ripple_set',
173
- MediaQuery: 'media_query',
174
- Context: 'context',
175
- effect: 'effect',
176
- untrack: 'untrack',
177
- RippleArray: 'ripple_array',
178
- RippleObject: 'ripple_object',
179
- };
180
-
181
- /**
182
- * Returns true if name is a DOM property
183
- * @param {string} name
184
- * @returns {boolean}
185
- */
186
- export function is_dom_property(name) {
187
- return DOM_PROPERTIES.includes(name);
188
- }
189
-
190
- /**
191
- * Determines if an event handler can be delegated
192
- * @param {string} event_name
193
- * @param {AST.Node} handler
194
- * @param {CommonContext} context
195
- * @returns {boolean}
196
- */
197
- export function is_delegated_event(event_name, handler, context) {
198
- // Handle delegated event handlers. Bail out if not a delegated event.
199
- if (
200
- !handler ||
201
- is_capture_event(event_name) ||
202
- is_non_delegated(normalize_event_name(event_name)) ||
203
- (handler.type !== 'FunctionExpression' &&
204
- handler.type !== 'ArrowFunctionExpression' &&
205
- !is_declared_function_within_component(/** @type {AST.Identifier}*/ (handler), context))
206
- ) {
207
- return false;
208
- }
209
- return true;
210
- }
211
-
212
- /**
213
- * Returns true if context is inside a top-level await: inside component or module
214
- * @param {CommonContext} context
215
- * @returns {boolean}
216
- */
217
- export function is_top_level_await(context) {
218
- for (let i = context.path.length - 1; i >= 0; i -= 1) {
219
- const context_node = context.path[i];
220
- const type = context_node.type;
221
-
222
- if (context_node.type === 'Component') {
223
- return true;
224
- }
225
-
226
- if (
227
- type === 'FunctionExpression' ||
228
- type === 'ArrowFunctionExpression' ||
229
- type === 'FunctionDeclaration'
230
- ) {
231
- return false;
232
- }
233
- }
234
- return true;
235
- }
236
-
237
- /**
238
- * Returns true if context is inside a Component node
239
- * @param {CommonContext} context
240
- * @param {boolean} [includes_functions=false]
241
- * @returns {AST.Component | undefined}
242
- */
243
- export function is_inside_component(context, includes_functions = false) {
244
- for (let i = context.path.length - 1; i >= 0; i -= 1) {
245
- const context_node = context.path[i];
246
- const type = context_node.type;
247
-
248
- if (
249
- !includes_functions &&
250
- (type === 'FunctionExpression' ||
251
- type === 'ArrowFunctionExpression' ||
252
- type === 'FunctionDeclaration')
253
- ) {
254
- return;
255
- }
256
- if (context_node.type === 'Component') {
257
- return context_node;
258
- }
259
- }
260
- return;
261
- }
262
-
263
- /**
264
- * Returns true if context is inside a component-level function
265
- * @param {CommonContext} context
266
- * @returns {boolean}
267
- */
268
- export function is_component_level_function(context) {
269
- for (let i = context.path.length - 1; i >= 0; i -= 1) {
270
- const context_node = context.path[i];
271
- const type = context_node.type;
272
-
273
- if (
274
- type === 'BlockStatement' &&
275
- context_node.body.find((n) => /** @type {AST.Node} */ (n).type === 'Component')
276
- ) {
277
- return true;
278
- }
279
-
280
- if (
281
- type === 'FunctionExpression' ||
282
- type === 'ArrowFunctionExpression' ||
283
- type === 'FunctionDeclaration'
284
- ) {
285
- return false;
286
- }
287
- }
288
- return true;
289
- }
290
-
291
- /**
292
- * Returns the matched Ripple tracking call name
293
- * @param {AST.Expression | AST.Super} callee
294
- * @param {CommonContext} context
295
- * @returns {'track' | null}
296
- */
297
- export function is_ripple_track_call(callee, context) {
298
- // Super expressions cannot be Ripple track calls
299
- if (callee.type === 'Super') return null;
300
-
301
- if (callee.type === 'Identifier' && callee.name === 'track') {
302
- return is_ripple_import(callee, context) ? 'track' : null;
303
- }
304
-
305
- if (
306
- callee.type === 'MemberExpression' &&
307
- callee.object.type === 'Identifier' &&
308
- callee.property.type === 'Identifier' &&
309
- callee.property.name === 'track' &&
310
- !callee.computed &&
311
- is_ripple_import(callee, context)
312
- ) {
313
- return 'track';
314
- }
315
-
316
- return null;
317
- }
318
-
319
- /**
320
- * Returns true if context is inside a call expression
321
- * @param {CommonContext} context
322
- * @returns {boolean}
323
- */
324
- export function is_inside_call_expression(context) {
325
- for (let i = context.path.length - 1; i >= 0; i -= 1) {
326
- const context_node = context.path[i];
327
- const type = context_node.type;
328
-
329
- if (
330
- type === 'FunctionExpression' ||
331
- type === 'ArrowFunctionExpression' ||
332
- type === 'FunctionDeclaration'
333
- ) {
334
- return false;
335
- }
336
- if (type === 'CallExpression') {
337
- const callee = context_node.callee;
338
- if (is_ripple_track_call(callee, context)) {
339
- return false;
340
- }
341
- return true;
342
- }
343
- }
344
- return false;
345
- }
346
-
347
- /**
348
- * Returns true if node is a static value (Literal, ArrayExpression, etc)
349
- * @param {AST.Node} node
350
- * @returns {boolean}
351
- */
352
- export function is_value_static(node) {
353
- if (node.type === 'Literal') {
354
- return true;
355
- }
356
- if (node.type === 'ArrayExpression') {
357
- return true;
358
- }
359
- if (node.type === 'NewExpression') {
360
- if (node.callee.type === 'Identifier' && node.callee.name === 'Array') {
361
- return true;
362
- }
363
- return false;
364
- }
365
-
366
- return false;
367
- }
368
-
369
- /**
370
- * Returns true if callee is a Ripple import
371
- * @param {AST.Expression} callee
372
- * @param {CommonContext} context
373
- * @returns {boolean}
374
- */
375
- export function is_ripple_import(callee, context) {
376
- if (callee.type === 'Identifier') {
377
- const binding = context.state.scope.get(callee.name);
378
-
379
- return (
380
- binding?.declaration_kind === 'import' &&
381
- binding.initial !== null &&
382
- binding.initial.type === 'ImportDeclaration' &&
383
- binding.initial.source.type === 'Literal' &&
384
- binding.initial.source.value === 'ripple'
385
- );
386
- } else if (
387
- callee.type === 'MemberExpression' &&
388
- callee.object.type === 'Identifier' &&
389
- !callee.computed
390
- ) {
391
- const binding = context.state.scope.get(callee.object.name);
392
-
393
- return (
394
- binding?.declaration_kind === 'import' &&
395
- binding.initial !== null &&
396
- binding.initial.type === 'ImportDeclaration' &&
397
- binding.initial.source.type === 'Literal' &&
398
- binding.initial.source.value === 'ripple'
399
- );
400
- }
401
-
402
- return false;
403
- }
404
-
405
- /**
406
- * Returns true if node is a function declared within a component
407
- * @param {AST.Node} node
408
- * @param {CommonContext} context
409
- * @returns {boolean}
410
- */
411
- export function is_declared_function_within_component(node, context) {
412
- const component = context.path?.find((n) => n.type === 'Component');
413
-
414
- if (node.type === 'Identifier' && component) {
415
- const binding = context.state.scope.get(node.name);
416
- const component_scope = context.state.scopes.get(component);
417
-
418
- if (binding !== null && component_scope !== undefined) {
419
- if (
420
- binding.declaration_kind !== 'function' &&
421
- binding.initial?.type !== 'FunctionDeclaration' &&
422
- binding.initial?.type !== 'ArrowFunctionExpression' &&
423
- binding.initial?.type !== 'FunctionExpression'
424
- ) {
425
- return false;
426
- }
427
- /** @type {ScopeInterface | null} */
428
- let scope = binding.scope;
429
-
430
- while (scope !== null) {
431
- if (scope === component_scope) {
432
- return true;
433
- }
434
- scope = scope.parent;
435
- }
436
- }
437
- }
438
-
439
- return false;
440
- }
441
- /**
442
- * Visits and transforms an assignment expression
443
- * @param {AST.AssignmentExpression} node
444
- * @param {CommonContext} context
445
- * @param {Function} build_assignment
446
- * @returns {AST.Expression | AST.AssignmentExpression | null}
447
- */
448
- export function visit_assignment_expression(node, context, build_assignment) {
449
- if (
450
- node.left.type === 'ArrayPattern' ||
451
- node.left.type === 'ObjectPattern' ||
452
- node.left.type === 'RestElement'
453
- ) {
454
- const value = /** @type {AST.Expression} */ (context.visit(node.right));
455
- const should_cache = value.type !== 'Identifier';
456
- const rhs = should_cache ? b.id('$$value') : value;
457
-
458
- let changed = false;
459
-
460
- const assignments = extract_paths(node.left).map((path) => {
461
- const value = path.expression?.(rhs);
462
-
463
- let assignment = build_assignment('=', path.node, value, context);
464
- if (assignment !== null) changed = true;
465
-
466
- return (
467
- assignment ??
468
- b.assignment(
469
- '=',
470
- /** @type {AST.Pattern} */ (context.visit(path.node)),
471
- /** @type {AST.Expression} */ (context.visit(value)),
472
- )
473
- );
474
- });
475
-
476
- if (!changed) {
477
- // No change to output -> nothing to transform -> we can keep the original assignment
478
- return null;
479
- }
480
-
481
- const is_standalone = context.path.at(-1)?.type.endsWith('Statement');
482
- const sequence = b.sequence(assignments);
483
-
484
- if (!is_standalone) {
485
- // this is part of an expression, we need the sequence to end with the value
486
- sequence.expressions.push(rhs);
487
- }
488
-
489
- if (should_cache) {
490
- // the right hand side is a complex expression, wrap in an IIFE to cache it
491
- const iife = b.arrow([rhs], sequence);
492
-
493
- return b.call(iife, value);
494
- }
495
-
496
- return sequence;
497
- }
498
-
499
- if (node.left.type !== 'Identifier' && node.left.type !== 'MemberExpression') {
500
- throw new Error(`Unexpected assignment type ${node.left.type}`);
501
- }
502
-
503
- const transformed = build_assignment(node.operator, node.left, node.right, context);
504
-
505
- if (transformed === node.left) {
506
- return node;
507
- }
508
-
509
- return transformed;
510
- }
511
-
512
- /**
513
- * Builds an assignment node, possibly transforming for reactivity
514
- * @param {AST.AssignmentOperator} operator
515
- * @param {AST.Pattern} left
516
- * @param {AST.Expression} right
517
- * @param {CommonContext} context
518
- * @returns {AST.Expression | null}
519
- */
520
- export function build_assignment(operator, left, right, context) {
521
- let object = left;
522
-
523
- while (object.type === 'MemberExpression') {
524
- // @ts-expect-error
525
- object = object.object;
526
- }
527
-
528
- if (object.type !== 'Identifier') {
529
- return null;
530
- }
531
-
532
- const binding = context.state.scope.get(object.name);
533
- if (!binding) return null;
534
-
535
- const transform = binding.transform;
536
-
537
- // reassignment
538
- if (object === left || (left.type === 'MemberExpression' && left.computed && operator === '=')) {
539
- const assign_fn = transform?.assign;
540
- if (assign_fn) {
541
- let value = /** @type {AST.Expression} */ (
542
- context.visit(build_assignment_value(operator, left, right))
543
- );
544
-
545
- return assign_fn(object, value);
546
- }
547
- }
548
-
549
- return null;
550
- }
551
-
552
- const ATTR_REGEX = /[&"<]/g;
553
- const CONTENT_REGEX = /[&<]/g;
554
-
555
- /**
556
- * Escapes HTML special characters in a string
557
- * @param {string | number | bigint | boolean | RegExp | null | undefined} value
558
- * @param {boolean} [is_attr=false]
559
- * @returns {string}
560
- */
561
- export function escape_html(value, is_attr = false) {
562
- const str = String(value ?? '');
563
-
564
- const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX;
565
- pattern.lastIndex = 0;
566
-
567
- let escaped = '';
568
- let last = 0;
569
-
570
- while (pattern.test(str)) {
571
- const i = pattern.lastIndex - 1;
572
- const ch = str[i];
573
- escaped += str.substring(last, i) + (ch === '&' ? '&amp;' : ch === '"' ? '&quot;' : '&lt;');
574
- last = i + 1;
575
- }
576
-
577
- return escaped + str.substring(last);
578
- }
579
-
580
- /**
581
- * Returns true if node is a DOM element (not a component)
582
- * @param {AST.Node} node
583
- * @returns {boolean}
584
- */
585
- export function is_element_dom_element(node) {
586
- const id = /** @type {AST.Element} */ (node).id;
587
- return (
588
- id.type === 'Identifier' &&
589
- id.name[0].toLowerCase() === id.name[0] &&
590
- id.name !== 'children' &&
591
- !id.tracked
592
- );
593
- }
594
-
595
- /**
596
- * Returns true if element is a dynamic element
597
- * @param {AST.Element} node
598
- * @returns {boolean}
599
- */
600
- export function is_element_dynamic(node) {
601
- return is_id_dynamic(node.id);
602
- }
603
-
604
- /**
605
- * @param {AST.Identifier | AST.MemberExpression | AST.Literal} node
606
- * @returns {boolean}
607
- */
608
- function is_id_dynamic(node) {
609
- if (node.type === 'Identifier') {
610
- return !!node.tracked;
611
- }
612
-
613
- return false;
614
- }
615
-
616
- /**
617
- * Normalizes children nodes (merges adjacent text, removes empty)
618
- * @param {AST.Node[]} children
619
- * @param {CommonContext} context
620
- * @returns {AST.Node[]}
621
- */
622
- export function normalize_children(children, context) {
623
- /** @type {AST.Node[]} */
624
- const normalized = [];
625
-
626
- for (const node of children) {
627
- normalize_child(node, normalized, context);
628
- }
629
-
630
- for (let i = normalized.length - 1; i >= 0; i--) {
631
- const child = normalized[i];
632
- const prev_child = normalized[i - 1];
633
-
634
- if (
635
- (child.type === 'RippleExpression' || child.type === 'Text') &&
636
- (prev_child?.type === 'RippleExpression' || prev_child?.type === 'Text')
637
- ) {
638
- if (
639
- (child.type === 'RippleExpression' &&
640
- is_children_template_expression(child.expression, context.state.scope)) ||
641
- (prev_child.type === 'RippleExpression' &&
642
- is_children_template_expression(prev_child.expression, context.state.scope))
643
- ) {
644
- continue;
645
- }
646
-
647
- if (prev_child.type === 'Text' || child.type === 'Text') {
648
- prev_child.type = 'Text';
649
- }
650
- if (child.expression.type === 'Literal' && prev_child.expression.type === 'Literal') {
651
- prev_child.expression = b.literal(
652
- prev_child.expression.value + String(child.expression.value),
653
- );
654
- } else {
655
- prev_child.expression = b.binary(
656
- '+',
657
- prev_child.expression,
658
- b.call('String', child.expression),
659
- );
660
- }
661
- normalized.splice(i, 1);
662
- }
663
- }
664
-
665
- return normalized;
666
- }
667
-
668
- /**
669
- * @param {AST.Expression} expression
670
- * @returns {AST.Expression}
671
- */
672
- export function unwrap_template_expression(expression) {
673
- /** @type {AST.Expression} */
674
- let node = expression;
675
-
676
- while (true) {
677
- if (
678
- node.type === 'ParenthesizedExpression' ||
679
- node.type === 'TSAsExpression' ||
680
- node.type === 'TSSatisfiesExpression' ||
681
- node.type === 'TSNonNullExpression' ||
682
- node.type === 'TSInstantiationExpression'
683
- ) {
684
- node = /** @type {AST.Expression} */ (node.expression);
685
- continue;
686
- }
687
-
688
- if (node.type === 'ChainExpression') {
689
- node = /** @type {AST.Expression} */ (node.expression);
690
- continue;
691
- }
692
-
693
- break;
694
- }
695
-
696
- return node;
697
- }
698
-
699
- /**
700
- * @param {AST.Expression} expression
701
- * @param {ScopeInterface | null | undefined} scope
702
- * @param {ScopeInterface | null} [component_scope]
703
- * @returns {boolean}
704
- */
705
- export function is_children_template_expression(expression, scope, component_scope = null) {
706
- if (scope == null) {
707
- return false;
708
- }
709
-
710
- const unwrapped = unwrap_template_expression(expression);
711
-
712
- if (unwrapped.type === 'MemberExpression') {
713
- let property_name = null;
714
-
715
- if (!unwrapped.computed && unwrapped.property.type === 'Identifier') {
716
- property_name = unwrapped.property.name;
717
- } else if (
718
- unwrapped.computed &&
719
- unwrapped.property.type === 'Literal' &&
720
- typeof unwrapped.property.value === 'string'
721
- ) {
722
- property_name = unwrapped.property.value;
723
- }
724
-
725
- if (property_name === 'children') {
726
- const target = unwrap_template_expression(/** @type {AST.Expression} */ (unwrapped.object));
727
-
728
- if (target.type === 'Identifier') {
729
- const binding = scope.get(target.name);
730
- return (
731
- binding?.declaration_kind === 'param' &&
732
- (component_scope === null || binding.scope === component_scope)
733
- );
734
- }
735
- }
736
- }
737
-
738
- if (unwrapped.type !== 'Identifier' || unwrapped.name !== 'children') {
739
- return false;
740
- }
741
-
742
- const binding = scope.get(unwrapped.name);
743
- return (
744
- (binding?.declaration_kind === 'param' ||
745
- binding?.kind === 'prop' ||
746
- binding?.kind === 'prop_fallback' ||
747
- binding?.kind === 'lazy' ||
748
- binding?.kind === 'lazy_fallback') &&
749
- (component_scope === null || binding.scope === component_scope)
750
- );
751
- }
752
-
753
- /**
754
- * @param {AST.Node} node
755
- * @param {AST.Node[]} normalized
756
- * @param {CommonContext} context
757
- */
758
- function normalize_child(node, normalized, context) {
759
- if (node.type === 'EmptyStatement') {
760
- return;
761
- } else if (
762
- node.type === 'Element' &&
763
- node.id.type === 'Identifier' &&
764
- ((node.id.name === 'style' &&
765
- !context.state.inside_head &&
766
- !context.state.keep_component_style) ||
767
- node.id.name === 'head' ||
768
- (node.id.name === 'title' && context.state.inside_head))
769
- ) {
770
- return;
771
- } else {
772
- normalized.push(node);
773
- }
774
- }
775
-
776
- /**
777
- * Replaces any lazy subpatterns in a parameter pattern with their generated identifiers.
778
- * This is used by client and server transforms so nested lazy destructuring can coexist
779
- * with otherwise normal object/array params.
780
- * @param {AST.Pattern} pattern
781
- * @returns {AST.Pattern}
782
- */
783
- export function replace_lazy_param_pattern(pattern) {
784
- switch (pattern.type) {
785
- case 'AssignmentPattern':
786
- return { ...pattern, left: replace_lazy_param_pattern(pattern.left) };
787
-
788
- case 'ObjectPattern':
789
- if (pattern.lazy && pattern.metadata?.lazy_id) {
790
- return /** @type {AST.Pattern} */ (b.id(pattern.metadata.lazy_id));
791
- }
792
-
793
- return {
794
- ...pattern,
795
- properties: pattern.properties.map((property) =>
796
- property.type === 'RestElement'
797
- ? { ...property, argument: replace_lazy_param_pattern(property.argument) }
798
- : { ...property, value: replace_lazy_param_pattern(property.value) },
799
- ),
800
- };
801
-
802
- case 'ArrayPattern':
803
- if (pattern.lazy && pattern.metadata?.lazy_id) {
804
- return /** @type {AST.Pattern} */ (b.id(pattern.metadata.lazy_id));
805
- }
806
-
807
- return {
808
- ...pattern,
809
- elements: pattern.elements.map((element) =>
810
- element === null ? null : replace_lazy_param_pattern(element),
811
- ),
812
- };
813
-
814
- case 'RestElement':
815
- return { ...pattern, argument: replace_lazy_param_pattern(pattern.argument) };
816
-
817
- default:
818
- return pattern;
819
- }
820
- }
821
-
822
- /**
823
- * @param {CommonContext} context
824
- */
825
- export function get_parent_block_node(context) {
826
- const path = context.path;
827
-
828
- for (let i = path.length - 1; i >= 0; i -= 1) {
829
- const context_node = path[i];
830
- if (
831
- context_node.type === 'IfStatement' ||
832
- context_node.type === 'ForOfStatement' ||
833
- context_node.type === 'SwitchStatement' ||
834
- context_node.type === 'TryStatement' ||
835
- context_node.type === 'Component'
836
- ) {
837
- return context_node;
838
- }
839
- if (
840
- context_node.type === 'FunctionExpression' ||
841
- context_node.type === 'ArrowFunctionExpression' ||
842
- context_node.type === 'FunctionDeclaration'
843
- ) {
844
- return null;
845
- }
846
- }
847
- return null;
848
- }
849
-
850
- /**
851
- * Builds a getter for a tracked identifier
852
- * @param {AST.Identifier} node
853
- * @param {CommonContext} context
854
- * @returns {AST.Expression | AST.Identifier}
855
- */
856
- export function build_getter(node, context) {
857
- const state = context.state;
858
-
859
- if (!context.path) return node;
860
-
861
- for (let i = context.path.length - 1; i >= 0; i -= 1) {
862
- const binding = state.scope.get(node.name);
863
- const transform = binding?.transform;
864
-
865
- // don't transform the declaration itself
866
- if (node !== binding?.node) {
867
- const read_fn = transform?.read;
868
-
869
- if (read_fn) {
870
- return read_fn(node);
871
- }
872
- }
873
- }
874
-
875
- return node;
876
- }
877
-
878
- /**
879
- * Determines the namespace for child elements
880
- * @param {string} element_name
881
- * @param {NameSpace} current_namespace
882
- * @returns {NameSpace}
883
- */
884
- export function determine_namespace_for_children(element_name, current_namespace) {
885
- if (element_name === 'foreignObject') {
886
- return 'html';
887
- }
888
-
889
- if (element_name === 'svg') {
890
- return 'svg';
891
- }
892
-
893
- if (element_name === 'math') {
894
- return 'mathml';
895
- }
896
-
897
- return current_namespace;
898
- }
899
-
900
- /**
901
- * Converts and index to a key string, where the starting character is a
902
- * letter.
903
- * @param {number} index
904
- */
905
- export function index_to_key(index) {
906
- const letters = 'abcdefghijklmnopqrstuvwxyz';
907
- let key = '';
908
-
909
- do {
910
- key = letters[index % 26] + key;
911
- index = Math.floor(index / 26) - 1;
912
- } while (index >= 0);
913
-
914
- return key;
915
- }
916
-
917
- /**
918
- * Check if a binding ultimately refers to a function, following reference chains
919
- * @param {Binding} binding
920
- * @param {ScopeInterface} scope
921
- * @param {Set<Binding>} visited
922
- * @returns {boolean}
923
- */
924
- export function is_binding_function(binding, scope, visited = new Set()) {
925
- if (!binding || visited.has(binding)) {
926
- return false;
927
- }
928
- visited.add(binding);
929
-
930
- const initial = binding.initial;
931
- if (!initial) {
932
- return false;
933
- }
934
-
935
- // Direct function
936
- if (
937
- initial.type === 'FunctionDeclaration' ||
938
- initial.type === 'FunctionExpression' ||
939
- initial.type === 'ArrowFunctionExpression'
940
- ) {
941
- return true;
942
- }
943
-
944
- // Follow identifier references (e.g., const alias = myFunc)
945
- if (initial.type === 'Identifier') {
946
- const next_binding = scope.get(initial.name);
947
- if (next_binding) {
948
- return is_binding_function(next_binding, scope, visited);
949
- }
950
- }
951
-
952
- return false;
953
- }
954
-
955
- /**
956
- * @param {AST.TryStatement} try_parent_stmt
957
- * @param {CommonContext} context
958
- * @returns {boolean}
959
- */
960
- export function is_inside_try_block(try_parent_stmt, context) {
961
- /** @type {AST.BlockStatement | null} */
962
- let block_node = null;
963
- for (let i = context.path.length - 1; i >= 0; i -= 1) {
964
- const context_node = context.path[i];
965
-
966
- if (context_node.type === 'BlockStatement') {
967
- block_node = /** @type {AST.BlockStatement} */ (context_node);
968
- }
969
-
970
- if (context_node === try_parent_stmt) {
971
- break;
972
- }
973
- }
974
-
975
- return block_node !== null && try_parent_stmt.block === block_node;
976
- }
977
-
978
- /**
979
- * Checks if a node is used as the left side of an assignment or update expression.
980
- * @param {AST.Node} node
981
- * @returns {boolean}
982
- */
983
- export function is_inside_left_side_assignment(node) {
984
- const path = node.metadata?.path;
985
- if (!path || path.length === 0) {
986
- return false;
987
- }
988
-
989
- /** @type {AST.Node} */
990
- let current = node;
991
-
992
- for (let i = path.length - 1; i >= 0; i--) {
993
- const parent = path[i];
994
-
995
- switch (parent.type) {
996
- case 'AssignmentExpression':
997
- case 'AssignmentPattern':
998
- if (parent.right === current) {
999
- return false;
1000
- }
1001
-
1002
- if (parent.left === current) {
1003
- return true;
1004
- }
1005
- current = parent;
1006
- continue;
1007
- case 'UpdateExpression':
1008
- return true;
1009
- case 'MemberExpression':
1010
- // In obj[computeKey()] = 10, computeKey() is evaluated to determine
1011
- // which property to assign to, but is not itself an assignment target
1012
- if (parent.computed && parent.property === current) {
1013
- return false;
1014
- }
1015
- current = parent;
1016
- continue;
1017
- case 'Property':
1018
- // exit here to stop promoting current to parent
1019
- // and thus reaching VariableDeclarator, causing an erroneous truthy result
1020
- // e.g. const { [computeKey()]: value } = obj; where node = computeKey:
1021
- if (parent.key === current) {
1022
- return false;
1023
- }
1024
- current = parent;
1025
- continue;
1026
- case 'VariableDeclarator':
1027
- return parent.id === current;
1028
- case 'ForInStatement':
1029
- case 'ForOfStatement':
1030
- return parent.left === current;
1031
-
1032
- case 'Program':
1033
- case 'FunctionDeclaration':
1034
- case 'FunctionExpression':
1035
- case 'ArrowFunctionExpression':
1036
- case 'ClassDeclaration':
1037
- case 'ClassExpression':
1038
- case 'MethodDefinition':
1039
- case 'PropertyDefinition':
1040
- case 'StaticBlock':
1041
- case 'Component':
1042
- case 'Element':
1043
- return false;
1044
-
1045
- default:
1046
- current = parent;
1047
- continue;
1048
- }
1049
- }
1050
-
1051
- return false;
1052
- }
1053
-
1054
- /**
1055
- * Flattens top-level BlockStatements in switch case consequents so that
1056
- * BreakStatements and elements inside block-scoped cases are properly handled.
1057
- * e.g. `case 1: { <div /> break; }` → `[Element, BreakStatement]`
1058
- * @param {AST.Node[]} consequent
1059
- * @returns {AST.Node[]}
1060
- */
1061
- export function flatten_switch_consequent(consequent) {
1062
- /** @type {AST.Node[]} */
1063
- const result = [];
1064
- for (const node of consequent) {
1065
- if (node.type === 'BlockStatement') {
1066
- result.push(.../** @type {AST.BlockStatement} */ (node).body);
1067
- } else {
1068
- result.push(node);
1069
- }
1070
- }
1071
- return result;
1072
- }
1073
-
1074
- /**
1075
- * @param {string | null | undefined} name
1076
- * @returns {string | null}
1077
- */
1078
- export function get_ripple_namespace_call_name(name) {
1079
- return name == null ? null : (RIPPLE_IMPORT_CALL_NAME[name] ?? null);
1080
- }
1081
-
1082
- /**
1083
- * Returns true if the given import name requires a __block parameter
1084
- * @param {string} name
1085
- * @returns {boolean}
1086
- */
1087
- export function ripple_import_requires_block(name) {
1088
- return name !== 'effect' && name !== 'untrack' && name !== 'Context';
1089
- }
1090
-
1091
- /**
1092
- * Converts a JSXMemberExpression to an AST MemberExpression.
1093
- * e.g., <Foo.Bar.Baz> → MemberExpression(MemberExpression(Foo, Bar), Baz)
1094
- * @param {import('estree-jsx').JSXMemberExpression} jsx_member
1095
- * @returns {AST.MemberExpression}
1096
- */
1097
- function jsx_member_expression_to_member_expression(jsx_member) {
1098
- /** @type {AST.Expression} */
1099
- let object;
1100
-
1101
- if (jsx_member.object.type === 'JSXMemberExpression') {
1102
- // Recursively convert nested member expressions
1103
- object = jsx_member_expression_to_member_expression(jsx_member.object);
1104
- } else {
1105
- // Base case: JSXIdentifier
1106
- object = /** @type {AST.Identifier} */ ({
1107
- type: 'Identifier',
1108
- name: jsx_member.object.name,
1109
- start: jsx_member.object.start,
1110
- end: jsx_member.object.end,
1111
- });
1112
- }
1113
-
1114
- return /** @type {AST.MemberExpression} */ ({
1115
- type: 'MemberExpression',
1116
- object,
1117
- property: /** @type {AST.Identifier} */ ({
1118
- type: 'Identifier',
1119
- name: jsx_member.property.name,
1120
- start: jsx_member.property.start,
1121
- end: jsx_member.property.end,
1122
- }),
1123
- computed: false,
1124
- optional: false,
1125
- start: jsx_member.start,
1126
- end: jsx_member.end,
1127
- });
1128
- }
1129
-
1130
- /**
1131
- * Converts a JSX AST node (JSXElement, JSXText, etc.) to a Ripple AST node
1132
- * (Element, Text, RippleExpression) for processing inside `<tsx>` blocks.
1133
- * @param {AST.Node} node
1134
- * @returns {AST.Node | AST.Node[] | null}
1135
- */
1136
- export function jsx_to_ripple_node(node) {
1137
- if (node.type === 'JSXElement') {
1138
- const opening = node.openingElement;
1139
- const name = opening.name;
1140
-
1141
- /** @type {AST.Identifier | AST.MemberExpression} */
1142
- let id;
1143
-
1144
- if (name.type === 'JSXIdentifier') {
1145
- id = /** @type {AST.Identifier} */ ({
1146
- type: 'Identifier',
1147
- name: name.name,
1148
- start: name.start,
1149
- end: name.end,
1150
- });
1151
- } else if (name.type === 'JSXMemberExpression') {
1152
- // Convert JSXMemberExpression to MemberExpression
1153
- // e.g., <Foo.Bar.Baz> → MemberExpression(MemberExpression(Foo, Bar), Baz)
1154
- id = jsx_member_expression_to_member_expression(name);
1155
- } else if (name.type === 'JSXNamespacedName') {
1156
- // For JSXNamespacedName like <namespace:element>, create an identifier with the full name
1157
- id = /** @type {AST.Identifier} */ ({
1158
- type: 'Identifier',
1159
- name: name.namespace.name + ':' + name.name.name,
1160
- start: name.start,
1161
- end: name.end,
1162
- });
1163
- } else {
1164
- // Fallback - should not reach here
1165
- id = /** @type {AST.Identifier} */ ({
1166
- type: 'Identifier',
1167
- name: 'unknown',
1168
- start: /** @type {any} */ (name).start,
1169
- end: /** @type {any} */ (name).end,
1170
- });
1171
- }
1172
-
1173
- const attributes = opening.attributes
1174
- .map((attr) => {
1175
- if (attr.type === 'JSXAttribute') {
1176
- const is_dynamic = attr.value && attr.value.type === 'JSXExpressionContainer';
1177
- return /** @type {AST.Node} */ ({
1178
- type: 'Attribute',
1179
- name: {
1180
- type: 'Identifier',
1181
- name:
1182
- attr.name.type === 'JSXIdentifier'
1183
- ? attr.name.name
1184
- : attr.name.namespace.name + ':' + attr.name.name.name,
1185
- tracked: is_dynamic,
1186
- start: attr.name.start,
1187
- end: attr.name.end,
1188
- },
1189
- value: attr.value
1190
- ? attr.value.type === 'JSXExpressionContainer'
1191
- ? attr.value.expression
1192
- : attr.value
1193
- : null,
1194
- shorthand: false,
1195
- start: attr.start,
1196
- end: attr.end,
1197
- });
1198
- } else if (attr.type === 'JSXSpreadAttribute') {
1199
- return /** @type {AST.Node} */ ({
1200
- type: 'SpreadAttribute',
1201
- argument: attr.argument,
1202
- start: attr.start,
1203
- end: attr.end,
1204
- });
1205
- }
1206
- return null;
1207
- })
1208
- .filter(Boolean);
1209
-
1210
- const children = /** @type {AST.Node[]} */ (
1211
- /** @type {AST.Node[]} */ (node.children).map(jsx_to_ripple_node).flat().filter(Boolean)
1212
- );
1213
-
1214
- return /** @type {AST.Element} */ (
1215
- /** @type {unknown} */ ({
1216
- type: 'Element',
1217
- id,
1218
- attributes,
1219
- children,
1220
- selfClosing: opening.selfClosing,
1221
- metadata: { scoped: false, path: /** @type {string[]} */ ([]) },
1222
- start: node.start,
1223
- end: node.end,
1224
- })
1225
- );
1226
- }
1227
-
1228
- if (node.type === 'JSXText') {
1229
- if (node.value.trim() === '') return null;
1230
- return /** @type {AST.Node} */ ({
1231
- type: 'Text',
1232
- expression: {
1233
- type: 'Literal',
1234
- value: node.value,
1235
- raw: JSON.stringify(node.value),
1236
- start: node.start,
1237
- end: node.end,
1238
- },
1239
- metadata: {},
1240
- start: node.start,
1241
- end: node.end,
1242
- });
1243
- }
1244
-
1245
- if (node.type === 'JSXExpressionContainer') {
1246
- if (node.expression.type === 'JSXEmptyExpression') return null;
1247
- return /** @type {AST.Node} */ ({
1248
- type: 'RippleExpression',
1249
- expression: node.expression,
1250
- metadata: {},
1251
- start: node.start,
1252
- end: node.end,
1253
- });
1254
- }
1255
-
1256
- if (node.type === 'JSXFragment') {
1257
- return /** @type {AST.Node[]} */ (
1258
- /** @type {AST.Node[]} */ (node.children).map(jsx_to_ripple_node).flat().filter(Boolean)
1259
- );
1260
- }
1261
-
1262
- return node;
1263
- }