ripple 0.2.208 → 0.2.210

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 (108) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +2 -1
  3. package/package.json +2 -6
  4. package/shims/rollup-estree-types.d.ts +1 -1
  5. package/src/compiler/index.d.ts +1 -0
  6. package/src/compiler/index.js +7 -1
  7. package/src/compiler/phases/1-parse/index.js +15 -6
  8. package/src/compiler/phases/2-analyze/css-analyze.js +100 -104
  9. package/src/compiler/phases/2-analyze/index.js +215 -2
  10. package/src/compiler/phases/3-transform/client/index.js +388 -50
  11. package/src/compiler/phases/3-transform/segments.js +123 -39
  12. package/src/compiler/phases/3-transform/server/index.js +266 -13
  13. package/src/compiler/types/index.d.ts +16 -3
  14. package/src/compiler/utils.js +1 -15
  15. package/src/constants.js +0 -2
  16. package/src/helpers.d.ts +4 -0
  17. package/src/html-tree-validation.js +211 -0
  18. package/src/jsx-runtime.d.ts +260 -259
  19. package/src/jsx-runtime.js +12 -12
  20. package/src/runtime/array.js +17 -17
  21. package/src/runtime/create-subscriber.js +1 -1
  22. package/src/runtime/index-client.js +1 -5
  23. package/src/runtime/index-server.js +15 -0
  24. package/src/runtime/internal/client/compat.js +3 -3
  25. package/src/runtime/internal/client/composite.js +6 -1
  26. package/src/runtime/internal/client/head.js +50 -4
  27. package/src/runtime/internal/client/html.js +73 -12
  28. package/src/runtime/internal/client/hydration.js +12 -0
  29. package/src/runtime/internal/client/index.js +1 -1
  30. package/src/runtime/internal/client/portal.js +54 -29
  31. package/src/runtime/internal/client/rpc.js +3 -1
  32. package/src/runtime/internal/client/switch.js +5 -0
  33. package/src/runtime/internal/client/template.js +117 -11
  34. package/src/runtime/internal/client/try.js +1 -0
  35. package/src/runtime/internal/server/index.js +113 -1
  36. package/src/runtime/internal/server/rpc.js +4 -4
  37. package/src/runtime/map.js +2 -2
  38. package/src/runtime/object.js +6 -6
  39. package/src/runtime/proxy.js +12 -11
  40. package/src/runtime/reactive-value.js +9 -1
  41. package/src/runtime/set.js +12 -7
  42. package/src/runtime/url-search-params.js +0 -1
  43. package/src/server/index.js +4 -0
  44. package/src/utils/hashing.js +15 -0
  45. package/src/utils/normalize_css_property_name.js +1 -1
  46. package/tests/client/array/array.mutations.test.ripple +8 -8
  47. package/tests/client/basic/basic.errors.test.ripple +28 -0
  48. package/tests/client/basic/basic.events.test.ripple +6 -3
  49. package/tests/client/basic/basic.utilities.test.ripple +1 -1
  50. package/tests/client/compiler/compiler.regex.test.ripple +10 -8
  51. package/tests/client/composite/composite.generics.test.ripple +5 -2
  52. package/tests/client/dynamic-elements.test.ripple +30 -1
  53. package/tests/client/function-overload-import.ripple +6 -7
  54. package/tests/client/html.test.ripple +0 -1
  55. package/tests/client/object.test.ripple +2 -2
  56. package/tests/client/portal.test.ripple +3 -3
  57. package/tests/client/return.test.ripple +2500 -0
  58. package/tests/client/try.test.ripple +69 -0
  59. package/tests/client/typescript-generics.test.ripple +1 -1
  60. package/tests/client/url/url.derived.test.ripple +1 -1
  61. package/tests/client/url/url.parsing.test.ripple +3 -3
  62. package/tests/client/url/url.partial-removal.test.ripple +7 -7
  63. package/tests/client/url/url.reactivity.test.ripple +15 -15
  64. package/tests/client/url/url.serialization.test.ripple +2 -2
  65. package/tests/hydration/basic.test.js +23 -0
  66. package/tests/hydration/build-components.js +10 -4
  67. package/tests/hydration/compiled/client/basic.js +165 -3
  68. package/tests/hydration/compiled/client/for.js +1140 -23
  69. package/tests/hydration/compiled/client/head.js +234 -0
  70. package/tests/hydration/compiled/client/html.js +135 -0
  71. package/tests/hydration/compiled/client/portal.js +172 -0
  72. package/tests/hydration/compiled/client/reactivity.js +3 -1
  73. package/tests/hydration/compiled/client/return.js +1976 -0
  74. package/tests/hydration/compiled/client/switch.js +162 -0
  75. package/tests/hydration/compiled/server/basic.js +249 -0
  76. package/tests/hydration/compiled/server/events.js +1 -1
  77. package/tests/hydration/compiled/server/for.js +891 -1
  78. package/tests/hydration/compiled/server/head.js +291 -0
  79. package/tests/hydration/compiled/server/html.js +133 -0
  80. package/tests/hydration/compiled/server/if.js +1 -1
  81. package/tests/hydration/compiled/server/portal.js +250 -0
  82. package/tests/hydration/compiled/server/reactivity.js +1 -1
  83. package/tests/hydration/compiled/server/return.js +1969 -0
  84. package/tests/hydration/compiled/server/switch.js +130 -0
  85. package/tests/hydration/components/basic.ripple +55 -0
  86. package/tests/hydration/components/for.ripple +403 -0
  87. package/tests/hydration/components/head.ripple +111 -0
  88. package/tests/hydration/components/html.ripple +38 -0
  89. package/tests/hydration/components/portal.ripple +49 -0
  90. package/tests/hydration/components/return.ripple +564 -0
  91. package/tests/hydration/components/switch.ripple +51 -0
  92. package/tests/hydration/for.test.js +363 -0
  93. package/tests/hydration/head.test.js +105 -0
  94. package/tests/hydration/html.test.js +46 -0
  95. package/tests/hydration/portal.test.js +71 -0
  96. package/tests/hydration/return.test.js +544 -0
  97. package/tests/hydration/switch.test.js +42 -0
  98. package/tests/server/basic.attributes.test.ripple +1 -1
  99. package/tests/server/compiler.test.ripple +22 -0
  100. package/tests/server/composite.test.ripple +5 -2
  101. package/tests/server/html-nesting-validation.test.ripple +237 -0
  102. package/tests/server/return.test.ripple +1379 -0
  103. package/tests/setup-hydration.js +6 -1
  104. package/tests/utils/escaping.test.js +3 -1
  105. package/tests/utils/normalize_css_property_name.test.js +0 -1
  106. package/tests/utils/patterns.test.js +6 -2
  107. package/tests/utils/sanitize_template_string.test.js +3 -2
  108. package/types/server.d.ts +16 -0
@@ -0,0 +1,237 @@
1
+ import { compile } from 'ripple/compiler';
2
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
3
+ import { render } from 'ripple/server';
4
+ import { push_element, pop_element, reset_element_state } from 'ripple/internal/server';
5
+
6
+ describe('HTML nesting validation', () => {
7
+ let consoleErrorSpy;
8
+
9
+ beforeEach(() => {
10
+ reset_element_state();
11
+ consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
12
+ });
13
+
14
+ afterEach(() => {
15
+ consoleErrorSpy.mockRestore();
16
+ reset_element_state();
17
+ });
18
+
19
+ describe('push_element and pop_element runtime', () => {
20
+ it('does not warn for valid nesting', () => {
21
+ push_element('div', 'test.ripple', 1, 0);
22
+ push_element('span', 'test.ripple', 2, 0);
23
+ pop_element();
24
+ pop_element();
25
+
26
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
27
+ });
28
+
29
+ it('warns when button is nested inside button', () => {
30
+ push_element('button', 'test.ripple', 1, 0);
31
+ push_element('button', 'test.ripple', 2, 0);
32
+ pop_element();
33
+ pop_element();
34
+
35
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
36
+ const msg = consoleErrorSpy.mock.calls[0][0];
37
+ expect(msg).toContain('`<button>`');
38
+ expect(msg).toContain('cannot be');
39
+ });
40
+
41
+ it('warns when a is nested inside a', () => {
42
+ push_element('a', 'test.ripple', 1, 0);
43
+ push_element('a', 'test.ripple', 2, 0);
44
+ pop_element();
45
+ pop_element();
46
+
47
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
48
+ const msg = consoleErrorSpy.mock.calls[0][0];
49
+ expect(msg).toContain('`<a>`');
50
+ });
51
+
52
+ it('warns when div is inside p', () => {
53
+ push_element('p', 'test.ripple', 1, 0);
54
+ push_element('div', 'test.ripple', 2, 0);
55
+ pop_element();
56
+ pop_element();
57
+
58
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
59
+ const msg = consoleErrorSpy.mock.calls[0][0];
60
+ expect(msg).toContain('`<div>`');
61
+ expect(msg).toContain('`<p>`');
62
+ });
63
+
64
+ it('warns when heading is nested inside heading', () => {
65
+ push_element('h1', 'test.ripple', 1, 0);
66
+ push_element('h2', 'test.ripple', 2, 0);
67
+ pop_element();
68
+ pop_element();
69
+
70
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
71
+ const msg = consoleErrorSpy.mock.calls[0][0];
72
+ expect(msg).toContain('`<h2>`');
73
+ expect(msg).toContain('`<h1>`');
74
+ });
75
+
76
+ it('warns when form is nested inside form', () => {
77
+ push_element('form', 'test.ripple', 1, 0);
78
+ push_element('form', 'test.ripple', 2, 0);
79
+ pop_element();
80
+ pop_element();
81
+
82
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
83
+ const msg = consoleErrorSpy.mock.calls[0][0];
84
+ expect(msg).toContain('`<form>`');
85
+ });
86
+
87
+ it('warns when td is not child of tr', () => {
88
+ push_element('div', 'test.ripple', 1, 0);
89
+ push_element('td', 'test.ripple', 2, 0);
90
+ pop_element();
91
+ pop_element();
92
+
93
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
94
+ const msg = consoleErrorSpy.mock.calls[0][0];
95
+ expect(msg).toContain('`<td>`');
96
+ expect(msg).toContain('`<tr>`');
97
+ });
98
+
99
+ it('warns when tr is not child of thead or tbody or tfoot', () => {
100
+ push_element('div', 'test.ripple', 1, 0);
101
+ push_element('tr', 'test.ripple', 2, 0);
102
+ pop_element();
103
+ pop_element();
104
+
105
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
106
+ const msg = consoleErrorSpy.mock.calls[0][0];
107
+ expect(msg).toContain('`<tr>`');
108
+ });
109
+
110
+ it('deduplicates warnings for the same message', () => {
111
+ push_element('button', 'test.ripple', 1, 0);
112
+ push_element('button', 'test.ripple', 2, 0);
113
+ pop_element();
114
+ pop_element();
115
+
116
+ push_element('button', 'test.ripple', 1, 0);
117
+ push_element('button', 'test.ripple', 2, 0);
118
+ pop_element();
119
+ pop_element();
120
+
121
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
122
+ });
123
+
124
+ it('includes location information in warning', () => {
125
+ push_element('button', 'App.ripple', 5, 2);
126
+ push_element('button', 'App.ripple', 10, 4);
127
+ pop_element();
128
+ pop_element();
129
+
130
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
131
+ const msg = consoleErrorSpy.mock.calls[0][0];
132
+ expect(msg).toContain('App.ripple:10:4');
133
+ expect(msg).toContain('App.ripple:5:2');
134
+ });
135
+
136
+ it('does not warn for custom elements', () => {
137
+ push_element('my-button', 'test.ripple', 1, 0);
138
+ push_element('my-button', 'test.ripple', 2, 0);
139
+ pop_element();
140
+ pop_element();
141
+
142
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
143
+ });
144
+
145
+ it('validates ancestor nesting (button deep inside button)', () => {
146
+ push_element('button', 'test.ripple', 1, 0);
147
+ push_element('div', 'test.ripple', 2, 0);
148
+ push_element('span', 'test.ripple', 3, 0);
149
+ push_element('button', 'test.ripple', 4, 0);
150
+ pop_element();
151
+ pop_element();
152
+ pop_element();
153
+ pop_element();
154
+
155
+ expect(consoleErrorSpy).toHaveBeenCalled();
156
+ const msg = consoleErrorSpy.mock.calls[0][0];
157
+ expect(msg).toContain('`<button>`');
158
+ });
159
+
160
+ it('warns when li is direct child of another li', () => {
161
+ push_element('li', 'test.ripple', 1, 0);
162
+ push_element('li', 'test.ripple', 2, 0);
163
+ pop_element();
164
+ pop_element();
165
+
166
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
167
+ const msg = consoleErrorSpy.mock.calls[0][0];
168
+ expect(msg).toContain('`<li>`');
169
+ });
170
+
171
+ it('includes hydration mismatch warning text', () => {
172
+ push_element('p', 'test.ripple', 1, 0);
173
+ push_element('div', 'test.ripple', 2, 0);
174
+ pop_element();
175
+ pop_element();
176
+
177
+ const msg = consoleErrorSpy.mock.calls[0][0];
178
+ expect(msg).toContain('hydration mismatch');
179
+ });
180
+ });
181
+
182
+ describe('compiler dev mode output', () => {
183
+ it('emits push_element and pop_element calls in dev mode', () => {
184
+ const lines = [];
185
+ lines.push('component App() {');
186
+ lines.push(' <div>');
187
+ lines.push(' <span>{"Hello"}</span>');
188
+ lines.push(' </div>');
189
+ lines.push('}');
190
+ const source = lines.join('\n');
191
+
192
+ const result = compile(source, 'test.ripple', { mode: 'server', dev: true });
193
+
194
+ expect(result.js.code).toContain('_$_.push_element');
195
+ expect(result.js.code).toContain('_$_.pop_element');
196
+ });
197
+
198
+ it('does not emit push_element or pop_element in non-dev mode', () => {
199
+ const lines = [];
200
+ lines.push('component App() {');
201
+ lines.push(' <div>');
202
+ lines.push(' <span>{"Hello"}</span>');
203
+ lines.push(' </div>');
204
+ lines.push('}');
205
+ const source = lines.join('\n');
206
+
207
+ const result = compile(source, 'test.ripple', { mode: 'server', dev: false });
208
+
209
+ expect(result.js.code).not.toContain('push_element');
210
+ expect(result.js.code).not.toContain('pop_element');
211
+ });
212
+
213
+ it('emits push_element with correct tag name', () => {
214
+ const lines = [];
215
+ lines.push('component App() {');
216
+ lines.push(' <button>{"Click"}</button>');
217
+ lines.push('}');
218
+ const source = lines.join('\n');
219
+
220
+ const result = compile(source, 'test.ripple', { mode: 'server', dev: true });
221
+
222
+ expect(result.js.code).toContain('_$_.push_element(\'button\'');
223
+ });
224
+
225
+ it('does not emit push_element for client mode', () => {
226
+ const lines = [];
227
+ lines.push('component App() {');
228
+ lines.push(' <div>{"Hello"}</div>');
229
+ lines.push('}');
230
+ const source = lines.join('\n');
231
+
232
+ const result = compile(source, 'test.ripple', { mode: 'client', dev: true });
233
+
234
+ expect(result.js.code).not.toContain('push_element');
235
+ });
236
+ });
237
+ });