ripple 0.2.152 → 0.2.154
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/README.md +3 -3
- package/package.json +5 -5
- package/src/compiler/phases/1-parse/index.js +1 -1
- package/src/compiler/phases/3-transform/client/index.js +37 -16
- package/src/compiler/phases/3-transform/server/index.js +43 -25
- package/src/runtime/internal/client/events.js +5 -1
- package/src/runtime/internal/client/index.js +2 -1
- package/src/runtime/internal/client/render.js +18 -15
- package/src/runtime/internal/client/runtime.js +75 -10
- package/src/runtime/internal/server/index.js +51 -11
- package/src/server/index.js +1 -1
- package/tests/client/array/array.derived.test.ripple +61 -33
- package/tests/client/array/array.iteration.test.ripple +3 -1
- package/tests/client/array/array.mutations.test.ripple +19 -15
- package/tests/client/array/array.static.test.ripple +115 -104
- package/tests/client/array/array.to-methods.test.ripple +3 -3
- package/tests/client/basic/basic.attributes.test.ripple +110 -57
- package/tests/client/basic/basic.collections.test.ripple +41 -22
- package/tests/client/basic/basic.errors.test.ripple +12 -6
- package/tests/client/basic/basic.events.test.ripple +51 -33
- package/tests/client/basic/basic.reactivity.test.ripple +120 -56
- package/tests/client/basic/basic.rendering.test.ripple +49 -19
- package/tests/client/basic/basic.styling.test.ripple +2 -2
- package/tests/client/basic/basic.utilities.test.ripple +1 -1
- package/tests/client/boundaries.test.ripple +70 -58
- package/tests/client/compiler/compiler.assignments.test.ripple +32 -4
- package/tests/client/compiler/compiler.attributes.test.ripple +46 -46
- package/tests/client/compiler/compiler.basic.test.ripple +18 -15
- package/tests/client/compiler/compiler.tracked-access.test.ripple +53 -42
- package/tests/client/compiler/compiler.typescript.test.ripple +1 -2
- package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
- package/tests/client/composite/composite.generics.test.ripple +39 -36
- package/tests/client/composite/composite.props.test.ripple +4 -3
- package/tests/client/composite/composite.reactivity.test.ripple +112 -27
- package/tests/client/composite/composite.render.test.ripple +9 -8
- package/tests/client/computed-properties.test.ripple +24 -24
- package/tests/client/context.test.ripple +11 -9
- package/tests/client/date.test.ripple +3 -1
- package/tests/client/dynamic-elements.test.ripple +103 -78
- package/tests/client/for.test.ripple +27 -17
- package/tests/client/head.test.ripple +42 -6
- package/tests/client/html.test.ripple +42 -32
- package/tests/client/input-value.test.ripple +4 -4
- package/tests/client/map.test.ripple +140 -141
- package/tests/client/media-query.test.ripple +31 -31
- package/tests/client/object.test.ripple +148 -112
- package/tests/client/portal.test.ripple +29 -15
- package/tests/client/ref.test.ripple +9 -3
- package/tests/client/set.test.ripple +111 -111
- package/tests/client/tracked-expression.test.ripple +16 -17
- package/tests/client/url/url.derived.test.ripple +19 -9
- package/tests/client/url/url.parsing.test.ripple +24 -8
- package/tests/client/url/url.partial-removal.test.ripple +12 -4
- package/tests/client/url/url.reactivity.test.ripple +63 -25
- package/tests/client/url/url.serialization.test.ripple +18 -6
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +10 -6
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +3 -1
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +26 -14
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -1
- package/tests/server/await.test.ripple +23 -22
- package/tests/server/basic.test.ripple +1 -1
- package/tests/server/compiler.test.ripple +3 -7
- package/tests/server/composite.test.ripple +38 -36
- package/tests/server/for.test.ripple +9 -5
- package/tests/server/if.test.ripple +1 -1
- package/tests/server/streaming-ssr.test.ripple +67 -0
- package/types/server.d.ts +5 -4
|
@@ -70,10 +70,38 @@ describe('compiler > assignments', () => {
|
|
|
70
70
|
render(App);
|
|
71
71
|
|
|
72
72
|
expect(logs).toEqual([
|
|
73
|
-
undefined,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
undefined,
|
|
74
|
+
undefined,
|
|
75
|
+
undefined,
|
|
76
|
+
undefined,
|
|
77
|
+
undefined,
|
|
78
|
+
undefined,
|
|
79
|
+
undefined,
|
|
80
|
+
undefined,
|
|
81
|
+
123,
|
|
82
|
+
123,
|
|
83
|
+
123,
|
|
84
|
+
123,
|
|
85
|
+
123,
|
|
86
|
+
123,
|
|
87
|
+
123,
|
|
88
|
+
123,
|
|
89
|
+
125,
|
|
90
|
+
125,
|
|
91
|
+
125,
|
|
92
|
+
125,
|
|
93
|
+
125,
|
|
94
|
+
125,
|
|
95
|
+
125,
|
|
96
|
+
125,
|
|
97
|
+
124,
|
|
98
|
+
123,
|
|
99
|
+
124,
|
|
100
|
+
123,
|
|
101
|
+
124,
|
|
102
|
+
123,
|
|
103
|
+
124,
|
|
104
|
+
123,
|
|
77
105
|
]);
|
|
78
106
|
});
|
|
79
107
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { parse, compile } from 'ripple/compiler';
|
|
2
2
|
|
|
3
3
|
describe('compiler > attributes', () => {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
it('generates valid JavaScript for component props with hyphenated attributes', () => {
|
|
5
|
+
const source = `
|
|
6
6
|
component Child(props) {
|
|
7
7
|
<div />
|
|
8
8
|
}
|
|
@@ -11,34 +11,34 @@ describe('compiler > attributes', () => {
|
|
|
11
11
|
<Child data-scope="test" aria-label="accessible" class="valid" />
|
|
12
12
|
}`;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const result = compile(source, 'test.ripple', { mode: 'client' });
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
// Should contain properly quoted hyphenated properties and unquoted valid identifiers
|
|
17
|
+
expect(result.js.code).toMatch(/'data-scope': "test"/);
|
|
18
|
+
expect(result.js.code).toMatch(/'aria-label': "accessible"/);
|
|
19
|
+
expect(result.js.code).toMatch(/class: "valid"/);
|
|
20
|
+
});
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
it('generates valid JavaScript for all types of hyphenated attributes', () => {
|
|
23
|
+
const testCases = [
|
|
24
|
+
{ attr: 'data-testid="value"', expected: /'data-testid': "value"/ },
|
|
25
|
+
{ attr: 'aria-label="label"', expected: /'aria-label': "label"/ },
|
|
26
|
+
{ attr: 'data-custom-attr="custom"', expected: /'data-custom-attr': "custom"/ },
|
|
27
|
+
{ attr: 'ng-if="condition"', expected: /'ng-if': "condition"/ },
|
|
28
|
+
];
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
testCases.forEach(({ attr, expected }) => {
|
|
31
|
+
const source = `
|
|
32
32
|
component Child(props) { <div /> }
|
|
33
33
|
export default component App() { <Child ${attr} /> }`;
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
35
|
+
const result = compile(source, 'test.ripple', { mode: 'client' });
|
|
36
|
+
expect(result.js.code).toMatch(expected);
|
|
38
37
|
});
|
|
38
|
+
});
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
it('handles mixed valid and invalid attribute identifiers correctly', () => {
|
|
41
|
+
const source = `
|
|
42
42
|
component Child(props) {
|
|
43
43
|
<div />
|
|
44
44
|
}
|
|
@@ -54,21 +54,21 @@ describe('compiler > attributes', () => {
|
|
|
54
54
|
/>
|
|
55
55
|
}`;
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
const result = compile(source, 'test.ripple', { mode: 'client' });
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
// Valid identifiers should not be quoted
|
|
60
|
+
expect(result.js.code).toMatch(/validProp: "valid"/);
|
|
61
|
+
expect(result.js.code).toMatch(/class: "valid"/);
|
|
62
|
+
expect(result.js.code).toMatch(/id: "valid"/);
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
// Invalid identifiers (with hyphens) should be quoted
|
|
65
|
+
expect(result.js.code).toMatch(/'data-invalid': "invalid"/);
|
|
66
|
+
expect(result.js.code).toMatch(/'aria-invalid': "invalid"/);
|
|
67
|
+
expect(result.js.code).toMatch(/'custom-prop': "invalid"/);
|
|
68
|
+
});
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
it('ensures generated code is syntactically valid JavaScript', () => {
|
|
71
|
+
const source = `
|
|
72
72
|
component Child(props) {
|
|
73
73
|
<div />
|
|
74
74
|
}
|
|
@@ -77,19 +77,19 @@ describe('compiler > attributes', () => {
|
|
|
77
77
|
<Child data-scope="test" />
|
|
78
78
|
}`;
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
const result = compile(source, 'test.ripple', { mode: 'client' });
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
// Extract the props object from the generated code and test it's valid JavaScript
|
|
83
|
+
const match = result.js.code.match(/Child\([^,]+,\s*(\{[^}]+\})/);
|
|
84
|
+
expect(match).toBeTruthy();
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
const propsObject = match?.[1];
|
|
87
|
+
expect(() => {
|
|
88
|
+
// Test that the object literal is syntactically valid
|
|
89
|
+
new Function(`return ${propsObject}`);
|
|
90
|
+
}).not.toThrow();
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
// Also verify it contains the expected quoted property
|
|
93
|
+
expect(propsObject).toMatch(/'data-scope': "test"/);
|
|
94
|
+
});
|
|
95
95
|
});
|
|
@@ -247,8 +247,10 @@ component App() {
|
|
|
247
247
|
expect(count_occurrences(result, 'createRefKey')).toBe(2);
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
-
it(
|
|
251
|
-
|
|
250
|
+
it(
|
|
251
|
+
'doesn\'t add duplicate imports for renamed imports when encountering shorthand syntax',
|
|
252
|
+
() => {
|
|
253
|
+
const source = `
|
|
252
254
|
import {
|
|
253
255
|
TrackedArray as TA,
|
|
254
256
|
TrackedObject as TO,
|
|
@@ -266,19 +268,20 @@ component App() {
|
|
|
266
268
|
<div {ref () => {}} />
|
|
267
269
|
}
|
|
268
270
|
`;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
271
|
+
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
272
|
+
|
|
273
|
+
expect(count_occurrences(result, 'TrackedArray')).toBe(1);
|
|
274
|
+
expect(count_occurrences(result, 'TA')).toBe(2);
|
|
275
|
+
expect(count_occurrences(result, 'TrackedObject')).toBe(1);
|
|
276
|
+
expect(count_occurrences(result, 'TO')).toBe(2);
|
|
277
|
+
expect(count_occurrences(result, 'TrackedSet')).toBe(1);
|
|
278
|
+
expect(count_occurrences(result, 'TS')).toBe(2);
|
|
279
|
+
expect(count_occurrences(result, 'TrackedMap')).toBe(1);
|
|
280
|
+
expect(count_occurrences(result, 'TM')).toBe(2);
|
|
281
|
+
expect(count_occurrences(result, 'createRefKey')).toBe(1);
|
|
282
|
+
expect(count_occurrences(result, 'crk')).toBe(2);
|
|
283
|
+
},
|
|
284
|
+
);
|
|
282
285
|
|
|
283
286
|
it('adds missing imports for shorthand syntax', () => {
|
|
284
287
|
const source = `
|
|
@@ -2,107 +2,118 @@ import { compile } from 'ripple/compiler';
|
|
|
2
2
|
import { track } from 'ripple';
|
|
3
3
|
|
|
4
4
|
describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const code = `
|
|
5
|
+
it('should error on direct access to __v of a tracked object', () => {
|
|
6
|
+
const code = `
|
|
8
7
|
export default component App() {
|
|
9
8
|
let count = track(0);
|
|
10
9
|
console.log(count.__v);
|
|
11
10
|
}
|
|
12
11
|
`;
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
expect(() => compile(code, 'test.ripple')).toThrow(
|
|
13
|
+
/Directly accessing internal property "__v" of a tracked object is not allowed/,
|
|
14
|
+
);
|
|
15
|
+
});
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
it('should error on direct access to "a" (get/set config) of a tracked object', () => {
|
|
18
|
+
const code = `
|
|
18
19
|
export default component App() {
|
|
19
20
|
let myTracked = track(0);
|
|
20
21
|
console.log(myTracked.a);
|
|
21
22
|
}
|
|
22
23
|
`;
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
expect(() => compile(code, 'test.ripple')).toThrow(
|
|
25
|
+
/Directly accessing internal property "a" of a tracked object is not allowed/,
|
|
26
|
+
);
|
|
27
|
+
});
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
it('should error on direct access to "b" (block) of a tracked object', () => {
|
|
30
|
+
const code = `
|
|
28
31
|
export default component App() {
|
|
29
32
|
let myTracked = track(0);
|
|
30
33
|
console.log(myTracked.b);
|
|
31
34
|
}
|
|
32
35
|
`;
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
expect(() => compile(code, 'test.ripple')).toThrow(
|
|
37
|
+
/Directly accessing internal property "b" of a tracked object is not allowed/,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
it('should error on direct access to "c" (clock) of a tracked object', () => {
|
|
42
|
+
const code = `
|
|
38
43
|
export default component App() {
|
|
39
44
|
let myTracked = track(0);
|
|
40
45
|
console.log(myTracked.c);
|
|
41
46
|
}
|
|
42
47
|
`;
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
expect(() => compile(code, 'test.ripple')).toThrow(
|
|
49
|
+
/Directly accessing internal property "c" of a tracked object is not allowed/,
|
|
50
|
+
);
|
|
51
|
+
});
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
it('should error on direct access to "f" (flags) of a tracked object', () => {
|
|
54
|
+
const code = `
|
|
48
55
|
export default component App() {
|
|
49
56
|
let myTracked = track(0);
|
|
50
57
|
console.log(myTracked.f);
|
|
51
58
|
}
|
|
52
59
|
`;
|
|
53
|
-
|
|
54
|
-
|
|
60
|
+
expect(() => compile(code, 'test.ripple')).toThrow(
|
|
61
|
+
/Directly accessing internal property "f" of a tracked object is not allowed/,
|
|
62
|
+
);
|
|
63
|
+
});
|
|
55
64
|
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
it('should compile successfully with correct @ syntax access', () => {
|
|
66
|
+
const code = `
|
|
58
67
|
export default component App() {
|
|
59
68
|
let count = track(0);
|
|
60
69
|
console.log(@count);
|
|
61
70
|
}
|
|
62
71
|
`;
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
73
|
+
});
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
it('should compile successfully with correct get() function access', () => {
|
|
76
|
+
const code = `
|
|
68
77
|
import { get, track } from 'ripple';
|
|
69
78
|
export default component App() {
|
|
70
79
|
let count = track(0);
|
|
71
80
|
console.log(get(count));
|
|
72
81
|
}
|
|
73
82
|
`;
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
84
|
+
});
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
it('should not error on accessing __v of a non-tracked object', () => {
|
|
87
|
+
const code = `
|
|
79
88
|
export default component App() {
|
|
80
89
|
let obj = { __v: 123 };
|
|
81
90
|
console.log(obj.__v);
|
|
82
91
|
}
|
|
83
92
|
`;
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
94
|
+
});
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
it('should not error on accessing __v of a non-tracked object (member expression)', () => {
|
|
97
|
+
const code = `
|
|
89
98
|
export default component App() {
|
|
90
99
|
let data = { value: { __v: 456 } };
|
|
91
100
|
console.log(data.value.__v);
|
|
92
101
|
}
|
|
93
102
|
`;
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
104
|
+
});
|
|
96
105
|
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
it(
|
|
107
|
+
'should not error on accessing a property named like an internal one on a non-tracked object',
|
|
108
|
+
() => {
|
|
109
|
+
const code = `
|
|
99
110
|
export default component App() {
|
|
100
111
|
let config = { a: 'some_value', b: 'another_value' };
|
|
101
112
|
console.log(config.a);
|
|
102
113
|
console.log(config.b);
|
|
103
114
|
}
|
|
104
115
|
`;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
117
|
+
},
|
|
118
|
+
);
|
|
108
119
|
});
|
|
@@ -2,8 +2,7 @@ import { compile } from 'ripple/compiler';
|
|
|
2
2
|
|
|
3
3
|
describe('compiler > typescript', () => {
|
|
4
4
|
it('compiles TSInstantiationExpression', () => {
|
|
5
|
-
const source =
|
|
6
|
-
`function makeBox<T>(value: T) {
|
|
5
|
+
const source = `function makeBox<T,>(value: T) {
|
|
7
6
|
return { value };
|
|
8
7
|
}
|
|
9
8
|
const makeStringBox = makeBox<string>;
|
|
@@ -28,7 +28,7 @@ describe('composite > dynamic components', () => {
|
|
|
28
28
|
|
|
29
29
|
const obj = {
|
|
30
30
|
tracked_basic,
|
|
31
|
-
}
|
|
31
|
+
};
|
|
32
32
|
|
|
33
33
|
<obj.@tracked_basic />
|
|
34
34
|
}
|
|
@@ -49,7 +49,7 @@ describe('composite > dynamic components', () => {
|
|
|
49
49
|
|
|
50
50
|
const obj = {
|
|
51
51
|
tracked_basic,
|
|
52
|
-
}
|
|
52
|
+
};
|
|
53
53
|
|
|
54
54
|
const tracked_object = track(obj);
|
|
55
55
|
|
|
@@ -64,21 +64,21 @@ describe('composite > dynamic components', () => {
|
|
|
64
64
|
|
|
65
65
|
it('handles dynamic component switching', () => {
|
|
66
66
|
component Child1() {
|
|
67
|
-
<div>{
|
|
67
|
+
<div>{'I am child 1'}</div>
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
component Child2() {
|
|
71
|
-
<div>{
|
|
71
|
+
<div>{'I am child 2'}</div>
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
component App() {
|
|
75
|
-
let thing = track(() => Child1)
|
|
75
|
+
let thing = track(() => Child1);
|
|
76
76
|
|
|
77
77
|
<div id="container">
|
|
78
78
|
<@thing />
|
|
79
79
|
</div>
|
|
80
80
|
|
|
81
|
-
<button onClick={() => @thing = @thing === Child1 ? Child2 : Child1}>{
|
|
81
|
+
<button onClick={() => @thing = @thing === Child1 ? Child2 : Child1}>{'Change Child'}</button>
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
render(App);
|
|
@@ -7,32 +7,36 @@ describe('composite > generics', () => {
|
|
|
7
7
|
|
|
8
8
|
// 7. Generic following optional chaining
|
|
9
9
|
const maybe = {
|
|
10
|
-
factory<T>() {
|
|
10
|
+
factory: function <T>() {
|
|
11
11
|
return {
|
|
12
|
-
make<U>() {
|
|
12
|
+
make: function <U>() {
|
|
13
13
|
return 1;
|
|
14
|
-
}
|
|
14
|
+
},
|
|
15
15
|
};
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
17
|
};
|
|
18
18
|
const g = maybe?.factory<number>()?.make<boolean>();
|
|
19
19
|
|
|
20
20
|
// 8. Comparison operator (ensure '<' here NOT misparsed as generics)
|
|
21
21
|
let x = 10, y = 20;
|
|
22
|
-
const h =
|
|
22
|
+
const h =
|
|
23
|
+
x < y ? 'lt' : 'ge';
|
|
23
24
|
|
|
24
25
|
// 9. Chained comparisons with intervening generics
|
|
25
26
|
class Box<T> {
|
|
26
27
|
value: T;
|
|
27
|
-
|
|
28
|
+
|
|
29
|
+
constructor(value: T) {
|
|
28
30
|
this.value = value;
|
|
29
31
|
}
|
|
32
|
+
|
|
30
33
|
open<U>() {
|
|
31
34
|
return new Box<U>();
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
const limit = 100;
|
|
35
|
-
const i =
|
|
38
|
+
const i =
|
|
39
|
+
new Box<number>().value < limit ? 'ok' : 'no';
|
|
36
40
|
|
|
37
41
|
// 10. JSX / Element should still work
|
|
38
42
|
<div class="still-works">
|
|
@@ -57,13 +61,13 @@ describe('composite > generics', () => {
|
|
|
57
61
|
// 13. Multiple generic segments in chain
|
|
58
62
|
function foo<T>() {
|
|
59
63
|
return {
|
|
60
|
-
bar<U>() {
|
|
64
|
+
bar: function <U>() {
|
|
61
65
|
return {
|
|
62
|
-
baz<V>() {
|
|
66
|
+
baz: function <V>() {
|
|
63
67
|
return true;
|
|
64
|
-
}
|
|
68
|
+
},
|
|
65
69
|
};
|
|
66
|
-
}
|
|
70
|
+
},
|
|
67
71
|
};
|
|
68
72
|
}
|
|
69
73
|
const l = foo<number>().bar<string>().baz<boolean>();
|
|
@@ -75,9 +79,11 @@ describe('composite > generics', () => {
|
|
|
75
79
|
// 15. Generic in angle after "new" + trailing call
|
|
76
80
|
class Wrapper<T> {
|
|
77
81
|
value: T;
|
|
82
|
+
|
|
78
83
|
constructor() {
|
|
79
84
|
this.value = null as unknown as T;
|
|
80
85
|
}
|
|
86
|
+
|
|
81
87
|
unwrap<U>() {
|
|
82
88
|
return null as unknown as U;
|
|
83
89
|
}
|
|
@@ -88,11 +94,11 @@ describe('composite > generics', () => {
|
|
|
88
94
|
function getUnknown(): unknown {
|
|
89
95
|
return new Map<string, number>([['a', 1]]);
|
|
90
96
|
}
|
|
91
|
-
getUnknown.factory = function<T>() {
|
|
97
|
+
getUnknown.factory = function <T>() {
|
|
92
98
|
return {
|
|
93
|
-
make<U>() {
|
|
99
|
+
make: function <U>() {
|
|
94
100
|
return 2;
|
|
95
|
-
}
|
|
101
|
+
},
|
|
96
102
|
};
|
|
97
103
|
};
|
|
98
104
|
const raw = getUnknown();
|
|
@@ -101,14 +107,17 @@ describe('composite > generics', () => {
|
|
|
101
107
|
// 17. Generic with comma + trailing less-than comparison on next token
|
|
102
108
|
class Pair<T1, T2> {
|
|
103
109
|
first: T1;
|
|
110
|
+
|
|
104
111
|
second: T2;
|
|
112
|
+
|
|
105
113
|
constructor() {
|
|
106
114
|
this.first = null as unknown as T1;
|
|
107
115
|
this.second = null as unknown as T2;
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
118
|
const p = new Pair<number, string>();
|
|
111
|
-
const q =
|
|
119
|
+
const q =
|
|
120
|
+
1 < 2 ? p : null;
|
|
112
121
|
|
|
113
122
|
// 18. Nested generics with line breaks resembling JSX indentation
|
|
114
123
|
interface Node<T> {
|
|
@@ -119,16 +128,15 @@ describe('composite > generics', () => {
|
|
|
119
128
|
}
|
|
120
129
|
class Graph<N, E> {
|
|
121
130
|
nodes: N[];
|
|
131
|
+
|
|
122
132
|
edges: E[];
|
|
133
|
+
|
|
123
134
|
constructor() {
|
|
124
135
|
this.nodes = [];
|
|
125
136
|
this.edges = [];
|
|
126
137
|
}
|
|
127
138
|
}
|
|
128
|
-
const r = new Graph<
|
|
129
|
-
Node<string>,
|
|
130
|
-
Edge<number>
|
|
131
|
-
>();
|
|
139
|
+
const r = new Graph<Node<string>, Edge<number>>();
|
|
132
140
|
|
|
133
141
|
// 19. Ternary containing generics in both branches
|
|
134
142
|
let flag = true;
|
|
@@ -148,18 +156,17 @@ describe('composite > generics', () => {
|
|
|
148
156
|
const v = make<number>()(10);
|
|
149
157
|
|
|
150
158
|
// 23. Generic followed by tagged template (ensure not confused with JSX)
|
|
151
|
-
function tagFn<T>(strings: TemplateStringsArray, ...values
|
|
159
|
+
function tagFn<T>(strings: TemplateStringsArray, ...values) {
|
|
152
160
|
return values[0];
|
|
153
161
|
}
|
|
154
|
-
const tagResult = tagFn
|
|
162
|
+
const tagResult = tagFn`value`;
|
|
155
163
|
|
|
156
164
|
// 24. Sequence mixing: (a < b) + generic call in same statement
|
|
157
165
|
function compute<T>(x: T, y: T): T {
|
|
158
166
|
return y;
|
|
159
167
|
}
|
|
160
168
|
|
|
161
|
-
const w =
|
|
162
|
-
|
|
169
|
+
const w = x < y && compute<number>(x, y);
|
|
163
170
|
|
|
164
171
|
// Additional component focusing on edge crankers
|
|
165
172
|
|
|
@@ -170,37 +177,33 @@ describe('composite > generics', () => {
|
|
|
170
177
|
class Builder<Kind> {
|
|
171
178
|
finalize<Result>() {
|
|
172
179
|
return {
|
|
173
|
-
result: null as unknown as Result
|
|
180
|
+
result: null as unknown as Result,
|
|
174
181
|
};
|
|
175
182
|
}
|
|
176
183
|
}
|
|
177
184
|
const builder = new Builder<Number>();
|
|
178
|
-
const result = (
|
|
185
|
+
const result = (function () {
|
|
186
|
+
return builder;
|
|
187
|
+
}() as Builder<Number>).finalize<boolean>();
|
|
179
188
|
|
|
180
189
|
// 30. Angle bracket start of conditional expression line
|
|
181
190
|
function adjust<T>(value: T): T {
|
|
182
191
|
return value;
|
|
183
192
|
}
|
|
184
193
|
const val =
|
|
185
|
-
new Wrapper<number>()
|
|
186
|
-
.value < 100
|
|
187
|
-
? adjust<number>(10)
|
|
188
|
-
: adjust<number>(20);
|
|
189
|
-
|
|
194
|
+
new Wrapper<number>().value < 100 ? adjust<number>(10) : adjust<number>(20);
|
|
190
195
|
|
|
191
196
|
// 32. Generic with comments inside angle list
|
|
192
197
|
class Mapper<Key, Value> {
|
|
193
198
|
map: Map<Key, Value>;
|
|
199
|
+
|
|
194
200
|
constructor() {
|
|
195
201
|
this.map = new Map<Key, Value>();
|
|
196
202
|
}
|
|
197
203
|
}
|
|
198
|
-
const gg = new Mapper
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
/* value type */
|
|
202
|
-
number
|
|
203
|
-
>();
|
|
204
|
+
const gg = new Mapper<// key type
|
|
205
|
+
string, /* value type */
|
|
206
|
+
number>();
|
|
204
207
|
|
|
205
208
|
// 33. Map of generic instance as key
|
|
206
209
|
const mm = new Map<TrackedArray<number>, TrackedArray<string>>();
|
|
@@ -76,7 +76,7 @@ describe('composite > props', () => {
|
|
|
76
76
|
it('mutating a tracked value prop should work as intended', () => {
|
|
77
77
|
const logs: number[] = [];
|
|
78
78
|
|
|
79
|
-
component Counter({count}) {
|
|
79
|
+
component Counter({ count }) {
|
|
80
80
|
effect(() => {
|
|
81
81
|
logs.push(@count);
|
|
82
82
|
});
|
|
@@ -88,7 +88,7 @@ describe('composite > props', () => {
|
|
|
88
88
|
const count = track(0);
|
|
89
89
|
|
|
90
90
|
<div>
|
|
91
|
-
<Counter
|
|
91
|
+
<Counter {count} />
|
|
92
92
|
</div>
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -133,7 +133,8 @@ describe('composite > props', () => {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
render(App);
|
|
136
|
-
const button1 = container.querySelectorAll('button')[0]
|
|
136
|
+
const button1 = container.querySelectorAll('button')[0];
|
|
137
|
+
|
|
137
138
|
const button2 = container.querySelectorAll('button')[1];
|
|
138
139
|
|
|
139
140
|
expect(button1.className).toContain('on');
|