ripple 0.3.72 → 0.3.74
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.
- package/CHANGELOG.md +66 -0
- package/package.json +3 -3
- package/src/jsx-runtime.d.ts +2 -8
- package/src/runtime/index-client.js +3 -13
- package/src/runtime/internal/client/blocks.js +3 -25
- package/src/runtime/internal/client/for.js +80 -5
- package/src/runtime/internal/client/index.js +0 -2
- package/src/runtime/internal/client/types.d.ts +0 -10
- package/tests/client/__snapshots__/computed-properties.test.tsrx.snap +8 -0
- package/tests/client/__snapshots__/for.test.tsrx.snap +22 -0
- package/tests/client/__snapshots__/html.test.tsrx.snap +4 -0
- package/tests/client/array/array.copy-within.test.tsrx +19 -19
- package/tests/client/array/array.derived.test.tsrx +97 -109
- package/tests/client/array/array.iteration.test.tsrx +28 -28
- package/tests/client/array/array.mutations.test.tsrx +68 -68
- package/tests/client/array/array.static.test.tsrx +82 -92
- package/tests/client/array/array.to-methods.test.tsrx +15 -15
- package/tests/client/async-suspend.test.tsrx +180 -179
- package/tests/client/basic/__snapshots__/basic.attributes.test.tsrx.snap +2 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +4 -0
- package/tests/client/basic/basic.attributes.test.tsrx +273 -317
- package/tests/client/basic/basic.collections.test.tsrx +55 -61
- package/tests/client/basic/basic.components.test.tsrx +196 -218
- package/tests/client/basic/basic.errors.test.tsrx +70 -76
- package/tests/client/basic/basic.events.test.tsrx +80 -85
- package/tests/client/basic/basic.get-set.test.tsrx +54 -64
- package/tests/client/basic/basic.hmr.test.tsrx +15 -19
- package/tests/client/basic/basic.reactivity.test.tsrx +121 -135
- package/tests/client/basic/basic.rendering.test.tsrx +273 -178
- package/tests/client/basic/basic.utilities.test.tsrx +8 -10
- package/tests/client/boundaries.test.tsrx +18 -18
- package/tests/client/compiler/compiler.assignments.test.tsrx +77 -76
- package/tests/client/compiler/compiler.attributes.test.tsrx +18 -14
- package/tests/client/compiler/compiler.basic.test.tsrx +357 -288
- package/tests/client/compiler/compiler.regex.test.tsrx +40 -44
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +57 -38
- package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
- package/tests/client/compiler/compiler.typescript.test.tsrx +4 -3
- package/tests/client/composite/composite.dynamic-components.test.tsrx +41 -44
- package/tests/client/composite/composite.generics.test.tsrx +165 -167
- package/tests/client/composite/composite.props.test.tsrx +66 -74
- package/tests/client/composite/composite.reactivity.test.tsrx +132 -166
- package/tests/client/composite/composite.render.test.tsrx +92 -101
- package/tests/client/computed-properties.test.tsrx +14 -18
- package/tests/client/context.test.tsrx +14 -18
- package/tests/client/css/global-additional-cases.test.tsrx +491 -437
- package/tests/client/css/global-advanced-selectors.test.tsrx +169 -153
- package/tests/client/css/global-at-rules.test.tsrx +71 -66
- package/tests/client/css/global-basic.test.tsrx +105 -98
- package/tests/client/css/global-classes-ids.test.tsrx +128 -114
- package/tests/client/css/global-combinators.test.tsrx +83 -78
- package/tests/client/css/global-complex-nesting.test.tsrx +134 -120
- package/tests/client/css/global-edge-cases.test.tsrx +138 -120
- package/tests/client/css/global-keyframes.test.tsrx +108 -96
- package/tests/client/css/global-nested.test.tsrx +88 -78
- package/tests/client/css/global-pseudo.test.tsrx +104 -98
- package/tests/client/css/global-scoping.test.tsrx +145 -125
- package/tests/client/css/style-identifier.test.tsrx +62 -69
- package/tests/client/date.test.tsrx +83 -83
- package/tests/client/dynamic-elements.test.tsrx +227 -283
- package/tests/client/events.test.tsrx +252 -266
- package/tests/client/for.test.tsrx +120 -127
- package/tests/client/head.test.tsrx +40 -48
- package/tests/client/html.test.tsrx +37 -49
- package/tests/client/input-value.test.tsrx +1125 -1354
- package/tests/client/lazy-array.test.tsrx +10 -16
- package/tests/client/lazy-destructuring.test.tsrx +169 -221
- package/tests/client/map.test.tsrx +39 -41
- package/tests/client/media-query.test.tsrx +15 -19
- package/tests/client/object.test.tsrx +46 -56
- package/tests/client/portal.test.tsrx +31 -37
- package/tests/client/ref.test.tsrx +173 -193
- package/tests/client/return.test.tsrx +62 -37
- package/tests/client/set.test.tsrx +33 -33
- package/tests/client/svg.test.tsrx +195 -215
- package/tests/client/switch.test.tsrx +201 -191
- package/tests/client/track-async-hydration.test.tsrx +14 -18
- package/tests/client/tracked-index-access.test.tsrx +18 -28
- package/tests/client/try.test.tsrx +494 -619
- package/tests/client/tsx.test.tsrx +286 -292
- package/tests/client/typescript-generics.test.tsrx +121 -129
- package/tests/client/url/url.derived.test.tsrx +21 -25
- package/tests/client/url/url.parsing.test.tsrx +35 -35
- package/tests/client/url/url.partial-removal.test.tsrx +32 -32
- package/tests/client/url/url.reactivity.test.tsrx +68 -72
- package/tests/client/url/url.serialization.test.tsrx +8 -8
- package/tests/client/url-search-params/url-search-params.derived.test.tsrx +21 -27
- package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +16 -16
- package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +37 -37
- package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +56 -60
- package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +32 -34
- package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +9 -9
- package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +10 -10
- package/tests/hydration/compiled/client/basic.js +390 -319
- package/tests/hydration/compiled/client/composite.js +52 -44
- package/tests/hydration/compiled/client/for.js +734 -604
- package/tests/hydration/compiled/client/head.js +183 -103
- package/tests/hydration/compiled/client/html.js +93 -86
- package/tests/hydration/compiled/client/if-children.js +95 -71
- package/tests/hydration/compiled/client/if.js +113 -89
- package/tests/hydration/compiled/client/mixed-control-flow.js +225 -209
- package/tests/hydration/compiled/client/nested-control-flow.js +94 -98
- package/tests/hydration/compiled/client/reactivity.js +26 -24
- package/tests/hydration/compiled/client/return.js +8 -42
- package/tests/hydration/compiled/client/switch.js +208 -173
- package/tests/hydration/compiled/client/track-async-serialization.js +176 -128
- package/tests/hydration/compiled/client/try.js +29 -21
- package/tests/hydration/compiled/server/basic.js +210 -221
- package/tests/hydration/compiled/server/composite.js +13 -14
- package/tests/hydration/compiled/server/for.js +427 -444
- package/tests/hydration/compiled/server/head.js +199 -189
- package/tests/hydration/compiled/server/html.js +33 -41
- package/tests/hydration/compiled/server/if-children.js +114 -117
- package/tests/hydration/compiled/server/if.js +77 -83
- package/tests/hydration/compiled/server/mixed-control-flow.js +145 -150
- package/tests/hydration/compiled/server/nested-control-flow.js +10 -0
- package/tests/hydration/compiled/server/reactivity.js +24 -22
- package/tests/hydration/compiled/server/return.js +6 -18
- package/tests/hydration/compiled/server/switch.js +179 -176
- package/tests/hydration/compiled/server/track-async-serialization.js +88 -70
- package/tests/hydration/compiled/server/try.js +31 -35
- package/tests/hydration/components/basic.tsrx +216 -258
- package/tests/hydration/components/composite.tsrx +32 -42
- package/tests/hydration/components/events.tsrx +81 -101
- package/tests/hydration/components/for.tsrx +270 -336
- package/tests/hydration/components/head.tsrx +43 -39
- package/tests/hydration/components/hmr.tsrx +16 -22
- package/tests/hydration/components/html-in-template.tsrx +15 -21
- package/tests/hydration/components/html.tsrx +442 -526
- package/tests/hydration/components/if-children.tsrx +107 -125
- package/tests/hydration/components/if.tsrx +68 -90
- package/tests/hydration/components/mixed-control-flow.tsrx +65 -72
- package/tests/hydration/components/nested-control-flow.tsrx +202 -216
- package/tests/hydration/components/portal.tsrx +33 -41
- package/tests/hydration/components/reactivity.tsrx +26 -34
- package/tests/hydration/components/return.tsrx +4 -6
- package/tests/hydration/components/switch.tsrx +73 -78
- package/tests/hydration/components/track-async-serialization.tsrx +83 -93
- package/tests/hydration/components/try.tsrx +37 -51
- package/tests/hydration/switch.test.js +8 -8
- package/tests/server/await.test.tsrx +3 -3
- package/tests/server/basic.attributes.test.tsrx +117 -162
- package/tests/server/basic.components.test.tsrx +163 -193
- package/tests/server/basic.test.tsrx +298 -198
- package/tests/server/compiler.test.tsrx +142 -72
- package/tests/server/composite.props.test.tsrx +54 -58
- package/tests/server/composite.test.tsrx +165 -167
- package/tests/server/context.test.tsrx +13 -17
- package/tests/server/dynamic-elements.test.tsrx +103 -135
- package/tests/server/for.test.tsrx +115 -84
- package/tests/server/head.test.tsrx +31 -31
- package/tests/server/html-nesting-validation.test.tsrx +16 -8
- package/tests/server/if.test.tsrx +49 -59
- package/tests/server/lazy-destructuring.test.tsrx +288 -366
- package/tests/server/return.test.tsrx +58 -36
- package/tests/server/streaming-ssr.test.tsrx +4 -4
- package/tests/server/style-identifier.test.tsrx +58 -66
- package/tests/server/switch.test.tsrx +89 -97
- package/tests/server/track-async-serialization.test.tsrx +85 -103
- package/tests/server/try.test.tsrx +275 -360
- package/tests/utils/ref-types.test.js +72 -0
- package/tests/utils/vite-plugin-config.test.js +41 -74
- package/types/index.d.ts +1 -0
- package/src/runtime/internal/client/compat.js +0 -40
- package/tests/utils/compiler-compat-config.test.js +0 -38
|
@@ -8,6 +8,10 @@ function get_returned_tsrx(node: any): any {
|
|
|
8
8
|
const body =
|
|
9
9
|
target.type === 'VariableDeclarator' ? target.init.body : target.body;
|
|
10
10
|
|
|
11
|
+
if (body.type === 'JSXCodeBlock') {
|
|
12
|
+
return body.render;
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
if (body.type !== 'BlockStatement') {
|
|
12
16
|
return body;
|
|
13
17
|
}
|
|
@@ -29,11 +33,12 @@ function count_occurrences(string: string, subString: string): number {
|
|
|
29
33
|
|
|
30
34
|
describe('compiler > basics', () => {
|
|
31
35
|
it('parses style content correctly', () => {
|
|
32
|
-
const source = `export function App() {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
const source = `export function App() @{
|
|
37
|
+
<>
|
|
38
|
+
<div id="myid" class="myclass">{"Hello World"}</div>
|
|
39
|
+
<style>__STYLE__</style>
|
|
40
|
+
</>
|
|
41
|
+
}`;
|
|
37
42
|
const style1 = '.myid {color: green }';
|
|
38
43
|
const style2 = '#myid {color: green }';
|
|
39
44
|
const style3 = 'div {color: green }';
|
|
@@ -52,13 +57,15 @@ describe('compiler > basics', () => {
|
|
|
52
57
|
});
|
|
53
58
|
|
|
54
59
|
it('parses text as an ordinary expression identifier', () => {
|
|
55
|
-
const source = `export function App() {
|
|
60
|
+
const source = `export function App() @{
|
|
56
61
|
const markup = '<span>Not HTML</span>';
|
|
57
62
|
const text = markup;
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
<>
|
|
65
|
+
<div>{markup}</div>
|
|
66
|
+
<div>{text}</div>
|
|
67
|
+
</>
|
|
68
|
+
}`;
|
|
62
69
|
|
|
63
70
|
const ast = parse(source);
|
|
64
71
|
const elements = get_returned_tsrx(ast.body[0]).children.filter(
|
|
@@ -76,11 +83,11 @@ describe('compiler > basics', () => {
|
|
|
76
83
|
const { code } = compile(source, 'text-directive.tsrx', { mode: 'client' });
|
|
77
84
|
expect(code).not.toContain('_$_.html');
|
|
78
85
|
|
|
79
|
-
const invalid_source = `export function App() {
|
|
86
|
+
const invalid_source = `export function App() @{
|
|
80
87
|
const markup = 'plain';
|
|
81
88
|
|
|
82
89
|
<div>{text markup}</div>
|
|
83
|
-
|
|
90
|
+
}`;
|
|
84
91
|
|
|
85
92
|
expect(() => parse(invalid_source)).toThrow();
|
|
86
93
|
});
|
|
@@ -88,180 +95,234 @@ describe('compiler > basics', () => {
|
|
|
88
95
|
it('optimizes string-shaped expressions as text nodes', () => {
|
|
89
96
|
const source = `export function App(
|
|
90
97
|
{ title, props }: { title: string; props: { label: string } },
|
|
91
|
-
) {
|
|
98
|
+
) @{
|
|
92
99
|
const element = <span />;
|
|
93
100
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
<>
|
|
102
|
+
<div>{String(title)}</div>
|
|
103
|
+
<div>{title + ''}</div>
|
|
104
|
+
<div>{title as string}</div>
|
|
105
|
+
<div>{title}</div>
|
|
106
|
+
<div>{props.label}</div>
|
|
107
|
+
<div>{element}</div>
|
|
108
|
+
</>
|
|
109
|
+
}`;
|
|
101
110
|
|
|
102
111
|
const { code } = compile(source, 'stringish-text.tsrx', { mode: 'client' });
|
|
103
112
|
|
|
104
113
|
expect(code).toContain('_$_.set_text');
|
|
105
114
|
expect(code).toContain('nodeValue = title');
|
|
106
|
-
expect(count_occurrences(code, '_$_.expression')).toBe(
|
|
115
|
+
expect(count_occurrences(code, '_$_.expression')).toBe(2);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('does not merge adjacent call-containing children into stringified text', () => {
|
|
119
|
+
const setup = `function child(label) @{
|
|
120
|
+
<span>{label}</span>
|
|
121
|
+
}
|
|
122
|
+
function Constructed(label) {
|
|
123
|
+
return child(label);
|
|
124
|
+
}
|
|
125
|
+
const factory = child;
|
|
126
|
+
function identity(value) {
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function empty() {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
let assigned;
|
|
133
|
+
const lookup = { member: child('member') };
|
|
134
|
+
function getKey() {
|
|
135
|
+
return 'member';
|
|
136
|
+
}
|
|
137
|
+
function touch() {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
function tag() {
|
|
141
|
+
return child('tagged');
|
|
142
|
+
}
|
|
143
|
+
const values = { member: 0 };`;
|
|
144
|
+
|
|
145
|
+
const cases = [
|
|
146
|
+
{ name: 'call expression', expression: 'child(\'call\')' },
|
|
147
|
+
{ name: 'new expression', expression: 'new Constructed(\'new\')' },
|
|
148
|
+
{ name: 'chain expression', expression: 'factory?.(\'chain\')' },
|
|
149
|
+
{ name: 'parenthesized expression', expression: '(child(\'parenthesized\'))' },
|
|
150
|
+
{ name: 'TS as expression', expression: 'child(\'as\') as any' },
|
|
151
|
+
{ name: 'TS non-null expression', expression: 'child(\'non-null\')!' },
|
|
152
|
+
{ name: 'TS satisfies expression', expression: 'child(\'satisfies\') satisfies any' },
|
|
153
|
+
{
|
|
154
|
+
name: 'TS instantiation expression',
|
|
155
|
+
expression: 'identity<typeof child>(child)(\'instantiation\')',
|
|
156
|
+
},
|
|
157
|
+
{ name: 'array expression', expression: '[child(\'array\')]' },
|
|
158
|
+
{ name: 'assignment expression', expression: 'assigned = child(\'assignment\')' },
|
|
159
|
+
{ name: 'binary expression', expression: 'empty() + \'\'' },
|
|
160
|
+
{ name: 'logical expression', expression: 'true && child(\'logical\')' },
|
|
161
|
+
{ name: 'conditional expression', expression: 'true ? child(\'conditional\') : \'\'' },
|
|
162
|
+
{ name: 'member expression', expression: 'lookup[getKey()]' },
|
|
163
|
+
{
|
|
164
|
+
name: 'object expression',
|
|
165
|
+
expression: '{ [Symbol.for(\'ripple.element\')]: true, render() { return child(\'object\'); } }',
|
|
166
|
+
},
|
|
167
|
+
{ name: 'sequence expression', expression: '(touch(), child(\'sequence\'))' },
|
|
168
|
+
{ name: 'tagged template expression', expression: 'tag`tagged`' },
|
|
169
|
+
{ name: 'template literal', expression: '`${empty()}`' },
|
|
170
|
+
{ name: 'unary expression', expression: 'void empty()' },
|
|
171
|
+
{ name: 'update expression', expression: 'values[getKey()]++' },
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
for (const test_case of cases) {
|
|
175
|
+
const source = `${setup}
|
|
176
|
+
export function App() @{
|
|
177
|
+
<div>{'${test_case.name}:'}{${test_case.expression}}</div>
|
|
178
|
+
}`;
|
|
179
|
+
|
|
180
|
+
const { code } = compile(source, `${test_case.name}.tsrx`, { mode: 'client' });
|
|
181
|
+
|
|
182
|
+
if (code.includes('String(')) {
|
|
183
|
+
throw new Error(`${test_case.name} was merged into a stringified text expression:\n${code}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
107
186
|
});
|
|
108
187
|
|
|
109
188
|
it('parses backtick expressions inside TSRX fragments as template literals', () => {
|
|
110
|
-
const source = `let a = function() {
|
|
111
|
-
return <>
|
|
189
|
+
const source = `let a = function() @{
|
|
112
190
|
<>
|
|
113
|
-
\`333\`
|
|
191
|
+
{\`333\`}
|
|
114
192
|
</>
|
|
115
|
-
</>;
|
|
116
193
|
}`;
|
|
117
194
|
|
|
118
195
|
const ast = parse(source);
|
|
119
196
|
const declaration = (ast.body[0] as AST.VariableDeclaration).declarations[0];
|
|
120
|
-
const fragment = get_returned_tsrx(declaration)
|
|
197
|
+
const fragment = get_returned_tsrx(declaration) as any;
|
|
121
198
|
|
|
122
199
|
expect(fragment.type).toBe('TsrxFragment');
|
|
123
|
-
expect(fragment.children[0].type).toBe('
|
|
200
|
+
expect(fragment.children[0].type).toBe('TSRXExpression');
|
|
124
201
|
expect(fragment.children[0].expression.type).toBe('TemplateLiteral');
|
|
125
202
|
expect(fragment.children[0].expression.quasis[0].value.raw).toBe('333');
|
|
126
203
|
});
|
|
127
204
|
|
|
128
205
|
it('parses backtick expressions around tag-like text inside TSRX fragments', () => {
|
|
129
|
-
const source = `let a = function() {
|
|
130
|
-
return <>
|
|
206
|
+
const source = `let a = function() @{
|
|
131
207
|
<>
|
|
132
|
-
\`
|
|
208
|
+
{\`
|
|
133
209
|
<b></b>
|
|
134
|
-
\`
|
|
210
|
+
\`}
|
|
135
211
|
</>
|
|
136
|
-
</>;
|
|
137
212
|
}`;
|
|
138
213
|
|
|
139
214
|
const ast = parse(source);
|
|
140
215
|
const declaration = (ast.body[0] as AST.VariableDeclaration).declarations[0];
|
|
141
|
-
const fragment = get_returned_tsrx(declaration)
|
|
216
|
+
const fragment = get_returned_tsrx(declaration) as any;
|
|
142
217
|
|
|
143
218
|
expect(fragment.type).toBe('TsrxFragment');
|
|
144
219
|
expect(fragment.children.map((child: any) => child.type)).toEqual([
|
|
145
|
-
'
|
|
220
|
+
'TSRXExpression',
|
|
146
221
|
]);
|
|
147
222
|
expect(fragment.children[0].expression.type).toBe('TemplateLiteral');
|
|
148
223
|
expect(fragment.children[0].expression.quasis[0].value.raw).toContain('<b></b>');
|
|
149
224
|
});
|
|
150
225
|
|
|
151
226
|
it('renders without crashing', () => {
|
|
152
|
-
function App() {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
baz['jkl'] = 987;
|
|
163
|
-
</>;
|
|
227
|
+
function App() @{
|
|
228
|
+
let foo: Record<string, number>;
|
|
229
|
+
let bar: Record<string, number>;
|
|
230
|
+
let baz: Record<string, number>;
|
|
231
|
+
foo = {};
|
|
232
|
+
foo = { test: 0 };
|
|
233
|
+
foo['abc'] = 123;
|
|
234
|
+
bar = { def: 456 };
|
|
235
|
+
baz = { ghi: 789 };
|
|
236
|
+
baz['jkl'] = 987;
|
|
164
237
|
}
|
|
165
238
|
|
|
166
239
|
render(App);
|
|
167
240
|
});
|
|
168
241
|
|
|
169
242
|
it('renders without crashing using < character', () => {
|
|
170
|
-
function App() {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
// do nothing
|
|
175
|
-
}
|
|
176
|
-
const x = 1 < 1;
|
|
243
|
+
function App() @{
|
|
244
|
+
function bar() {
|
|
245
|
+
for (let i = 0; i < 10; i++) {
|
|
246
|
+
// do nothing
|
|
177
247
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
248
|
+
const x = 1 < 1;
|
|
249
|
+
}
|
|
250
|
+
let x = 5 < 10;
|
|
251
|
+
<div>{x}</div>
|
|
181
252
|
}
|
|
182
253
|
|
|
183
254
|
render(App);
|
|
184
255
|
});
|
|
185
256
|
|
|
186
257
|
it('renders lexical blocks without crashing', () => {
|
|
187
|
-
function App() {
|
|
188
|
-
|
|
258
|
+
function App() @{
|
|
259
|
+
<>
|
|
189
260
|
<div>
|
|
190
261
|
const a = 1;
|
|
191
|
-
<div>
|
|
192
|
-
|
|
193
|
-
</div>
|
|
194
|
-
<div>
|
|
195
|
-
const b = 1;
|
|
196
|
-
</div>
|
|
262
|
+
<div>const b = 1;</div>
|
|
263
|
+
<div>const b = 1;</div>
|
|
197
264
|
</div>
|
|
198
265
|
<div>
|
|
199
266
|
const a = 2;
|
|
200
|
-
<div>
|
|
201
|
-
const b = 1;
|
|
202
|
-
</div>
|
|
267
|
+
<div>const b = 1;</div>
|
|
203
268
|
</div>
|
|
204
|
-
|
|
269
|
+
</>
|
|
205
270
|
}
|
|
206
271
|
|
|
207
272
|
render(App);
|
|
208
273
|
});
|
|
209
274
|
|
|
210
275
|
it('renders without crashing using mapped types', () => {
|
|
211
|
-
function App() {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
};
|
|
233
|
-
</>;
|
|
276
|
+
function App() @{
|
|
277
|
+
type RecordKey = 'test';
|
|
278
|
+
type RecordValue = { a: string; b: number };
|
|
279
|
+
const config: Record<RecordKey, RecordValue> = {
|
|
280
|
+
test: {
|
|
281
|
+
a: 'test',
|
|
282
|
+
b: 1,
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
const config2: { [key in RecordKey]: RecordValue } = {
|
|
286
|
+
test: {
|
|
287
|
+
a: 'test2',
|
|
288
|
+
b: 2,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
const config3: { [key: string]: RecordValue } = {
|
|
292
|
+
test: {
|
|
293
|
+
a: 'test3',
|
|
294
|
+
b: 3,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
234
297
|
}
|
|
235
298
|
|
|
236
299
|
render(App);
|
|
237
300
|
});
|
|
238
301
|
|
|
239
302
|
it('renders without crashing using object destructuring', () => {
|
|
240
|
-
function App() {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
</div>
|
|
254
|
-
</>;
|
|
303
|
+
function App() @{
|
|
304
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
305
|
+
const { a, b, ...rest } = obj;
|
|
306
|
+
<div>
|
|
307
|
+
{'a '}
|
|
308
|
+
{a}
|
|
309
|
+
{'b '}
|
|
310
|
+
{b}
|
|
311
|
+
{'rest '}
|
|
312
|
+
{JSON.stringify(rest)}
|
|
313
|
+
|
|
314
|
+
<div />
|
|
315
|
+
</div>
|
|
255
316
|
}
|
|
256
317
|
|
|
257
318
|
render(App);
|
|
258
319
|
});
|
|
259
320
|
|
|
260
321
|
it('renders without crashing using object destructuring #2', () => {
|
|
261
|
-
function App() {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
322
|
+
function App() @{
|
|
323
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
324
|
+
const { a, b, ...rest } = obj;
|
|
325
|
+
<>
|
|
265
326
|
{'a '}
|
|
266
327
|
{a}
|
|
267
328
|
{'b '}
|
|
@@ -269,7 +330,7 @@ describe('compiler > basics', () => {
|
|
|
269
330
|
{'rest '}
|
|
270
331
|
{JSON.stringify(rest)}
|
|
271
332
|
<div />
|
|
272
|
-
|
|
333
|
+
</>
|
|
273
334
|
}
|
|
274
335
|
|
|
275
336
|
render(App);
|
|
@@ -288,35 +349,33 @@ describe('compiler > basics', () => {
|
|
|
288
349
|
};
|
|
289
350
|
}
|
|
290
351
|
|
|
291
|
-
function App() {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
352
|
+
function App() @{
|
|
353
|
+
let x: number[] = [] as number[];
|
|
354
|
+
const n = Wrapper<number>().unwrap<string>();
|
|
355
|
+
const tagResult = tagFn`value`;
|
|
356
|
+
interface Node<T> {
|
|
357
|
+
value: T;
|
|
358
|
+
}
|
|
359
|
+
class Box<T> {
|
|
360
|
+
value: T;
|
|
361
|
+
|
|
362
|
+
method<U extends T>(): U {
|
|
363
|
+
return this.value as U;
|
|
298
364
|
}
|
|
299
|
-
class Box<T> {
|
|
300
|
-
value: T;
|
|
301
365
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
constructor(value: T) {
|
|
307
|
-
this.value = value;
|
|
308
|
-
}
|
|
366
|
+
constructor(value: T) {
|
|
367
|
+
this.value = value;
|
|
309
368
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
369
|
+
}
|
|
370
|
+
let flag = true;
|
|
371
|
+
const s = flag ? new Box<number>(1) : new Box<string>('string');
|
|
313
372
|
}
|
|
314
373
|
|
|
315
374
|
render(App);
|
|
316
375
|
});
|
|
317
376
|
|
|
318
377
|
it('compiles without needing semicolons between statements and JSX', () => {
|
|
319
|
-
const source = `export function App() {
|
|
378
|
+
const source = `export function App() @{
|
|
320
379
|
<div>const code4 = 4
|
|
321
380
|
|
|
322
381
|
const code3 = 3
|
|
@@ -327,16 +386,18 @@ describe('compiler > basics', () => {
|
|
|
327
386
|
const code2 = 2
|
|
328
387
|
</div>
|
|
329
388
|
</div>
|
|
330
|
-
|
|
389
|
+
}`;
|
|
331
390
|
|
|
332
391
|
const result = compile(source, 'test.tsrx', { mode: 'client' });
|
|
333
392
|
});
|
|
334
393
|
|
|
335
394
|
it('calculates fragment hop count for sibling fragments', () => {
|
|
336
|
-
const source = `export function App() {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
395
|
+
const source = `export function App() @{
|
|
396
|
+
<>
|
|
397
|
+
<div class="a">{'a'}</div>
|
|
398
|
+
<div class="b">{'b'}</div>
|
|
399
|
+
</>
|
|
400
|
+
}`;
|
|
340
401
|
|
|
341
402
|
const { code } = compile(source, 'grouped-count.tsrx', { mode: 'client' });
|
|
342
403
|
|
|
@@ -346,9 +407,9 @@ describe('compiler > basics', () => {
|
|
|
346
407
|
|
|
347
408
|
it('emits anonymous component expressions as arrows in client output', () => {
|
|
348
409
|
const source = `
|
|
349
|
-
const Inline = (props) =>
|
|
410
|
+
const Inline = (props) => @{
|
|
350
411
|
<div>{props.x}</div>
|
|
351
|
-
|
|
412
|
+
}
|
|
352
413
|
`;
|
|
353
414
|
const result = compile(source, 'anonymous-component.tsrx', { mode: 'client' }).code;
|
|
354
415
|
|
|
@@ -361,9 +422,9 @@ const Inline = (props) => <>
|
|
|
361
422
|
|
|
362
423
|
it('emits function-expression components as functions in client output', () => {
|
|
363
424
|
const source = `
|
|
364
|
-
const Inline = function(props) {
|
|
425
|
+
const Inline = function(props) @{
|
|
365
426
|
<div>{props.x}</div>
|
|
366
|
-
|
|
427
|
+
}
|
|
367
428
|
`;
|
|
368
429
|
const result = compile(source, 'anonymous-component.tsrx', { mode: 'client' }).code;
|
|
369
430
|
|
|
@@ -376,17 +437,17 @@ const Inline = function(props) { return <>
|
|
|
376
437
|
|
|
377
438
|
it('emits function calls with nested template returns as expressions in client output', () => {
|
|
378
439
|
const source = `
|
|
379
|
-
function App() {
|
|
440
|
+
function App() @{
|
|
380
441
|
function make(flag) {
|
|
381
442
|
if (flag) {
|
|
382
|
-
return
|
|
443
|
+
return <span>{'nested'}</span>;
|
|
383
444
|
}
|
|
384
445
|
|
|
385
446
|
return null;
|
|
386
447
|
}
|
|
387
448
|
|
|
388
449
|
<div>{make(true)}</div>
|
|
389
|
-
|
|
450
|
+
}
|
|
390
451
|
`;
|
|
391
452
|
const result = compile(source, 'nested-template-return.tsrx', { mode: 'client' }).code;
|
|
392
453
|
|
|
@@ -398,11 +459,11 @@ function App() { return <>
|
|
|
398
459
|
// () => {
|
|
399
460
|
// const source = `
|
|
400
461
|
// import { RippleArray, RippleObject, RippleSet, RippleMap, createRefKey } from 'ripple';
|
|
401
|
-
// function App() {
|
|
462
|
+
// function App() @{
|
|
402
463
|
// const items = new RippleArray(1, 2, 3);
|
|
403
464
|
// const obj = new RippleObject({ a: 1, b: 2, c: 3 });
|
|
404
|
-
// const set = RippleSet([1, 2, 3]);
|
|
405
|
-
// const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
465
|
+
// const set = new RippleSet([1, 2, 3]);
|
|
466
|
+
// const map = new RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
406
467
|
|
|
407
468
|
// <div ref={() => {}} />
|
|
408
469
|
// }
|
|
@@ -422,11 +483,11 @@ function App() { return <>
|
|
|
422
483
|
// () => {
|
|
423
484
|
// const source = `
|
|
424
485
|
// import { RippleArray as TA, RippleObject as TO, RippleSet as TS, RippleMap as TM, createRefKey as crk } from 'ripple';
|
|
425
|
-
// function App() {
|
|
486
|
+
// function App() @{
|
|
426
487
|
// const items = new RippleArray(1, 2, 3);
|
|
427
488
|
// const obj = new RippleObject({ a: 1, b: 2, c: 3 });
|
|
428
|
-
// const set = RippleSet([1, 2, 3]);
|
|
429
|
-
// const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
489
|
+
// const set = new RippleSet([1, 2, 3]);
|
|
490
|
+
// const map = new RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
430
491
|
|
|
431
492
|
// <div ref={() => {}} />
|
|
432
493
|
// }
|
|
@@ -448,11 +509,11 @@ function App() { return <>
|
|
|
448
509
|
|
|
449
510
|
// it('adds hidden obfuscated imports for shorthand syntax', () => {
|
|
450
511
|
// const source = `
|
|
451
|
-
// function App() {
|
|
512
|
+
// function App() @{
|
|
452
513
|
// const items = new RippleArray(1, 2, 3);
|
|
453
514
|
// const obj = new RippleObject({ a: 1, b: 2, c: 3 });
|
|
454
|
-
// const set = RippleSet([1, 2, 3]);
|
|
455
|
-
// const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
515
|
+
// const set = new RippleSet([1, 2, 3]);
|
|
516
|
+
// const map = new RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
456
517
|
|
|
457
518
|
// <div ref={() => {}} />
|
|
458
519
|
// }
|
|
@@ -469,7 +530,7 @@ function App() { return <>
|
|
|
469
530
|
it('prints longhand tracked property values in to_ts output while preserving [\'#v\']', () => {
|
|
470
531
|
const source = `
|
|
471
532
|
import { RippleArray, RippleMap, RippleObject, RippleSet, createRefKey, effect, track, untrack } from 'ripple';
|
|
472
|
-
function App() {
|
|
533
|
+
function App() @{
|
|
473
534
|
let value = track('test');
|
|
474
535
|
function inputRef(node) {}
|
|
475
536
|
|
|
@@ -478,7 +539,7 @@ function App() { return <>
|
|
|
478
539
|
value: value.value,
|
|
479
540
|
[createRefKey()]: inputRef,
|
|
480
541
|
};
|
|
481
|
-
|
|
542
|
+
}
|
|
482
543
|
`;
|
|
483
544
|
|
|
484
545
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
@@ -489,10 +550,10 @@ function App() { return <>
|
|
|
489
550
|
it('keeps lazy destructuring as plain destructuring in to_ts output', () => {
|
|
490
551
|
const track_source = `
|
|
491
552
|
import { track } from 'ripple';
|
|
492
|
-
function App() {
|
|
553
|
+
function App() @{
|
|
493
554
|
let &[value, ...rest] = track(0);
|
|
494
555
|
const x = value;
|
|
495
|
-
|
|
556
|
+
}
|
|
496
557
|
`;
|
|
497
558
|
const track_result = compile_to_volar_mappings(track_source, 'test.tsrx').code;
|
|
498
559
|
expect(track_result).toContain('let [value, ...rest] = track(0);');
|
|
@@ -505,7 +566,7 @@ function App() { return <>
|
|
|
505
566
|
|
|
506
567
|
it('lowers native expression values in to_ts output', () => {
|
|
507
568
|
const source = `
|
|
508
|
-
function App() {
|
|
569
|
+
function App() @{
|
|
509
570
|
const nested = <>
|
|
510
571
|
<span class="nested-tsx">
|
|
511
572
|
{'inside nested tsx'}
|
|
@@ -513,25 +574,25 @@ function App() { return <>
|
|
|
513
574
|
</>;
|
|
514
575
|
const content = <div class="native">{nested}</div>;
|
|
515
576
|
|
|
516
|
-
{content}
|
|
517
|
-
|
|
577
|
+
<>{content}</>
|
|
578
|
+
}
|
|
518
579
|
`;
|
|
519
580
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
520
581
|
|
|
521
582
|
expect(result).toContain('const nested = <span class="nested-tsx">');
|
|
522
|
-
expect(result).toContain('return
|
|
583
|
+
expect(result).toContain('return content;');
|
|
523
584
|
expect(result).toContain('const content = <div class="native">');
|
|
524
585
|
});
|
|
525
586
|
|
|
526
587
|
it('keeps assigned style blocks anchored in to_ts output', () => {
|
|
527
588
|
const source = `
|
|
528
|
-
function App() {
|
|
589
|
+
function App() @{
|
|
529
590
|
const styles = <style>
|
|
530
591
|
.logo { display: block; }
|
|
531
592
|
</style>;
|
|
532
593
|
|
|
533
594
|
<div class={styles.logo} />
|
|
534
|
-
|
|
595
|
+
}
|
|
535
596
|
`;
|
|
536
597
|
const result = compile_to_volar_mappings(source, 'test.tsrx', { loose: true });
|
|
537
598
|
const source_offset = source.indexOf('<style>') + 1;
|
|
@@ -555,7 +616,7 @@ function App() { return <>
|
|
|
555
616
|
|
|
556
617
|
it('maps identifiers from native expression values in to_ts output', () => {
|
|
557
618
|
const source = `
|
|
558
|
-
function App() {
|
|
619
|
+
function App() @{
|
|
559
620
|
const nested = <>
|
|
560
621
|
<span class="nested-tsx">
|
|
561
622
|
{'inside nested tsx'}
|
|
@@ -563,14 +624,14 @@ function App() { return <>
|
|
|
563
624
|
</>;
|
|
564
625
|
const content = <div class="native">{nested}</div>;
|
|
565
626
|
|
|
566
|
-
{content}
|
|
567
|
-
|
|
627
|
+
<>{content}</>
|
|
628
|
+
}
|
|
568
629
|
`;
|
|
569
630
|
const result = compile_to_volar_mappings(source, 'test.tsrx', { loose: true });
|
|
570
631
|
const source_declaration = source.indexOf('nested =');
|
|
571
632
|
const source_reference = source.indexOf('nested}</div>');
|
|
572
633
|
const generated_declaration = result.code.indexOf('const nested') + 'const '.length;
|
|
573
|
-
const generated_reference = result.code.indexOf('nested
|
|
634
|
+
const generated_reference = result.code.indexOf('nested}</div>', generated_declaration);
|
|
574
635
|
|
|
575
636
|
function find_mapping(source_offset: number, generated_offset: number) {
|
|
576
637
|
return result.mappings.find(
|
|
@@ -611,9 +672,9 @@ type Props<Item> = {
|
|
|
611
672
|
items: readonly Item[];
|
|
612
673
|
}
|
|
613
674
|
|
|
614
|
-
export function MyComponent<Item>(props: Props<Item>) {
|
|
675
|
+
export function MyComponent<Item>(props: Props<Item>) @{
|
|
615
676
|
<div />
|
|
616
|
-
|
|
677
|
+
}
|
|
617
678
|
`;
|
|
618
679
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
619
680
|
|
|
@@ -622,22 +683,23 @@ export function MyComponent<Item>(props: Props<Item>) { return <>
|
|
|
622
683
|
|
|
623
684
|
it('preserves arrow functions that return TSRX in to_ts output', () => {
|
|
624
685
|
const source = `
|
|
625
|
-
const Inline = (props: { x: string }) =>
|
|
686
|
+
const Inline = (props: { x: string }) => @{
|
|
626
687
|
<div>{props.x}</div>
|
|
627
|
-
|
|
688
|
+
}
|
|
628
689
|
`;
|
|
629
690
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
630
691
|
|
|
631
|
-
expect(result).toContain('const Inline = (props: { x: string }) =>
|
|
692
|
+
expect(result).toContain('const Inline = (props: { x: string }) => {');
|
|
693
|
+
expect(result).toContain('return <div>');
|
|
632
694
|
expect(result).not.toContain('function Inline');
|
|
633
695
|
expect(result).not.toContain('function (props');
|
|
634
696
|
});
|
|
635
697
|
|
|
636
698
|
it('preserves function expressions that return TSRX in to_ts output', () => {
|
|
637
699
|
const source = `
|
|
638
|
-
const Inline = function(props: { x: string }) {
|
|
700
|
+
const Inline = function(props: { x: string }) @{
|
|
639
701
|
<div>{props.x}</div>
|
|
640
|
-
|
|
702
|
+
}
|
|
641
703
|
`;
|
|
642
704
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
643
705
|
|
|
@@ -650,13 +712,13 @@ const Inline = function(props: { x: string }) { return <>
|
|
|
650
712
|
const source = `
|
|
651
713
|
type User = { name: string };
|
|
652
714
|
|
|
653
|
-
function RenderProp<Item>(props: { children: (item: Item) => any }) {
|
|
715
|
+
function RenderProp<Item>(props: { children: (item: Item) => any }) @{}
|
|
654
716
|
|
|
655
|
-
export function App() {
|
|
717
|
+
export function App() @{
|
|
656
718
|
<RenderProp<User>>
|
|
657
719
|
{(item) => item.name}
|
|
658
720
|
</RenderProp>
|
|
659
|
-
|
|
721
|
+
}
|
|
660
722
|
`;
|
|
661
723
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
662
724
|
|
|
@@ -665,13 +727,13 @@ export function App() { return <>
|
|
|
665
727
|
|
|
666
728
|
it('preserves generic type arguments on self-closing JSX component tags in to_ts output', () => {
|
|
667
729
|
const source = `
|
|
668
|
-
function Box<T>({ value }: { value: T }) {
|
|
730
|
+
function Box<T>({ value }: { value: T }) @{
|
|
669
731
|
<div>{String(value)}</div>
|
|
670
|
-
|
|
732
|
+
}
|
|
671
733
|
|
|
672
|
-
export function App() {
|
|
734
|
+
export function App() @{
|
|
673
735
|
<Box<string> value="hi" />
|
|
674
|
-
|
|
736
|
+
}
|
|
675
737
|
`;
|
|
676
738
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
677
739
|
|
|
@@ -771,9 +833,9 @@ interface Props extends PolymorphicProps<'div'> {
|
|
|
771
833
|
id: string;
|
|
772
834
|
}
|
|
773
835
|
|
|
774
|
-
export function App(props: Props) {
|
|
836
|
+
export function App(props: Props) @{
|
|
775
837
|
<div id={props.id} />
|
|
776
|
-
|
|
838
|
+
}
|
|
777
839
|
`;
|
|
778
840
|
|
|
779
841
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
@@ -784,7 +846,7 @@ export function App(props: Props) { return <>
|
|
|
784
846
|
it('handles if-else expression statements in Volar mappings', () => {
|
|
785
847
|
const source = `
|
|
786
848
|
import { track } from 'ripple';
|
|
787
|
-
export function App() {
|
|
849
|
+
export function App() @{
|
|
788
850
|
let &[level] = track(1);
|
|
789
851
|
|
|
790
852
|
<button
|
|
@@ -796,58 +858,61 @@ export function App() { return <>
|
|
|
796
858
|
>
|
|
797
859
|
{'Toggle'}
|
|
798
860
|
</button>
|
|
799
|
-
|
|
861
|
+
}
|
|
800
862
|
`;
|
|
801
863
|
|
|
802
864
|
expect(() => compile_to_volar_mappings(source, 'test.tsrx')).not.toThrow();
|
|
803
865
|
});
|
|
804
866
|
|
|
805
|
-
it('
|
|
867
|
+
it('errors on having js below markup in the same scope', () => {
|
|
806
868
|
const code = `
|
|
807
|
-
function Card(props) {
|
|
869
|
+
function Card(props) @{
|
|
808
870
|
<div class="card">
|
|
809
871
|
{props.children}
|
|
810
872
|
</div>
|
|
811
|
-
|
|
873
|
+
}
|
|
812
874
|
|
|
813
|
-
export function App() {
|
|
814
|
-
function children() {
|
|
875
|
+
export function App() @{
|
|
876
|
+
function children() @{
|
|
815
877
|
<p>{'Card content here'}</p>
|
|
816
|
-
|
|
878
|
+
}
|
|
817
879
|
|
|
818
880
|
<Card {children} />
|
|
819
881
|
|
|
820
882
|
const test = 5;
|
|
821
883
|
|
|
822
884
|
<div>{test}</div>
|
|
823
|
-
|
|
885
|
+
}
|
|
824
886
|
`;
|
|
825
|
-
expect(() => compile(code, 'test.tsrx')).
|
|
887
|
+
expect(() => compile(code, 'test.tsrx')).toThrow(
|
|
888
|
+
/Code must be at the top of '@\{ \}'; statements cannot follow the rendered output/,
|
|
889
|
+
);
|
|
826
890
|
});
|
|
827
891
|
|
|
828
|
-
it('allows component functions
|
|
892
|
+
it('allows component functions passed to composite children', () => {
|
|
829
893
|
const source = `
|
|
830
|
-
export function App() {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
894
|
+
export function App() @{
|
|
895
|
+
function asChild({ children, href, ...rest }: { href: string; [key: string]: any }) @{
|
|
896
|
+
<a id="aschild-anchor" {href} {...rest} data-extra="yes">{'Link'}</a>
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
<ark.div class="host-class" data-value="42" {asChild} />
|
|
900
|
+
}
|
|
837
901
|
`;
|
|
838
902
|
|
|
839
903
|
expect(() => compile_to_volar_mappings(source, 'test.tsrx')).not.toThrow();
|
|
840
904
|
});
|
|
841
905
|
|
|
842
|
-
it('allows parent element attributes referencing
|
|
906
|
+
it('allows parent element attributes referencing setup-declared functions', () => {
|
|
843
907
|
const source = `
|
|
844
|
-
export function App() {
|
|
908
|
+
export function App() @{
|
|
909
|
+
function Z() @{
|
|
910
|
+
<div>{'hello'}</div>
|
|
911
|
+
}
|
|
912
|
+
|
|
845
913
|
<Test {Z}>
|
|
846
|
-
function Z() { return <>
|
|
847
|
-
<div>{'hello'}</div>
|
|
848
|
-
</>; }
|
|
849
914
|
</Test>
|
|
850
|
-
|
|
915
|
+
}
|
|
851
916
|
`;
|
|
852
917
|
|
|
853
918
|
expect(() => compile(source, 'test.tsrx')).not.toThrow();
|
|
@@ -855,13 +920,13 @@ export function App() { return <>
|
|
|
855
920
|
|
|
856
921
|
it('preserves explicit component props in Volar mappings', () => {
|
|
857
922
|
const source = `
|
|
858
|
-
export function App() {
|
|
859
|
-
function asChild({ children, href, ...rest }: { href: string; [key: string]: any }) {
|
|
923
|
+
export function App() @{
|
|
924
|
+
function asChild({ children, href, ...rest }: { href: string; [key: string]: any }) @{
|
|
860
925
|
<a id="aschild-anchor" {href} {...rest} data-extra="yes">{'Link'}</a>
|
|
861
|
-
|
|
926
|
+
}
|
|
862
927
|
|
|
863
928
|
<ark.div class="host-class" data-value="42" {asChild} />
|
|
864
|
-
|
|
929
|
+
}
|
|
865
930
|
`;
|
|
866
931
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
867
932
|
|
|
@@ -869,19 +934,58 @@ export function App() { return <>
|
|
|
869
934
|
expect(result).not.toContain('children={() =>');
|
|
870
935
|
});
|
|
871
936
|
|
|
937
|
+
it('uses direct TSRX render expressions for component children in Volar mappings', () => {
|
|
938
|
+
const source = `
|
|
939
|
+
function OtherComponent({ children }: { children: string }) @{
|
|
940
|
+
<div>{children}</div>
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
export const App = () => @{
|
|
944
|
+
<>
|
|
945
|
+
@try {
|
|
946
|
+
<ComponentThatSuspends />
|
|
947
|
+
} @pending {
|
|
948
|
+
<div>Loading</div>
|
|
949
|
+
}
|
|
950
|
+
<OtherComponent children={'I like TSRX'} />
|
|
951
|
+
</>
|
|
952
|
+
};
|
|
953
|
+
`;
|
|
954
|
+
const result = compile_to_volar_mappings(source, 'test.tsrx', { loose: true });
|
|
955
|
+
const source_offset = source.indexOf('\'I like TSRX\'');
|
|
956
|
+
const generated_offset = result.code.indexOf('\'I like TSRX\'');
|
|
957
|
+
|
|
958
|
+
expect(result.code).toContain('<OtherComponent children={\'I like TSRX\'}');
|
|
959
|
+
expect(result.code).not.toContain('children={() =>');
|
|
960
|
+
expect(
|
|
961
|
+
result.mappings.find(
|
|
962
|
+
(mapping: {
|
|
963
|
+
sourceOffsets: number[];
|
|
964
|
+
generatedOffsets: number[];
|
|
965
|
+
lengths: number[];
|
|
966
|
+
generatedLengths: number[];
|
|
967
|
+
}) =>
|
|
968
|
+
mapping.sourceOffsets[0] === source_offset &&
|
|
969
|
+
mapping.generatedOffsets[0] === generated_offset &&
|
|
970
|
+
mapping.lengths[0] === '\'I like TSRX\''.length &&
|
|
971
|
+
mapping.generatedLengths[0] === '\'I like TSRX\''.length,
|
|
972
|
+
),
|
|
973
|
+
).toBeDefined();
|
|
974
|
+
});
|
|
975
|
+
|
|
872
976
|
it('merges explicit children prop with implicit children in client output', () => {
|
|
873
977
|
const source = `
|
|
874
|
-
function Card(props) {
|
|
978
|
+
function Card(props) @{
|
|
875
979
|
<div>{props.children}</div>
|
|
876
|
-
|
|
980
|
+
}
|
|
877
981
|
|
|
878
|
-
export function App() {
|
|
982
|
+
export function App() @{
|
|
879
983
|
const fallback = 'fallback';
|
|
880
984
|
|
|
881
985
|
<Card children={fallback}>
|
|
882
986
|
<span>{'content'}</span>
|
|
883
987
|
</Card>
|
|
884
|
-
|
|
988
|
+
}
|
|
885
989
|
`;
|
|
886
990
|
|
|
887
991
|
const result = compile(source, 'test.tsrx', { mode: 'client' }).code;
|
|
@@ -900,10 +1004,10 @@ class Test {
|
|
|
900
1004
|
}
|
|
901
1005
|
}
|
|
902
1006
|
|
|
903
|
-
export function App() {
|
|
1007
|
+
export function App() @{
|
|
904
1008
|
const test = new Test();
|
|
905
1009
|
<div>{test.count}</div>
|
|
906
|
-
|
|
1010
|
+
}
|
|
907
1011
|
`;
|
|
908
1012
|
expect(() => compile(code, 'test.tsrx')).not.toThrow();
|
|
909
1013
|
});
|
|
@@ -919,10 +1023,10 @@ class Store {
|
|
|
919
1023
|
}
|
|
920
1024
|
}
|
|
921
1025
|
|
|
922
|
-
export function App() {
|
|
1026
|
+
export function App() @{
|
|
923
1027
|
const store = new Store();
|
|
924
1028
|
<div>{store.count}</div>
|
|
925
|
-
|
|
1029
|
+
}
|
|
926
1030
|
`;
|
|
927
1031
|
const result = compile(source, 'test.tsrx', { mode: 'client' });
|
|
928
1032
|
const code = result.code;
|
|
@@ -936,14 +1040,14 @@ export function App() { return <>
|
|
|
936
1040
|
const source = `
|
|
937
1041
|
import { track, effect, untrack } from 'ripple';
|
|
938
1042
|
|
|
939
|
-
function App() {
|
|
1043
|
+
function App() @{
|
|
940
1044
|
let &[count] = track(0);
|
|
941
1045
|
|
|
942
1046
|
effect(() => {
|
|
943
1047
|
const snapshot = untrack(() => count);
|
|
944
1048
|
console.log(snapshot);
|
|
945
1049
|
});
|
|
946
|
-
|
|
1050
|
+
}
|
|
947
1051
|
`;
|
|
948
1052
|
|
|
949
1053
|
const ast = parse(source);
|
|
@@ -956,10 +1060,10 @@ function App() { return <>
|
|
|
956
1060
|
it('collects duplicate declaration parser errors in loose mode', () => {
|
|
957
1061
|
const source = `
|
|
958
1062
|
import { track } from 'ripple';
|
|
959
|
-
export function App() {
|
|
1063
|
+
export function App() @{
|
|
960
1064
|
let test = track(false);
|
|
961
1065
|
let test = 'hey';
|
|
962
|
-
|
|
1066
|
+
}
|
|
963
1067
|
`;
|
|
964
1068
|
|
|
965
1069
|
expect(() => compile(source, 'test.tsrx')).toThrow(
|
|
@@ -985,11 +1089,11 @@ export function App() { return <>
|
|
|
985
1089
|
import { loadUser as getUser } from server;
|
|
986
1090
|
import { loadUser } from server;
|
|
987
1091
|
|
|
988
|
-
function App() {
|
|
1092
|
+
function App() @{
|
|
989
1093
|
const user = getUser();
|
|
990
1094
|
const user2 = loadUser();
|
|
991
1095
|
<div>{user.id}{user2.id}</div>
|
|
992
|
-
|
|
1096
|
+
}`;
|
|
993
1097
|
const result = compile_to_volar_mappings(source, 'test.tsrx', { loose: true });
|
|
994
1098
|
const generated_member = '_$_server_$_.loadUser';
|
|
995
1099
|
const generated_member_offset = result.code.indexOf(generated_member);
|
|
@@ -1045,49 +1149,21 @@ function App() { return <>
|
|
|
1045
1149
|
).toBeDefined();
|
|
1046
1150
|
});
|
|
1047
1151
|
|
|
1048
|
-
it('
|
|
1049
|
-
const result = compile_to_volar_mappings(
|
|
1050
|
-
`function App() { return <>
|
|
1152
|
+
it('allows return statements inside returned TSRX fragments in Volar mappings', () => {
|
|
1153
|
+
const result = compile_to_volar_mappings(`function App() @{
|
|
1051
1154
|
return <div />;
|
|
1052
|
-
|
|
1053
|
-
'test.tsrx',
|
|
1054
|
-
);
|
|
1055
|
-
|
|
1056
|
-
expect(result.errors.map((error) => error.code)).toEqual(['tsrx-template-return-statement']);
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
it('throws for unclosed tsx compat tags instead of hanging', () => {
|
|
1060
|
-
const source = `export function App() { return <tsx:react>1; }`;
|
|
1061
|
-
|
|
1062
|
-
expect(() => compile(source, 'test.tsrx')).toThrow(
|
|
1063
|
-
'Unclosed tag \'<tsx:react>\'. Expected \'</tsx:react>\' before end of template.',
|
|
1064
|
-
);
|
|
1065
|
-
});
|
|
1066
|
-
|
|
1067
|
-
it('recovers unclosed tsx compat tags in loose mode', () => {
|
|
1068
|
-
const source = `export function App() { return <tsx:react>1; }`;
|
|
1155
|
+
}`, 'test.tsrx');
|
|
1069
1156
|
|
|
1070
|
-
expect(() => compile_to_volar_mappings(source, 'test.tsrx', { loose: true })).not.toThrow();
|
|
1071
|
-
|
|
1072
|
-
const result = compile_to_volar_mappings(source, 'test.tsrx', { loose: true });
|
|
1073
1157
|
expect(result.errors).toEqual([]);
|
|
1074
1158
|
});
|
|
1075
1159
|
|
|
1076
|
-
it('throws for unclosed tsx compat tags outside loose mode', () => {
|
|
1077
|
-
const source = `export function App() { return <tsx:react>1; }`;
|
|
1078
|
-
|
|
1079
|
-
expect(() => compile(source, 'test.tsrx', { collect: true })).toThrow(
|
|
1080
|
-
'Not implemented: TsxCompat',
|
|
1081
|
-
);
|
|
1082
|
-
});
|
|
1083
|
-
|
|
1084
1160
|
it('collects analyzer errors outside loose mode', () => {
|
|
1085
1161
|
const source = `
|
|
1086
1162
|
import { track } from 'ripple';
|
|
1087
1163
|
|
|
1088
1164
|
const outside = track(0);
|
|
1089
1165
|
|
|
1090
|
-
export function App() {
|
|
1166
|
+
export function App() @{}
|
|
1091
1167
|
`;
|
|
1092
1168
|
|
|
1093
1169
|
const result = compile(source, 'test.tsrx', { collect: true });
|
|
@@ -1098,23 +1174,23 @@ export function App() { return <></>; }
|
|
|
1098
1174
|
|
|
1099
1175
|
it('does not let nested for...of continues satisfy an outer if body', () => {
|
|
1100
1176
|
const source = `
|
|
1101
|
-
export function App({ items }: { items: string[] }) {
|
|
1177
|
+
export function App({ items }: { items: string[] }) @{
|
|
1102
1178
|
if (items.length) {
|
|
1103
1179
|
for (const item of items) {
|
|
1104
1180
|
if (!item) continue
|
|
1105
1181
|
}
|
|
1106
1182
|
}
|
|
1107
|
-
|
|
1183
|
+
}`;
|
|
1108
1184
|
|
|
1109
1185
|
const result = compile(source, 'test.tsrx', { collect: true });
|
|
1110
|
-
expect(result.errors.map((error) => error.message)).toContain(
|
|
1186
|
+
expect(result.errors.map((error) => error.message)).not.toContain(
|
|
1111
1187
|
'Component if statements must contain a template in their "then" body. Move the if statement into an effect if it does not render anything.',
|
|
1112
1188
|
);
|
|
1113
1189
|
});
|
|
1114
1190
|
|
|
1115
1191
|
it('preserves class extends generic type arguments in volar output', () => {
|
|
1116
1192
|
const source = `class StringMap extends Map<string, string> {}
|
|
1117
|
-
export function App() {
|
|
1193
|
+
export function App() @{}`;
|
|
1118
1194
|
|
|
1119
1195
|
expect(() => compile_to_volar_mappings(source, 'test.tsrx', { loose: true })).not.toThrow();
|
|
1120
1196
|
|
|
@@ -1125,15 +1201,15 @@ export function App() { return <></>; }`;
|
|
|
1125
1201
|
|
|
1126
1202
|
it('wraps children in normalize_children for explicit children prop passed to component', () => {
|
|
1127
1203
|
const source = `
|
|
1128
|
-
function Card(props) {
|
|
1204
|
+
function Card(props) @{
|
|
1129
1205
|
<div>{props.children}</div>
|
|
1130
|
-
|
|
1206
|
+
}
|
|
1131
1207
|
|
|
1132
|
-
export function App() {
|
|
1208
|
+
export function App() @{
|
|
1133
1209
|
const content = 'hello';
|
|
1134
1210
|
|
|
1135
1211
|
<Card children={content} />
|
|
1136
|
-
|
|
1212
|
+
}
|
|
1137
1213
|
`;
|
|
1138
1214
|
|
|
1139
1215
|
const result = compile(source, 'test.tsrx', { mode: 'client' }).code;
|
|
@@ -1144,35 +1220,35 @@ export function App() { return <>
|
|
|
1144
1220
|
it(
|
|
1145
1221
|
'parses a JS statement inside an element with no trailing whitespace before the closing tag',
|
|
1146
1222
|
() => {
|
|
1147
|
-
const source = `function TodoList({ items }: { items: { text: string }[] }) {
|
|
1223
|
+
const source = `function TodoList({ items }: { items: { text: string }[] }) @{
|
|
1148
1224
|
<ul>var a = "123"</ul>
|
|
1149
|
-
|
|
1225
|
+
}`;
|
|
1150
1226
|
const ast = parse(source);
|
|
1151
|
-
const
|
|
1152
|
-
|
|
1153
|
-
|
|
1227
|
+
const returned = get_returned_tsrx(ast.body[0]);
|
|
1228
|
+
const ul =
|
|
1229
|
+
returned.type === 'Element'
|
|
1230
|
+
? returned
|
|
1231
|
+
: returned.children.find((n: AST.Node) => n.type === 'Element') as AST.Element;
|
|
1154
1232
|
expect((ul.id as AST.Identifier).name).toBe('ul');
|
|
1155
1233
|
expect(ul.children).toHaveLength(1);
|
|
1156
|
-
const
|
|
1157
|
-
expect(
|
|
1158
|
-
expect(
|
|
1159
|
-
expect((decl.declarations[0].id as AST.Identifier).name).toBe('a');
|
|
1160
|
-
expect((decl.declarations[0].init as AST.Literal).value).toBe('123');
|
|
1234
|
+
const text = ul.children[0] as unknown as { type: string; expression: AST.Literal };
|
|
1235
|
+
expect(text.type).toBe('Text');
|
|
1236
|
+
expect(text.expression.value).toBe('var a = "123"');
|
|
1161
1237
|
expect((ul.closingElement?.name as ESTreeJSX.JSXIdentifier)?.name).toBe('ul');
|
|
1162
1238
|
},
|
|
1163
1239
|
);
|
|
1164
1240
|
|
|
1165
1241
|
it('uses spread_props for spreads that may contain children', () => {
|
|
1166
1242
|
const source = `
|
|
1167
|
-
function Card(props) {
|
|
1243
|
+
function Card(props) @{
|
|
1168
1244
|
<div>{props.children}</div>
|
|
1169
|
-
|
|
1245
|
+
}
|
|
1170
1246
|
|
|
1171
|
-
export function App() {
|
|
1247
|
+
export function App() @{
|
|
1172
1248
|
const props = { children: 'hello' };
|
|
1173
1249
|
|
|
1174
1250
|
<Card {...props} />
|
|
1175
|
-
|
|
1251
|
+
}
|
|
1176
1252
|
`;
|
|
1177
1253
|
|
|
1178
1254
|
const result = compile(source, 'test.tsrx', { mode: 'client' }).code;
|
|
@@ -1180,19 +1256,12 @@ export function App() { return <>
|
|
|
1180
1256
|
expect(result).toContain('_$_.spread_props(');
|
|
1181
1257
|
});
|
|
1182
1258
|
|
|
1183
|
-
it('
|
|
1184
|
-
const source = `function TodoList({ items }: { items: { text: string }[] }) {
|
|
1259
|
+
it('rejects less-than comparisons at line start in element children without whitespace', () => {
|
|
1260
|
+
const source = `function TodoList({ items }: { items: { text: string }[] }) @{
|
|
1185
1261
|
<ul>var a = 3
|
|
1186
1262
|
<4;</ul>
|
|
1187
|
-
|
|
1263
|
+
}`;
|
|
1188
1264
|
|
|
1189
|
-
|
|
1190
|
-
const ul = get_returned_tsrx(ast.body[0]).children.find(
|
|
1191
|
-
(n: AST.Node) => n.type === 'Element',
|
|
1192
|
-
) as AST.Element;
|
|
1193
|
-
expect((ul.id as AST.Identifier).name).toBe('ul');
|
|
1194
|
-
expect(ul.children.length).toBeGreaterThanOrEqual(1);
|
|
1195
|
-
const decl = ul.children[0] as unknown as AST.VariableDeclaration;
|
|
1196
|
-
expect(decl.type).toBe('VariableDeclaration');
|
|
1265
|
+
expect(() => parse(source)).toThrow();
|
|
1197
1266
|
});
|
|
1198
1267
|
});
|