ripple 0.3.68 → 0.3.70
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 +57 -0
- package/package.json +3 -3
- package/src/jsx-runtime.d.ts +2 -2
- package/src/runtime/element.js +1 -1
- package/src/runtime/index-client.js +11 -11
- package/src/runtime/index-server.js +7 -4
- package/src/runtime/internal/client/bindings.js +1 -1
- package/src/runtime/internal/client/blocks.js +13 -4
- package/src/runtime/internal/client/component.js +55 -0
- package/src/runtime/internal/client/composite.js +4 -2
- package/src/runtime/internal/client/expression.js +65 -7
- package/src/runtime/internal/client/hmr.js +54 -43
- package/src/runtime/internal/client/index.js +5 -1
- package/src/runtime/internal/client/portal.js +70 -69
- package/src/runtime/internal/client/render.js +3 -0
- package/src/runtime/internal/server/index.js +92 -8
- package/tests/client/__snapshots__/html.test.tsrx.snap +3 -3
- package/tests/client/array/array.copy-within.test.tsrx +33 -31
- package/tests/client/array/array.derived.test.tsrx +186 -169
- package/tests/client/array/array.iteration.test.tsrx +40 -37
- package/tests/client/array/array.mutations.test.tsrx +113 -101
- package/tests/client/array/array.static.test.tsrx +119 -101
- package/tests/client/array/array.to-methods.test.tsrx +24 -21
- package/tests/client/async-suspend.test.tsrx +247 -246
- package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +0 -1
- package/tests/client/basic/basic.attributes.test.tsrx +428 -423
- package/tests/client/basic/basic.collections.test.tsrx +109 -102
- package/tests/client/basic/basic.components.test.tsrx +323 -205
- package/tests/client/basic/basic.errors.test.tsrx +91 -91
- package/tests/client/basic/basic.events.test.tsrx +114 -115
- package/tests/client/basic/basic.get-set.test.tsrx +97 -87
- package/tests/client/basic/basic.hmr.test.tsrx +19 -16
- package/tests/client/basic/basic.reactivity.test.tsrx +199 -191
- package/tests/client/basic/basic.rendering.test.tsrx +272 -182
- package/tests/client/basic/basic.styling.test.tsrx +23 -22
- package/tests/client/basic/basic.utilities.test.tsrx +10 -8
- package/tests/client/boundaries.test.tsrx +26 -26
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +5 -5
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +5 -5
- package/tests/client/compiler/compiler.assignments.test.tsrx +77 -81
- package/tests/client/compiler/compiler.attributes.test.tsrx +15 -15
- package/tests/client/compiler/compiler.basic.test.tsrx +322 -314
- package/tests/client/compiler/compiler.regex.test.tsrx +44 -47
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +38 -38
- package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
- package/tests/client/compiler/compiler.typescript.test.tsrx +2 -2
- package/tests/client/composite/composite.dynamic-components.test.tsrx +47 -48
- package/tests/client/composite/composite.generics.test.tsrx +168 -192
- package/tests/client/composite/composite.props.test.tsrx +97 -81
- package/tests/client/composite/composite.reactivity.test.tsrx +177 -147
- package/tests/client/composite/composite.render.test.tsrx +122 -105
- package/tests/client/computed-properties.test.tsrx +28 -28
- package/tests/client/context.test.tsrx +21 -21
- package/tests/client/css/global-additional-cases.test.tsrx +58 -58
- package/tests/client/css/global-advanced-selectors.test.tsrx +16 -16
- package/tests/client/css/global-at-rules.test.tsrx +10 -10
- package/tests/client/css/global-basic.test.tsrx +14 -14
- package/tests/client/css/global-classes-ids.test.tsrx +14 -14
- package/tests/client/css/global-combinators.test.tsrx +10 -10
- package/tests/client/css/global-complex-nesting.test.tsrx +14 -14
- package/tests/client/css/global-edge-cases.test.tsrx +18 -18
- package/tests/client/css/global-keyframes.test.tsrx +12 -12
- package/tests/client/css/global-nested.test.tsrx +10 -10
- package/tests/client/css/global-pseudo.test.tsrx +12 -12
- package/tests/client/css/global-scoping.test.tsrx +20 -20
- package/tests/client/css/style-identifier.test.tsrx +126 -259
- package/tests/client/date.test.tsrx +146 -133
- package/tests/client/dynamic-elements.test.tsrx +398 -365
- package/tests/client/events.test.tsrx +292 -290
- package/tests/client/for.test.tsrx +156 -153
- package/tests/client/head.test.tsrx +105 -96
- package/tests/client/html.test.tsrx +122 -26
- package/tests/client/input-value.test.tsrx +1361 -1314
- package/tests/client/lazy-array.test.tsrx +16 -13
- package/tests/client/lazy-destructuring.test.tsrx +257 -213
- package/tests/client/map.test.tsrx +65 -60
- package/tests/client/media-query.test.tsrx +22 -20
- package/tests/client/object.test.tsrx +87 -81
- package/tests/client/portal.test.tsrx +57 -51
- package/tests/client/ref.test.tsrx +233 -202
- package/tests/client/return.test.tsrx +71 -2560
- package/tests/client/set.test.tsrx +54 -45
- package/tests/client/svg.test.tsrx +216 -186
- package/tests/client/switch.test.tsrx +194 -193
- package/tests/client/track-async-hydration.test.tsrx +18 -14
- package/tests/client/tracked-index-access.test.tsrx +28 -18
- package/tests/client/try.test.tsrx +675 -548
- package/tests/client/tsx.test.tsrx +373 -311
- package/tests/client/typescript-generics.test.tsrx +145 -145
- package/tests/client/url/url.derived.test.tsrx +33 -28
- package/tests/client/url/url.parsing.test.tsrx +61 -51
- package/tests/client/url/url.partial-removal.test.tsrx +56 -48
- package/tests/client/url/url.reactivity.test.tsrx +142 -125
- package/tests/client/url/url.serialization.test.tsrx +13 -11
- package/tests/client/url-search-params/url-search-params.derived.test.tsrx +34 -29
- package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +25 -21
- package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +50 -45
- package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +111 -99
- package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +49 -43
- package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +14 -12
- package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +16 -14
- package/tests/hydration/basic.test.js +3 -3
- package/tests/hydration/compiled/client/basic.js +586 -651
- package/tests/hydration/compiled/client/composite.js +79 -104
- package/tests/hydration/compiled/client/events.js +140 -148
- package/tests/hydration/compiled/client/for.js +1005 -1018
- package/tests/hydration/compiled/client/head.js +124 -134
- package/tests/hydration/compiled/client/hmr.js +41 -48
- package/tests/hydration/compiled/client/html-in-template.js +38 -41
- package/tests/hydration/compiled/client/html.js +970 -1314
- package/tests/hydration/compiled/client/if-children.js +234 -249
- package/tests/hydration/compiled/client/if.js +182 -189
- package/tests/hydration/compiled/client/mixed-control-flow.js +347 -303
- package/tests/hydration/compiled/client/nested-control-flow.js +1084 -832
- package/tests/hydration/compiled/client/portal.js +65 -85
- package/tests/hydration/compiled/client/reactivity.js +84 -90
- package/tests/hydration/compiled/client/return.js +38 -1939
- package/tests/hydration/compiled/client/switch.js +218 -224
- package/tests/hydration/compiled/client/track-async-serialization.js +250 -259
- package/tests/hydration/compiled/client/try.js +123 -132
- package/tests/hydration/compiled/server/basic.js +773 -831
- package/tests/hydration/compiled/server/composite.js +166 -191
- package/tests/hydration/compiled/server/events.js +170 -184
- package/tests/hydration/compiled/server/for.js +851 -909
- package/tests/hydration/compiled/server/head.js +206 -216
- package/tests/hydration/compiled/server/hmr.js +64 -72
- package/tests/hydration/compiled/server/html-in-template.js +42 -76
- package/tests/hydration/compiled/server/html.js +1362 -1667
- package/tests/hydration/compiled/server/if-children.js +419 -445
- package/tests/hydration/compiled/server/if.js +194 -208
- package/tests/hydration/compiled/server/mixed-control-flow.js +249 -257
- package/tests/hydration/compiled/server/nested-control-flow.js +491 -515
- package/tests/hydration/compiled/server/portal.js +152 -160
- package/tests/hydration/compiled/server/reactivity.js +94 -106
- package/tests/hydration/compiled/server/return.js +28 -2172
- package/tests/hydration/compiled/server/switch.js +274 -286
- package/tests/hydration/compiled/server/track-async-serialization.js +340 -358
- package/tests/hydration/compiled/server/try.js +167 -185
- package/tests/hydration/components/basic.tsrx +320 -272
- package/tests/hydration/components/composite.tsrx +44 -32
- package/tests/hydration/components/events.tsrx +101 -91
- package/tests/hydration/components/for.tsrx +510 -452
- package/tests/hydration/components/head.tsrx +87 -80
- package/tests/hydration/components/hmr.tsrx +22 -17
- package/tests/hydration/components/html-in-template.tsrx +22 -17
- package/tests/hydration/components/html.tsrx +525 -443
- package/tests/hydration/components/if-children.tsrx +158 -148
- package/tests/hydration/components/if.tsrx +109 -95
- package/tests/hydration/components/mixed-control-flow.tsrx +100 -96
- package/tests/hydration/components/nested-control-flow.tsrx +215 -203
- package/tests/hydration/components/portal.tsrx +41 -34
- package/tests/hydration/components/reactivity.tsrx +37 -27
- package/tests/hydration/components/return.tsrx +12 -556
- package/tests/hydration/components/switch.tsrx +120 -114
- package/tests/hydration/components/track-async-serialization.tsrx +107 -91
- package/tests/hydration/components/try.tsrx +55 -40
- package/tests/hydration/html.test.js +4 -4
- package/tests/hydration/return.test.js +13 -532
- package/tests/server/await.test.tsrx +3 -3
- package/tests/server/basic.attributes.test.tsrx +264 -195
- package/tests/server/basic.components.test.tsrx +296 -169
- package/tests/server/basic.test.tsrx +300 -198
- package/tests/server/compiler.test.tsrx +62 -60
- package/tests/server/composite.props.test.tsrx +77 -63
- package/tests/server/composite.test.tsrx +168 -192
- package/tests/server/context.test.tsrx +18 -12
- package/tests/server/dynamic-elements.test.tsrx +197 -180
- package/tests/server/for.test.tsrx +85 -78
- package/tests/server/head.test.tsrx +50 -43
- package/tests/server/html-nesting-validation.test.tsrx +8 -8
- package/tests/server/if.test.tsrx +57 -51
- package/tests/server/lazy-destructuring.test.tsrx +366 -294
- package/tests/server/return.test.tsrx +76 -1355
- package/tests/server/streaming-ssr.test.tsrx +4 -75
- package/tests/server/style-identifier.test.tsrx +169 -131
- package/tests/server/switch.test.tsrx +91 -85
- package/tests/server/track-async-serialization.test.tsrx +105 -85
- package/tests/server/try.test.tsrx +374 -280
- package/tests/utils/compiler-compat-config.test.js +2 -2
- package/tests/utils/runtime-imports.test.js +10 -0
- package/types/index.d.ts +8 -0
- package/tests/client/__snapshots__/html.test.rsrx.snap +0 -40
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { parse, compile, compile_to_volar_mappings } from '@tsrx/ripple';
|
|
2
|
-
import { DIAGNOSTIC_CODES } from '@tsrx/core';
|
|
3
2
|
import type * as AST from 'estree';
|
|
4
3
|
import type * as ESTreeJSX from 'estree-jsx';
|
|
5
4
|
|
|
5
|
+
function get_returned_tsrx(node: any): any {
|
|
6
|
+
const target =
|
|
7
|
+
node.type === 'ExportNamedDeclaration' ? node.declaration : node;
|
|
8
|
+
const body =
|
|
9
|
+
target.type === 'VariableDeclarator' ? target.init.body : target.body;
|
|
10
|
+
|
|
11
|
+
if (body.type !== 'BlockStatement') {
|
|
12
|
+
return body;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return body.body.find((child: AST.Node) => child.type === 'ReturnStatement')?.argument;
|
|
16
|
+
}
|
|
17
|
+
|
|
6
18
|
function count_occurrences(string: string, subString: string): number {
|
|
7
19
|
let count = 0;
|
|
8
20
|
let pos = string.indexOf(subString);
|
|
@@ -17,231 +29,247 @@ function count_occurrences(string: string, subString: string): number {
|
|
|
17
29
|
|
|
18
30
|
describe('compiler > basics', () => {
|
|
19
31
|
it('parses style content correctly', () => {
|
|
20
|
-
const source = `export
|
|
32
|
+
const source = `export function App() { return <>
|
|
21
33
|
<div id="myid" class="myclass">{"Hello World"}</div>
|
|
22
34
|
|
|
23
35
|
<style>__STYLE__</style>
|
|
24
|
-
}`;
|
|
36
|
+
</>; }`;
|
|
25
37
|
const style1 = '.myid {color: green }';
|
|
26
38
|
const style2 = '#myid {color: green }';
|
|
27
39
|
const style3 = 'div {color: green }';
|
|
28
40
|
|
|
29
41
|
let input = source.replace('__STYLE__', style1);
|
|
30
42
|
let ast = parse(input);
|
|
31
|
-
expect(
|
|
32
|
-
((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
|
|
33
|
-
).toEqual(style1);
|
|
43
|
+
expect(get_returned_tsrx(ast.body[0]).children.at(-1).children[0].source).toEqual(style1);
|
|
34
44
|
|
|
35
45
|
input = source.replace('__STYLE__', style2);
|
|
36
46
|
ast = parse(input);
|
|
37
|
-
expect(
|
|
38
|
-
((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
|
|
39
|
-
).toEqual(style2);
|
|
47
|
+
expect(get_returned_tsrx(ast.body[0]).children.at(-1).children[0].source).toEqual(style2);
|
|
40
48
|
|
|
41
49
|
input = source.replace('__STYLE__', style3);
|
|
42
50
|
ast = parse(input);
|
|
43
|
-
expect(
|
|
44
|
-
((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
|
|
45
|
-
).toEqual(style3);
|
|
51
|
+
expect(get_returned_tsrx(ast.body[0]).children.at(-1).children[0].source).toEqual(style3);
|
|
46
52
|
});
|
|
47
53
|
|
|
48
|
-
it('parses
|
|
49
|
-
const source = `export
|
|
54
|
+
it('parses text as an ordinary expression identifier', () => {
|
|
55
|
+
const source = `export function App() { return <>
|
|
50
56
|
const markup = '<span>Not HTML</span>';
|
|
57
|
+
const text = markup;
|
|
51
58
|
|
|
52
59
|
<div>{markup}</div>
|
|
53
|
-
<div>{text
|
|
54
|
-
}`;
|
|
60
|
+
<div>{text}</div>
|
|
61
|
+
</>; }`;
|
|
55
62
|
|
|
56
63
|
const ast = parse(source);
|
|
57
|
-
const
|
|
58
|
-
|
|
64
|
+
const elements = get_returned_tsrx(ast.body[0]).children.filter(
|
|
65
|
+
(node: AST.Node) => node.type === 'Element',
|
|
66
|
+
) as AST.Element[];
|
|
59
67
|
const expression = elements[0].children[0] as AST.Node & { expression: AST.Expression };
|
|
60
|
-
const
|
|
68
|
+
const text_expression = elements[1].children[0] as AST.Node & { expression: AST.Expression };
|
|
61
69
|
|
|
62
70
|
expect(elements).toHaveLength(2);
|
|
63
71
|
expect(expression.type).toBe('TSRXExpression');
|
|
64
72
|
expect((expression.expression as AST.Identifier).name).toBe('markup');
|
|
65
|
-
expect(
|
|
66
|
-
expect((
|
|
73
|
+
expect(text_expression.type).toBe('TSRXExpression');
|
|
74
|
+
expect((text_expression.expression as AST.Identifier).name).toBe('text');
|
|
67
75
|
|
|
68
76
|
const { code } = compile(source, 'text-directive.tsrx', { mode: 'client' });
|
|
69
77
|
expect(code).not.toContain('_$_.html');
|
|
70
78
|
|
|
71
|
-
const invalid_source = `export
|
|
72
|
-
const
|
|
79
|
+
const invalid_source = `export function App() { return <>
|
|
80
|
+
const markup = 'plain';
|
|
73
81
|
|
|
74
|
-
<div>{text}</div>
|
|
75
|
-
}`;
|
|
82
|
+
<div>{text markup}</div>
|
|
83
|
+
</>; }`;
|
|
76
84
|
|
|
77
|
-
expect(() => parse(invalid_source)).toThrow(
|
|
78
|
-
'"text" is a TSRX keyword and must be used in the form {text some_value}',
|
|
79
|
-
);
|
|
85
|
+
expect(() => parse(invalid_source)).toThrow();
|
|
80
86
|
});
|
|
81
87
|
|
|
82
|
-
it('
|
|
83
|
-
const source = `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
it('optimizes string-shaped expressions as text nodes', () => {
|
|
89
|
+
const source = `export function App(
|
|
90
|
+
{ title, props }: { title: string; props: { label: string } },
|
|
91
|
+
) { return <>
|
|
92
|
+
const element = <span />;
|
|
93
|
+
|
|
94
|
+
<div>{String(title)}</div>
|
|
95
|
+
<div>{title + ''}</div>
|
|
96
|
+
<div>{title as string}</div>
|
|
97
|
+
<div>{title}</div>
|
|
98
|
+
<div>{props.label}</div>
|
|
99
|
+
<div>{element}</div>
|
|
100
|
+
</>; }`;
|
|
101
|
+
|
|
102
|
+
const { code } = compile(source, 'stringish-text.tsrx', { mode: 'client' });
|
|
103
|
+
|
|
104
|
+
expect(code).toContain('_$_.set_text');
|
|
105
|
+
expect(code).toContain('nodeValue = title');
|
|
106
|
+
expect(count_occurrences(code, '_$_.expression')).toBe(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('parses backtick expressions inside TSRX fragments as template literals', () => {
|
|
110
|
+
const source = `let a = function() {
|
|
111
|
+
return <>
|
|
112
|
+
<>
|
|
113
|
+
\`333\`
|
|
114
|
+
</>
|
|
115
|
+
</>;
|
|
87
116
|
}`;
|
|
88
117
|
|
|
89
118
|
const ast = parse(source);
|
|
90
119
|
const declaration = (ast.body[0] as AST.VariableDeclaration).declarations[0];
|
|
91
|
-
const
|
|
92
|
-
const fragment = component_node.body[0] as any;
|
|
120
|
+
const fragment = get_returned_tsrx(declaration).children[0] as any;
|
|
93
121
|
|
|
94
|
-
expect(fragment.type).toBe('
|
|
95
|
-
expect(fragment.children[0].type).toBe('
|
|
96
|
-
expect(fragment.children[0].
|
|
122
|
+
expect(fragment.type).toBe('Tsrx');
|
|
123
|
+
expect(fragment.children[0].type).toBe('ExpressionStatement');
|
|
124
|
+
expect(fragment.children[0].expression.type).toBe('TemplateLiteral');
|
|
125
|
+
expect(fragment.children[0].expression.quasis[0].value.raw).toBe('333');
|
|
97
126
|
});
|
|
98
127
|
|
|
99
|
-
it('parses backtick
|
|
100
|
-
const source = `let a =
|
|
101
|
-
<>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
it('parses backtick expressions around tag-like text inside TSRX fragments', () => {
|
|
129
|
+
const source = `let a = function() {
|
|
130
|
+
return <>
|
|
131
|
+
<>
|
|
132
|
+
\`
|
|
133
|
+
<b></b>
|
|
134
|
+
\`
|
|
135
|
+
</>
|
|
136
|
+
</>;
|
|
106
137
|
}`;
|
|
107
138
|
|
|
108
139
|
const ast = parse(source);
|
|
109
140
|
const declaration = (ast.body[0] as AST.VariableDeclaration).declarations[0];
|
|
110
|
-
const
|
|
111
|
-
const fragment = component_node.body[0] as any;
|
|
141
|
+
const fragment = get_returned_tsrx(declaration).children[0] as any;
|
|
112
142
|
|
|
113
|
-
expect(fragment.type).toBe('
|
|
143
|
+
expect(fragment.type).toBe('Tsrx');
|
|
114
144
|
expect(fragment.children.map((child: any) => child.type)).toEqual([
|
|
115
|
-
'
|
|
116
|
-
'JSXElement',
|
|
117
|
-
'JSXText',
|
|
145
|
+
'ExpressionStatement',
|
|
118
146
|
]);
|
|
119
|
-
expect(fragment.children[0].
|
|
120
|
-
expect(fragment.children[
|
|
121
|
-
expect(fragment.children[2].value).toContain('`');
|
|
147
|
+
expect(fragment.children[0].expression.type).toBe('TemplateLiteral');
|
|
148
|
+
expect(fragment.children[0].expression.quasis[0].value.raw).toContain('<b></b>');
|
|
122
149
|
});
|
|
123
150
|
|
|
124
151
|
it('renders without crashing', () => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
baz['jkl'] = 987;
|
|
152
|
+
function App() {
|
|
153
|
+
return <>
|
|
154
|
+
let foo: Record<string, number>;
|
|
155
|
+
let bar: Record<string, number>;
|
|
156
|
+
let baz: Record<string, number>;
|
|
157
|
+
foo = {};
|
|
158
|
+
foo = { test: 0 };
|
|
159
|
+
foo['abc'] = 123;
|
|
160
|
+
bar = { def: 456 };
|
|
161
|
+
baz = { ghi: 789 };
|
|
162
|
+
baz['jkl'] = 987;
|
|
163
|
+
</>;
|
|
138
164
|
}
|
|
139
165
|
|
|
140
166
|
render(App);
|
|
141
167
|
});
|
|
142
168
|
|
|
143
169
|
it('renders without crashing using < character', () => {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
170
|
+
function App() {
|
|
171
|
+
return <>
|
|
172
|
+
function bar() {
|
|
173
|
+
for (let i = 0; i < 10; i++) {
|
|
174
|
+
// do nothing
|
|
175
|
+
}
|
|
176
|
+
const x = 1 < 1;
|
|
148
177
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
let x = 5 < 10;
|
|
153
|
-
|
|
154
|
-
<div>{x}</div>
|
|
178
|
+
let x = 5 < 10;
|
|
179
|
+
<div>{x}</div>
|
|
180
|
+
</>;
|
|
155
181
|
}
|
|
156
182
|
|
|
157
183
|
render(App);
|
|
158
184
|
});
|
|
159
185
|
|
|
160
186
|
it('renders lexical blocks without crashing', () => {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const a = 1;
|
|
164
|
-
<div>
|
|
165
|
-
const b = 1;
|
|
166
|
-
</div>
|
|
187
|
+
function App() {
|
|
188
|
+
return <>
|
|
167
189
|
<div>
|
|
168
|
-
const
|
|
190
|
+
const a = 1;
|
|
191
|
+
<div>
|
|
192
|
+
const b = 1;
|
|
193
|
+
</div>
|
|
194
|
+
<div>
|
|
195
|
+
const b = 1;
|
|
196
|
+
</div>
|
|
169
197
|
</div>
|
|
170
|
-
</div>
|
|
171
|
-
<div>
|
|
172
|
-
const a = 2;
|
|
173
198
|
<div>
|
|
174
|
-
const
|
|
199
|
+
const a = 2;
|
|
200
|
+
<div>
|
|
201
|
+
const b = 1;
|
|
202
|
+
</div>
|
|
175
203
|
</div>
|
|
176
|
-
|
|
204
|
+
</>;
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
render(App);
|
|
180
208
|
});
|
|
181
209
|
|
|
182
210
|
it('renders without crashing using mapped types', () => {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
};
|
|
211
|
+
function App() {
|
|
212
|
+
return <>
|
|
213
|
+
type RecordKey = 'test';
|
|
214
|
+
type RecordValue = { a: string; b: number };
|
|
215
|
+
const config: Record<RecordKey, RecordValue> = {
|
|
216
|
+
test: {
|
|
217
|
+
a: 'test',
|
|
218
|
+
b: 1,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
const config2: { [key in RecordKey]: RecordValue } = {
|
|
222
|
+
test: {
|
|
223
|
+
a: 'test2',
|
|
224
|
+
b: 2,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
const config3: { [key: string]: RecordValue } = {
|
|
228
|
+
test: {
|
|
229
|
+
a: 'test3',
|
|
230
|
+
b: 3,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
</>;
|
|
207
234
|
}
|
|
208
235
|
|
|
209
236
|
render(App);
|
|
210
237
|
});
|
|
211
238
|
|
|
212
239
|
it('renders without crashing using object destructuring', () => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
240
|
+
function App() {
|
|
241
|
+
return <>
|
|
242
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
243
|
+
const { a, b, ...rest } = obj;
|
|
244
|
+
<div>
|
|
245
|
+
{'a '}
|
|
246
|
+
{a}
|
|
247
|
+
{'b '}
|
|
248
|
+
{b}
|
|
249
|
+
{'rest '}
|
|
250
|
+
{JSON.stringify(rest)}
|
|
251
|
+
|
|
252
|
+
<div />
|
|
253
|
+
</div>
|
|
254
|
+
</>;
|
|
255
|
+
}
|
|
216
256
|
|
|
217
|
-
|
|
257
|
+
render(App);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('renders without crashing using object destructuring #2', () => {
|
|
261
|
+
function App() {
|
|
262
|
+
return <>
|
|
263
|
+
const obj = { a: 1, b: 2, c: 3 };
|
|
264
|
+
const { a, b, ...rest } = obj;
|
|
218
265
|
{'a '}
|
|
219
266
|
{a}
|
|
220
267
|
{'b '}
|
|
221
268
|
{b}
|
|
222
269
|
{'rest '}
|
|
223
270
|
{JSON.stringify(rest)}
|
|
224
|
-
|
|
225
271
|
<div />
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
render(App);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('renders without crashing using object destructuring #2', () => {
|
|
233
|
-
component App() {
|
|
234
|
-
const obj = { a: 1, b: 2, c: 3 };
|
|
235
|
-
const { a, b, ...rest } = obj;
|
|
236
|
-
|
|
237
|
-
{'a '}
|
|
238
|
-
{a}
|
|
239
|
-
{'b '}
|
|
240
|
-
{b}
|
|
241
|
-
{'rest '}
|
|
242
|
-
{JSON.stringify(rest)}
|
|
243
|
-
|
|
244
|
-
<div />
|
|
272
|
+
</>;
|
|
245
273
|
}
|
|
246
274
|
|
|
247
275
|
render(App);
|
|
@@ -260,39 +288,35 @@ describe('compiler > basics', () => {
|
|
|
260
288
|
};
|
|
261
289
|
}
|
|
262
290
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
interface Node<T> {
|
|
271
|
-
value: T;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
class Box<T> {
|
|
275
|
-
value: T;
|
|
276
|
-
|
|
277
|
-
method<U extends T>(): U {
|
|
278
|
-
return this.value as U;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
constructor(value: T) {
|
|
282
|
-
this.value = value;
|
|
291
|
+
function App() {
|
|
292
|
+
return <>
|
|
293
|
+
let x: number[] = [] as number[];
|
|
294
|
+
const n = Wrapper<number>().unwrap<string>();
|
|
295
|
+
const tagResult = tagFn`value`;
|
|
296
|
+
interface Node<T> {
|
|
297
|
+
value: T;
|
|
283
298
|
}
|
|
284
|
-
|
|
299
|
+
class Box<T> {
|
|
300
|
+
value: T;
|
|
285
301
|
|
|
286
|
-
|
|
302
|
+
method<U extends T>(): U {
|
|
303
|
+
return this.value as U;
|
|
304
|
+
}
|
|
287
305
|
|
|
288
|
-
|
|
306
|
+
constructor(value: T) {
|
|
307
|
+
this.value = value;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
let flag = true;
|
|
311
|
+
const s = flag ? new Box<number>(1) : new Box<string>('string');
|
|
312
|
+
</>;
|
|
289
313
|
}
|
|
290
314
|
|
|
291
315
|
render(App);
|
|
292
316
|
});
|
|
293
317
|
|
|
294
318
|
it('compiles without needing semicolons between statements and JSX', () => {
|
|
295
|
-
const source = `export
|
|
319
|
+
const source = `export function App() { return <>
|
|
296
320
|
<div>const code4 = 4
|
|
297
321
|
|
|
298
322
|
const code3 = 3
|
|
@@ -303,56 +327,56 @@ describe('compiler > basics', () => {
|
|
|
303
327
|
const code2 = 2
|
|
304
328
|
</div>
|
|
305
329
|
</div>
|
|
306
|
-
}`;
|
|
330
|
+
</>; }`;
|
|
307
331
|
|
|
308
332
|
const result = compile(source, 'test.tsrx', { mode: 'client' });
|
|
309
333
|
});
|
|
310
334
|
|
|
311
|
-
it('calculates fragment hop count
|
|
312
|
-
const source = `export
|
|
313
|
-
let stop = false;
|
|
314
|
-
if (stop) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
335
|
+
it('calculates fragment hop count for sibling fragments', () => {
|
|
336
|
+
const source = `export function App() { return <>
|
|
317
337
|
<div class="a">{'a'}</div>
|
|
318
338
|
<div class="b">{'b'}</div>
|
|
319
|
-
}`;
|
|
339
|
+
</>; }`;
|
|
320
340
|
|
|
321
341
|
const { code } = compile(source, 'grouped-count.tsrx', { mode: 'client' });
|
|
322
342
|
|
|
323
|
-
expect(code).toMatch(/_\$_\.template\(
|
|
324
|
-
expect(code).not.toMatch(/_\$_\.template\(
|
|
343
|
+
expect(code).toMatch(/_\$_\.template\([\s\S]*,\s*1,\s*2\)/);
|
|
344
|
+
expect(code).not.toMatch(/_\$_\.template\([\s\S]*,\s*1,\s*3\)/);
|
|
325
345
|
});
|
|
326
346
|
|
|
327
347
|
it('emits anonymous component expressions as arrows in client output', () => {
|
|
328
348
|
const source = `
|
|
329
|
-
const Inline =
|
|
349
|
+
const Inline = (props) => <>
|
|
330
350
|
<div>{props.x}</div>
|
|
331
|
-
|
|
351
|
+
</>
|
|
332
352
|
`;
|
|
333
353
|
const result = compile(source, 'anonymous-component.tsrx', { mode: 'client' }).code;
|
|
334
354
|
|
|
335
|
-
expect(result).toContain('const Inline = (
|
|
355
|
+
expect(result).toContain('const Inline = (props) => {');
|
|
356
|
+
expect(result).toContain('(props) => {');
|
|
357
|
+
expect(result).toContain('return _$_.tsrx_element((__anchor, __block) =>');
|
|
336
358
|
expect(result).not.toContain('function Inline');
|
|
337
359
|
expect(result).not.toContain('function (__anchor');
|
|
338
360
|
});
|
|
339
361
|
|
|
340
|
-
it('emits
|
|
362
|
+
it('emits function-expression components as functions in client output', () => {
|
|
341
363
|
const source = `
|
|
342
|
-
const Inline =
|
|
364
|
+
const Inline = function(props) { return <>
|
|
343
365
|
<div>{props.x}</div>
|
|
344
|
-
}
|
|
366
|
+
</>; }
|
|
345
367
|
`;
|
|
346
368
|
const result = compile(source, 'anonymous-component.tsrx', { mode: 'client' }).code;
|
|
347
369
|
|
|
348
|
-
expect(result).toContain('const Inline = function (
|
|
370
|
+
expect(result).toContain('const Inline = function (props) {');
|
|
371
|
+
expect(result).toContain('function (props) {');
|
|
372
|
+
expect(result).toContain('return _$_.tsrx_element((__anchor, __block) =>');
|
|
349
373
|
expect(result).not.toContain('function Inline');
|
|
350
374
|
expect(result).not.toContain('const Inline = (__anchor, props, __block) => {');
|
|
351
375
|
});
|
|
352
376
|
|
|
353
377
|
it('emits function calls with nested template returns as expressions in client output', () => {
|
|
354
378
|
const source = `
|
|
355
|
-
|
|
379
|
+
function App() { return <>
|
|
356
380
|
function make(flag) {
|
|
357
381
|
if (flag) {
|
|
358
382
|
return <tsx><span>{'nested'}</span></tsx>;
|
|
@@ -362,7 +386,7 @@ component App() {
|
|
|
362
386
|
}
|
|
363
387
|
|
|
364
388
|
<div>{make(true)}</div>
|
|
365
|
-
}
|
|
389
|
+
</>; }
|
|
366
390
|
`;
|
|
367
391
|
const result = compile(source, 'nested-template-return.tsrx', { mode: 'client' }).code;
|
|
368
392
|
|
|
@@ -374,13 +398,13 @@ component App() {
|
|
|
374
398
|
// () => {
|
|
375
399
|
// const source = `
|
|
376
400
|
// import { RippleArray, RippleObject, RippleSet, RippleMap, createRefKey } from 'ripple';
|
|
377
|
-
//
|
|
401
|
+
// function App() { return <>
|
|
378
402
|
// const items = new RippleArray(1, 2, 3);
|
|
379
403
|
// const obj = new RippleObject({ a: 1, b: 2, c: 3 });
|
|
380
404
|
// const set = RippleSet([1, 2, 3]);
|
|
381
405
|
// const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
382
406
|
|
|
383
|
-
// <div {
|
|
407
|
+
// <div ref={() => {}} />
|
|
384
408
|
// }
|
|
385
409
|
// `;
|
|
386
410
|
// const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
@@ -398,13 +422,13 @@ component App() {
|
|
|
398
422
|
// () => {
|
|
399
423
|
// const source = `
|
|
400
424
|
// import { RippleArray as TA, RippleObject as TO, RippleSet as TS, RippleMap as TM, createRefKey as crk } from 'ripple';
|
|
401
|
-
//
|
|
425
|
+
// function App() { return <>
|
|
402
426
|
// const items = new RippleArray(1, 2, 3);
|
|
403
427
|
// const obj = new RippleObject({ a: 1, b: 2, c: 3 });
|
|
404
428
|
// const set = RippleSet([1, 2, 3]);
|
|
405
429
|
// const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
406
430
|
|
|
407
|
-
// <div {
|
|
431
|
+
// <div ref={() => {}} />
|
|
408
432
|
// }
|
|
409
433
|
// `;
|
|
410
434
|
// const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
@@ -424,13 +448,13 @@ component App() {
|
|
|
424
448
|
|
|
425
449
|
// it('adds hidden obfuscated imports for shorthand syntax', () => {
|
|
426
450
|
// const source = `
|
|
427
|
-
//
|
|
451
|
+
// function App() { return <>
|
|
428
452
|
// const items = new RippleArray(1, 2, 3);
|
|
429
453
|
// const obj = new RippleObject({ a: 1, b: 2, c: 3 });
|
|
430
454
|
// const set = RippleSet([1, 2, 3]);
|
|
431
455
|
// const map = RippleMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
432
456
|
|
|
433
|
-
// <div {
|
|
457
|
+
// <div ref={() => {}} />
|
|
434
458
|
// }
|
|
435
459
|
// `;
|
|
436
460
|
// const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
@@ -445,7 +469,7 @@ component App() {
|
|
|
445
469
|
it('prints longhand tracked property values in to_ts output while preserving [\'#v\']', () => {
|
|
446
470
|
const source = `
|
|
447
471
|
import { RippleArray, RippleMap, RippleObject, RippleSet, createRefKey, effect, track, untrack } from 'ripple';
|
|
448
|
-
|
|
472
|
+
function App() { return <>
|
|
449
473
|
let value = track('test');
|
|
450
474
|
function inputRef(node) {}
|
|
451
475
|
|
|
@@ -454,7 +478,7 @@ component App() {
|
|
|
454
478
|
value: value.value,
|
|
455
479
|
[createRefKey()]: inputRef,
|
|
456
480
|
};
|
|
457
|
-
}
|
|
481
|
+
</>; }
|
|
458
482
|
`;
|
|
459
483
|
|
|
460
484
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
@@ -465,10 +489,10 @@ component App() {
|
|
|
465
489
|
it('keeps lazy destructuring as plain destructuring in to_ts output', () => {
|
|
466
490
|
const track_source = `
|
|
467
491
|
import { track } from 'ripple';
|
|
468
|
-
|
|
492
|
+
function App() { return <>
|
|
469
493
|
let &[value, ...rest] = track(0);
|
|
470
494
|
const x = value;
|
|
471
|
-
}
|
|
495
|
+
</>; }
|
|
472
496
|
`;
|
|
473
497
|
const track_result = compile_to_volar_mappings(track_source, 'test.tsrx').code;
|
|
474
498
|
expect(track_result).toContain('let [value, ...rest] = track(0);');
|
|
@@ -479,49 +503,39 @@ component App() {
|
|
|
479
503
|
expect(track_result).not.toContain('lazy0');
|
|
480
504
|
});
|
|
481
505
|
|
|
482
|
-
it('lowers
|
|
506
|
+
it('lowers native expression values in to_ts output', () => {
|
|
483
507
|
const source = `
|
|
484
|
-
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
{'inside nested tsx'}
|
|
490
|
-
</span>
|
|
491
|
-
</tsx>;
|
|
492
|
-
<div class="native">{nested}</div>
|
|
493
|
-
</tsrx>}
|
|
508
|
+
function App() { return <>
|
|
509
|
+
const nested = <tsx>
|
|
510
|
+
<span class="nested-tsx">
|
|
511
|
+
{'inside nested tsx'}
|
|
512
|
+
</span>
|
|
494
513
|
</tsx>;
|
|
514
|
+
const content = <div class="native">{nested}</div>;
|
|
495
515
|
|
|
496
516
|
{content}
|
|
497
|
-
}
|
|
517
|
+
</>; }
|
|
498
518
|
`;
|
|
499
519
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
500
520
|
|
|
501
|
-
expect(result).not.toContain('<tsrx>');
|
|
502
|
-
expect(result).not.toContain('</tsrx>');
|
|
503
521
|
expect(result).not.toContain('<tsx>');
|
|
504
522
|
expect(result).not.toContain('</tsx>');
|
|
505
523
|
expect(result).toContain('const nested = <>');
|
|
506
|
-
expect(result).toContain('
|
|
524
|
+
expect(result).toContain('const content = <div class="native">');
|
|
507
525
|
});
|
|
508
526
|
|
|
509
|
-
it('maps identifiers from
|
|
527
|
+
it('maps identifiers from native expression values in to_ts output', () => {
|
|
510
528
|
const source = `
|
|
511
|
-
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
{'inside nested tsx'}
|
|
517
|
-
</span>
|
|
518
|
-
</tsx>;
|
|
519
|
-
<div class="native">{nested}</div>
|
|
520
|
-
</tsrx>}
|
|
529
|
+
function App() { return <>
|
|
530
|
+
const nested = <tsx>
|
|
531
|
+
<span class="nested-tsx">
|
|
532
|
+
{'inside nested tsx'}
|
|
533
|
+
</span>
|
|
521
534
|
</tsx>;
|
|
535
|
+
const content = <div class="native">{nested}</div>;
|
|
522
536
|
|
|
523
537
|
{content}
|
|
524
|
-
}
|
|
538
|
+
</>; }
|
|
525
539
|
`;
|
|
526
540
|
const result = compile_to_volar_mappings(source, 'test.tsrx', { loose: true });
|
|
527
541
|
const source_declaration = source.indexOf('nested =');
|
|
@@ -568,33 +582,33 @@ type Props<Item> = {
|
|
|
568
582
|
items: readonly Item[];
|
|
569
583
|
}
|
|
570
584
|
|
|
571
|
-
export
|
|
585
|
+
export function MyComponent<Item>(props: Props<Item>) { return <>
|
|
572
586
|
<div />
|
|
573
|
-
}
|
|
587
|
+
</>; }
|
|
574
588
|
`;
|
|
575
589
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
576
590
|
|
|
577
591
|
expect(result).toContain('export function MyComponent<Item>(props: Props<Item>)');
|
|
578
592
|
});
|
|
579
593
|
|
|
580
|
-
it('
|
|
594
|
+
it('preserves arrow functions that return TSRX in to_ts output', () => {
|
|
581
595
|
const source = `
|
|
582
|
-
const Inline =
|
|
596
|
+
const Inline = (props: { x: string }) => <>
|
|
583
597
|
<div>{props.x}</div>
|
|
584
|
-
|
|
598
|
+
</>
|
|
585
599
|
`;
|
|
586
600
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
587
601
|
|
|
588
|
-
expect(result).toContain('const Inline = (props: { x: string }) =>
|
|
602
|
+
expect(result).toContain('const Inline = (props: { x: string }) => <div>');
|
|
589
603
|
expect(result).not.toContain('function Inline');
|
|
590
604
|
expect(result).not.toContain('function (props');
|
|
591
605
|
});
|
|
592
606
|
|
|
593
|
-
it('
|
|
607
|
+
it('preserves function expressions that return TSRX in to_ts output', () => {
|
|
594
608
|
const source = `
|
|
595
|
-
const Inline =
|
|
609
|
+
const Inline = function(props: { x: string }) { return <>
|
|
596
610
|
<div>{props.x}</div>
|
|
597
|
-
}
|
|
611
|
+
</>; }
|
|
598
612
|
`;
|
|
599
613
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
600
614
|
|
|
@@ -607,13 +621,13 @@ const Inline = component(props: { x: string }) {
|
|
|
607
621
|
const source = `
|
|
608
622
|
type User = { name: string };
|
|
609
623
|
|
|
610
|
-
|
|
624
|
+
function RenderProp<Item>(props: { children: (item: Item) => any }) { return <></>; }
|
|
611
625
|
|
|
612
|
-
export
|
|
626
|
+
export function App() { return <>
|
|
613
627
|
<RenderProp<User>>
|
|
614
628
|
{(item) => item.name}
|
|
615
629
|
</RenderProp>
|
|
616
|
-
}
|
|
630
|
+
</>; }
|
|
617
631
|
`;
|
|
618
632
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
619
633
|
|
|
@@ -622,13 +636,13 @@ export component App() {
|
|
|
622
636
|
|
|
623
637
|
it('preserves generic type arguments on self-closing JSX component tags in to_ts output', () => {
|
|
624
638
|
const source = `
|
|
625
|
-
|
|
639
|
+
function Box<T>({ value }: { value: T }) { return <>
|
|
626
640
|
<div>{String(value)}</div>
|
|
627
|
-
}
|
|
641
|
+
</>; }
|
|
628
642
|
|
|
629
|
-
export
|
|
643
|
+
export function App() { return <>
|
|
630
644
|
<Box<string> value="hi" />
|
|
631
|
-
}
|
|
645
|
+
</>; }
|
|
632
646
|
`;
|
|
633
647
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
634
648
|
|
|
@@ -728,9 +742,9 @@ interface Props extends PolymorphicProps<'div'> {
|
|
|
728
742
|
id: string;
|
|
729
743
|
}
|
|
730
744
|
|
|
731
|
-
export
|
|
745
|
+
export function App(props: Props) { return <>
|
|
732
746
|
<div id={props.id} />
|
|
733
|
-
}
|
|
747
|
+
</>; }
|
|
734
748
|
`;
|
|
735
749
|
|
|
736
750
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
@@ -741,7 +755,7 @@ export component App(props: Props) {
|
|
|
741
755
|
it('handles if-else expression statements in Volar mappings', () => {
|
|
742
756
|
const source = `
|
|
743
757
|
import { track } from 'ripple';
|
|
744
|
-
export
|
|
758
|
+
export function App() { return <>
|
|
745
759
|
let &[level] = track(1);
|
|
746
760
|
|
|
747
761
|
<button
|
|
@@ -753,7 +767,7 @@ export component App() {
|
|
|
753
767
|
>
|
|
754
768
|
{'Toggle'}
|
|
755
769
|
</button>
|
|
756
|
-
}
|
|
770
|
+
</>; }
|
|
757
771
|
`;
|
|
758
772
|
|
|
759
773
|
expect(() => compile_to_volar_mappings(source, 'test.tsrx')).not.toThrow();
|
|
@@ -761,66 +775,64 @@ export component App() {
|
|
|
761
775
|
|
|
762
776
|
it('should not error on having js below markup in the same scope', () => {
|
|
763
777
|
const code = `
|
|
764
|
-
|
|
778
|
+
function Card(props) { return <>
|
|
765
779
|
<div class="card">
|
|
766
780
|
{props.children}
|
|
767
781
|
</div>
|
|
768
|
-
}
|
|
782
|
+
</>; }
|
|
769
783
|
|
|
770
|
-
export
|
|
771
|
-
|
|
784
|
+
export function App() { return <>
|
|
785
|
+
function children() { return <>
|
|
772
786
|
<p>{'Card content here'}</p>
|
|
773
|
-
}
|
|
787
|
+
</>; }
|
|
774
788
|
|
|
775
789
|
<Card {children} />
|
|
776
790
|
|
|
777
791
|
const test = 5;
|
|
778
792
|
|
|
779
793
|
<div>{test}</div>
|
|
780
|
-
}
|
|
794
|
+
</>; }
|
|
781
795
|
`;
|
|
782
796
|
expect(() => compile(code, 'test.tsrx')).not.toThrow();
|
|
783
797
|
});
|
|
784
798
|
|
|
785
|
-
it('allows component
|
|
799
|
+
it('allows component functions inside composite children', () => {
|
|
786
800
|
const source = `
|
|
787
|
-
export
|
|
801
|
+
export function App() { return <>
|
|
788
802
|
<ark.div class="host-class" data-value="42">
|
|
789
|
-
|
|
803
|
+
function asChild({ children, href, ...rest }: { href: string; [key: string]: any }) { return <>
|
|
790
804
|
<a id="aschild-anchor" {href} {...rest} data-extra="yes">{'Link'}</a>
|
|
791
|
-
}
|
|
805
|
+
</>; }
|
|
792
806
|
</ark.div>
|
|
793
|
-
}
|
|
807
|
+
</>; }
|
|
794
808
|
`;
|
|
795
809
|
|
|
796
810
|
expect(() => compile_to_volar_mappings(source, 'test.tsrx')).not.toThrow();
|
|
797
811
|
});
|
|
798
812
|
|
|
799
|
-
it('
|
|
813
|
+
it('allows parent element attributes referencing child-declared functions', () => {
|
|
800
814
|
const source = `
|
|
801
|
-
export
|
|
815
|
+
export function App() { return <>
|
|
802
816
|
<Test {Z}>
|
|
803
|
-
|
|
817
|
+
function Z() { return <>
|
|
804
818
|
<div>{'hello'}</div>
|
|
805
|
-
}
|
|
819
|
+
</>; }
|
|
806
820
|
</Test>
|
|
807
|
-
}
|
|
821
|
+
</>; }
|
|
808
822
|
`;
|
|
809
823
|
|
|
810
|
-
expect(() => compile(source, 'test.tsrx')).toThrow(
|
|
811
|
-
/Cannot use component 'Z' as a prop on its parent element/,
|
|
812
|
-
);
|
|
824
|
+
expect(() => compile(source, 'test.tsrx')).not.toThrow();
|
|
813
825
|
});
|
|
814
826
|
|
|
815
827
|
it('preserves explicit component props in Volar mappings', () => {
|
|
816
828
|
const source = `
|
|
817
|
-
export
|
|
818
|
-
|
|
829
|
+
export function App() { return <>
|
|
830
|
+
function asChild({ children, href, ...rest }: { href: string; [key: string]: any }) { return <>
|
|
819
831
|
<a id="aschild-anchor" {href} {...rest} data-extra="yes">{'Link'}</a>
|
|
820
|
-
}
|
|
832
|
+
</>; }
|
|
821
833
|
|
|
822
834
|
<ark.div class="host-class" data-value="42" {asChild} />
|
|
823
|
-
}
|
|
835
|
+
</>; }
|
|
824
836
|
`;
|
|
825
837
|
const result = compile_to_volar_mappings(source, 'test.tsrx').code;
|
|
826
838
|
|
|
@@ -830,17 +842,17 @@ export component App() {
|
|
|
830
842
|
|
|
831
843
|
it('merges explicit children prop with implicit children in client output', () => {
|
|
832
844
|
const source = `
|
|
833
|
-
|
|
845
|
+
function Card(props) { return <>
|
|
834
846
|
<div>{props.children}</div>
|
|
835
|
-
}
|
|
847
|
+
</>; }
|
|
836
848
|
|
|
837
|
-
export
|
|
849
|
+
export function App() { return <>
|
|
838
850
|
const fallback = 'fallback';
|
|
839
851
|
|
|
840
852
|
<Card children={fallback}>
|
|
841
853
|
<span>{'content'}</span>
|
|
842
854
|
</Card>
|
|
843
|
-
}
|
|
855
|
+
</>; }
|
|
844
856
|
`;
|
|
845
857
|
|
|
846
858
|
const result = compile(source, 'test.tsrx', { mode: 'client' }).code;
|
|
@@ -859,10 +871,10 @@ class Test {
|
|
|
859
871
|
}
|
|
860
872
|
}
|
|
861
873
|
|
|
862
|
-
export
|
|
874
|
+
export function App() { return <>
|
|
863
875
|
const test = new Test();
|
|
864
876
|
<div>{test.count}</div>
|
|
865
|
-
}
|
|
877
|
+
</>; }
|
|
866
878
|
`;
|
|
867
879
|
expect(() => compile(code, 'test.tsrx')).not.toThrow();
|
|
868
880
|
});
|
|
@@ -878,10 +890,10 @@ class Store {
|
|
|
878
890
|
}
|
|
879
891
|
}
|
|
880
892
|
|
|
881
|
-
export
|
|
893
|
+
export function App() { return <>
|
|
882
894
|
const store = new Store();
|
|
883
895
|
<div>{store.count}</div>
|
|
884
|
-
}
|
|
896
|
+
</>; }
|
|
885
897
|
`;
|
|
886
898
|
const result = compile(source, 'test.tsrx', { mode: 'client' });
|
|
887
899
|
const code = result.code;
|
|
@@ -895,14 +907,14 @@ export component App() {
|
|
|
895
907
|
const source = `
|
|
896
908
|
import { track, effect, untrack } from 'ripple';
|
|
897
909
|
|
|
898
|
-
|
|
910
|
+
function App() { return <>
|
|
899
911
|
let &[count] = track(0);
|
|
900
912
|
|
|
901
913
|
effect(() => {
|
|
902
914
|
const snapshot = untrack(() => count);
|
|
903
915
|
console.log(snapshot);
|
|
904
916
|
});
|
|
905
|
-
}
|
|
917
|
+
</>; }
|
|
906
918
|
`;
|
|
907
919
|
|
|
908
920
|
const ast = parse(source);
|
|
@@ -915,10 +927,10 @@ component App() {
|
|
|
915
927
|
it('collects duplicate declaration parser errors in loose mode', () => {
|
|
916
928
|
const source = `
|
|
917
929
|
import { track } from 'ripple';
|
|
918
|
-
export
|
|
930
|
+
export function App() { return <>
|
|
919
931
|
let test = track(false);
|
|
920
932
|
let test = 'hey';
|
|
921
|
-
}
|
|
933
|
+
</>; }
|
|
922
934
|
`;
|
|
923
935
|
|
|
924
936
|
expect(() => compile(source, 'test.tsrx')).toThrow(
|
|
@@ -944,11 +956,11 @@ export component App() {
|
|
|
944
956
|
import { loadUser as getUser } from server;
|
|
945
957
|
import { loadUser } from server;
|
|
946
958
|
|
|
947
|
-
|
|
959
|
+
function App() { return <>
|
|
948
960
|
const user = getUser();
|
|
949
961
|
const user2 = loadUser();
|
|
950
962
|
<div>{user.id}{user2.id}</div>
|
|
951
|
-
}`;
|
|
963
|
+
</>; }`;
|
|
952
964
|
const result = compile_to_volar_mappings(source, 'test.tsrx', { loose: true });
|
|
953
965
|
const generated_member = '_$_server_$_.loadUser';
|
|
954
966
|
const generated_member_offset = result.code.indexOf(generated_member);
|
|
@@ -1004,30 +1016,27 @@ component App() {
|
|
|
1004
1016
|
).toBeDefined();
|
|
1005
1017
|
});
|
|
1006
1018
|
|
|
1007
|
-
it('
|
|
1008
|
-
const result = compile_to_volar_mappings(
|
|
1019
|
+
it('reports return statements inside returned TSRX fragments in Volar mappings', () => {
|
|
1020
|
+
const result = compile_to_volar_mappings(
|
|
1021
|
+
`function App() { return <>
|
|
1009
1022
|
return <div />;
|
|
1010
|
-
}`,
|
|
1011
|
-
|
|
1012
|
-
expect(result.errors.map((error) => error.code)).toContain(
|
|
1013
|
-
DIAGNOSTIC_CODES.JSX_RETURN_IN_COMPONENT,
|
|
1023
|
+
</>; }`,
|
|
1024
|
+
'test.tsrx',
|
|
1014
1025
|
);
|
|
1026
|
+
|
|
1027
|
+
expect(result.errors.map((error) => error.code)).toEqual(['tsrx-template-return-statement']);
|
|
1015
1028
|
});
|
|
1016
1029
|
|
|
1017
1030
|
it('throws for unclosed tsx compat tags instead of hanging', () => {
|
|
1018
|
-
const source = `export
|
|
1019
|
-
<tsx:react>1
|
|
1020
|
-
}`;
|
|
1031
|
+
const source = `export function App() { return <tsx:react>1; }`;
|
|
1021
1032
|
|
|
1022
1033
|
expect(() => compile(source, 'test.tsrx')).toThrow(
|
|
1023
|
-
'Unclosed tag \'<tsx:react>\'. Expected \'</tsx:react>\' before end of
|
|
1034
|
+
'Unclosed tag \'<tsx:react>\'. Expected \'</tsx:react>\' before end of template.',
|
|
1024
1035
|
);
|
|
1025
1036
|
});
|
|
1026
1037
|
|
|
1027
1038
|
it('recovers unclosed tsx compat tags in loose mode', () => {
|
|
1028
|
-
const source = `export
|
|
1029
|
-
<tsx:react>1
|
|
1030
|
-
}`;
|
|
1039
|
+
const source = `export function App() { return <tsx:react>1; }`;
|
|
1031
1040
|
|
|
1032
1041
|
expect(() => compile_to_volar_mappings(source, 'test.tsrx', { loose: true })).not.toThrow();
|
|
1033
1042
|
|
|
@@ -1035,14 +1044,11 @@ component App() {
|
|
|
1035
1044
|
expect(result.errors).toEqual([]);
|
|
1036
1045
|
});
|
|
1037
1046
|
|
|
1038
|
-
it('
|
|
1039
|
-
const source = `export
|
|
1040
|
-
<tsx:react>1
|
|
1041
|
-
}`;
|
|
1047
|
+
it('throws for unclosed tsx compat tags outside loose mode', () => {
|
|
1048
|
+
const source = `export function App() { return <tsx:react>1; }`;
|
|
1042
1049
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
'Unclosed tag \'<tsx:react>\'. Expected \'</tsx:react>\' before end of component.',
|
|
1050
|
+
expect(() => compile(source, 'test.tsrx', { collect: true })).toThrow(
|
|
1051
|
+
'Not implemented: TsxCompat',
|
|
1046
1052
|
);
|
|
1047
1053
|
});
|
|
1048
1054
|
|
|
@@ -1052,7 +1058,7 @@ import { track } from 'ripple';
|
|
|
1052
1058
|
|
|
1053
1059
|
const outside = track(0);
|
|
1054
1060
|
|
|
1055
|
-
export
|
|
1061
|
+
export function App() { return <></>; }
|
|
1056
1062
|
`;
|
|
1057
1063
|
|
|
1058
1064
|
const result = compile(source, 'test.tsrx', { collect: true });
|
|
@@ -1063,13 +1069,13 @@ export component App() {}
|
|
|
1063
1069
|
|
|
1064
1070
|
it('does not let nested for...of continues satisfy an outer if body', () => {
|
|
1065
1071
|
const source = `
|
|
1066
|
-
export
|
|
1072
|
+
export function App({ items }: { items: string[] }) { return <>
|
|
1067
1073
|
if (items.length) {
|
|
1068
1074
|
for (const item of items) {
|
|
1069
1075
|
if (!item) continue
|
|
1070
1076
|
}
|
|
1071
1077
|
}
|
|
1072
|
-
}`;
|
|
1078
|
+
</>; }`;
|
|
1073
1079
|
|
|
1074
1080
|
const result = compile(source, 'test.tsrx', { collect: true });
|
|
1075
1081
|
expect(result.errors.map((error) => error.message)).toContain(
|
|
@@ -1079,7 +1085,7 @@ export component App({ items }: { items: string[] }) {
|
|
|
1079
1085
|
|
|
1080
1086
|
it('preserves class extends generic type arguments in volar output', () => {
|
|
1081
1087
|
const source = `class StringMap extends Map<string, string> {}
|
|
1082
|
-
export
|
|
1088
|
+
export function App() { return <></>; }`;
|
|
1083
1089
|
|
|
1084
1090
|
expect(() => compile_to_volar_mappings(source, 'test.tsrx', { loose: true })).not.toThrow();
|
|
1085
1091
|
|
|
@@ -1090,15 +1096,15 @@ export component App() {}`;
|
|
|
1090
1096
|
|
|
1091
1097
|
it('wraps children in normalize_children for explicit children prop passed to component', () => {
|
|
1092
1098
|
const source = `
|
|
1093
|
-
|
|
1099
|
+
function Card(props) { return <>
|
|
1094
1100
|
<div>{props.children}</div>
|
|
1095
|
-
}
|
|
1101
|
+
</>; }
|
|
1096
1102
|
|
|
1097
|
-
export
|
|
1103
|
+
export function App() { return <>
|
|
1098
1104
|
const content = 'hello';
|
|
1099
1105
|
|
|
1100
1106
|
<Card children={content} />
|
|
1101
|
-
}
|
|
1107
|
+
</>; }
|
|
1102
1108
|
`;
|
|
1103
1109
|
|
|
1104
1110
|
const result = compile(source, 'test.tsrx', { mode: 'client' }).code;
|
|
@@ -1109,12 +1115,13 @@ export component App() {
|
|
|
1109
1115
|
it(
|
|
1110
1116
|
'parses a JS statement inside an element with no trailing whitespace before the closing tag',
|
|
1111
1117
|
() => {
|
|
1112
|
-
const source = `
|
|
1118
|
+
const source = `function TodoList({ items }: { items: { text: string }[] }) { return <>
|
|
1113
1119
|
<ul>var a = "123"</ul>
|
|
1114
|
-
}`;
|
|
1120
|
+
</>; }`;
|
|
1115
1121
|
const ast = parse(source);
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1122
|
+
const ul = get_returned_tsrx(ast.body[0]).children.find(
|
|
1123
|
+
(n: AST.Node) => n.type === 'Element',
|
|
1124
|
+
) as AST.Element;
|
|
1118
1125
|
expect((ul.id as AST.Identifier).name).toBe('ul');
|
|
1119
1126
|
expect(ul.children).toHaveLength(1);
|
|
1120
1127
|
const decl = ul.children[0] as unknown as AST.VariableDeclaration;
|
|
@@ -1128,15 +1135,15 @@ export component App() {
|
|
|
1128
1135
|
|
|
1129
1136
|
it('uses spread_props for spreads that may contain children', () => {
|
|
1130
1137
|
const source = `
|
|
1131
|
-
|
|
1138
|
+
function Card(props) { return <>
|
|
1132
1139
|
<div>{props.children}</div>
|
|
1133
|
-
}
|
|
1140
|
+
</>; }
|
|
1134
1141
|
|
|
1135
|
-
export
|
|
1142
|
+
export function App() { return <>
|
|
1136
1143
|
const props = { children: 'hello' };
|
|
1137
1144
|
|
|
1138
1145
|
<Card {...props} />
|
|
1139
|
-
}
|
|
1146
|
+
</>; }
|
|
1140
1147
|
`;
|
|
1141
1148
|
|
|
1142
1149
|
const result = compile(source, 'test.tsrx', { mode: 'client' }).code;
|
|
@@ -1145,14 +1152,15 @@ export component App() {
|
|
|
1145
1152
|
});
|
|
1146
1153
|
|
|
1147
1154
|
it('parses less-than comparisons at line start in element children without whitespace', () => {
|
|
1148
|
-
const source = `
|
|
1155
|
+
const source = `function TodoList({ items }: { items: { text: string }[] }) { return <>
|
|
1149
1156
|
<ul>var a = 3
|
|
1150
1157
|
<4;</ul>
|
|
1151
|
-
}`;
|
|
1158
|
+
</>; }`;
|
|
1152
1159
|
|
|
1153
1160
|
const ast = parse(source);
|
|
1154
|
-
const
|
|
1155
|
-
|
|
1161
|
+
const ul = get_returned_tsrx(ast.body[0]).children.find(
|
|
1162
|
+
(n: AST.Node) => n.type === 'Element',
|
|
1163
|
+
) as AST.Element;
|
|
1156
1164
|
expect((ul.id as AST.Identifier).name).toBe('ul');
|
|
1157
1165
|
expect(ul.children.length).toBeGreaterThanOrEqual(1);
|
|
1158
1166
|
const decl = ul.children[0] as unknown as AST.VariableDeclaration;
|