ripple 0.2.216 → 0.3.1
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 +52 -0
- package/package.json +16 -7
- package/src/compiler/errors.js +1 -1
- package/src/compiler/identifier-utils.js +2 -0
- package/src/compiler/index.d.ts +2 -6
- package/src/compiler/phases/1-parse/index.js +171 -233
- package/src/compiler/phases/2-analyze/index.js +192 -16
- package/src/compiler/phases/2-analyze/prune.js +2 -2
- package/src/compiler/phases/3-transform/client/index.js +308 -91
- package/src/compiler/phases/3-transform/segments.js +43 -15
- package/src/compiler/phases/3-transform/server/index.js +71 -21
- package/src/compiler/scope.js +31 -12
- package/src/compiler/source-map-utils.js +4 -6
- package/src/compiler/types/acorn.d.ts +11 -0
- package/src/compiler/types/estree-jsx.d.ts +11 -0
- package/src/compiler/types/estree.d.ts +11 -0
- package/src/compiler/types/import.d.ts +32 -18
- package/src/compiler/types/index.d.ts +75 -23
- package/src/compiler/types/parse.d.ts +7 -10
- package/src/compiler/utils.js +48 -0
- package/src/runtime/array.js +53 -22
- package/src/runtime/date.js +15 -5
- package/src/runtime/index-client.js +41 -7
- package/src/runtime/index-server.js +7 -7
- package/src/runtime/internal/client/bindings.js +2 -2
- package/src/runtime/internal/client/blocks.js +40 -1
- package/src/runtime/internal/client/context.js +8 -0
- package/src/runtime/internal/client/for.js +3 -3
- package/src/runtime/internal/client/index.js +27 -5
- package/src/runtime/internal/client/render.js +20 -8
- package/src/runtime/internal/client/runtime.js +9 -7
- package/src/runtime/internal/client/try.js +15 -22
- package/src/runtime/internal/client/utils.js +1 -1
- package/src/runtime/internal/server/context.js +8 -0
- package/src/runtime/internal/server/index.js +99 -6
- package/src/runtime/map.js +7 -7
- package/src/runtime/media-query.js +10 -1
- package/src/runtime/object.js +6 -6
- package/src/runtime/proxy.js +6 -6
- package/src/runtime/set.js +11 -11
- package/src/runtime/url-search-params.js +13 -2
- package/src/runtime/url.js +15 -5
- package/src/utils/builders.js +13 -3
- package/tests/client/array/array.copy-within.test.ripple +11 -11
- package/tests/client/array/array.derived.test.ripple +42 -42
- package/tests/client/array/array.iteration.test.ripple +12 -12
- package/tests/client/array/array.mutations.test.ripple +25 -25
- package/tests/client/array/array.static.test.ripple +103 -106
- package/tests/client/array/array.to-methods.test.ripple +8 -8
- package/tests/client/async-suspend.test.ripple +94 -0
- package/tests/client/basic/basic.attributes.test.ripple +31 -31
- package/tests/client/basic/basic.collections.test.ripple +7 -7
- package/tests/client/basic/basic.components.test.ripple +48 -10
- package/tests/client/basic/basic.errors.test.ripple +46 -31
- package/tests/client/basic/basic.events.test.ripple +11 -11
- package/tests/client/basic/basic.get-set.test.ripple +18 -18
- package/tests/client/basic/basic.reactivity.test.ripple +47 -42
- package/tests/client/basic/basic.rendering.test.ripple +7 -7
- package/tests/client/basic/basic.utilities.test.ripple +4 -4
- package/tests/client/boundaries.test.ripple +7 -7
- package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +2 -2
- package/tests/client/compiler/compiler.assignments.test.ripple +21 -21
- package/tests/client/compiler/compiler.basic.test.ripple +223 -82
- package/tests/client/compiler/compiler.tracked-access.test.ripple +8 -9
- package/tests/client/composite/composite.dynamic-components.test.ripple +8 -8
- package/tests/client/composite/composite.generics.test.ripple +4 -4
- package/tests/client/composite/composite.props.test.ripple +9 -9
- package/tests/client/composite/composite.reactivity.test.ripple +32 -26
- package/tests/client/composite/composite.render.test.ripple +13 -4
- package/tests/client/computed-properties.test.ripple +3 -3
- package/tests/client/context.test.ripple +3 -3
- package/tests/client/css/global-additional-cases.test.ripple +4 -4
- package/tests/client/css/style-identifier.test.ripple +49 -41
- package/tests/client/date.test.ripple +40 -40
- package/tests/client/dynamic-elements.test.ripple +165 -30
- package/tests/client/events.test.ripple +25 -25
- package/tests/client/for.test.ripple +76 -8
- package/tests/client/function-overload.test.ripple +0 -1
- package/tests/client/head.test.ripple +7 -7
- package/tests/client/html.test.ripple +2 -2
- package/tests/client/input-value.test.ripple +174 -176
- package/tests/client/map.test.ripple +21 -21
- package/tests/client/media-query.test.ripple +4 -4
- package/tests/client/object.test.ripple +12 -12
- package/tests/client/portal.test.ripple +4 -4
- package/tests/client/ref.test.ripple +5 -5
- package/tests/client/return.test.ripple +17 -17
- package/tests/client/set.test.ripple +16 -16
- package/tests/client/svg.test.ripple +6 -7
- package/tests/client/switch.test.ripple +10 -10
- package/tests/client/tracked-expression.test.ripple +1 -3
- package/tests/client/try.test.ripple +33 -4
- package/tests/client/url/url.derived.test.ripple +10 -9
- package/tests/client/url/url.parsing.test.ripple +10 -10
- package/tests/client/url/url.partial-removal.test.ripple +10 -10
- package/tests/client/url/url.reactivity.test.ripple +17 -17
- package/tests/client/url/url.serialization.test.ripple +4 -4
- package/tests/client/url-search-params/url-search-params.derived.test.ripple +11 -10
- package/tests/client/url-search-params/url-search-params.initialization.test.ripple +5 -7
- package/tests/client/url-search-params/url-search-params.iteration.test.ripple +13 -13
- package/tests/client/url-search-params/url-search-params.mutation.test.ripple +19 -19
- package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +17 -17
- package/tests/client/url-search-params/url-search-params.serialization.test.ripple +5 -5
- package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +5 -5
- package/tests/hydration/compiled/client/events.js +8 -11
- package/tests/hydration/compiled/client/for.js +20 -23
- package/tests/hydration/compiled/client/head.js +17 -19
- package/tests/hydration/compiled/client/hmr.js +1 -3
- package/tests/hydration/compiled/client/html.js +1 -15
- package/tests/hydration/compiled/client/if-children.js +7 -9
- package/tests/hydration/compiled/client/if.js +5 -7
- package/tests/hydration/compiled/client/mixed-control-flow.js +3 -5
- package/tests/hydration/compiled/client/portal.js +1 -1
- package/tests/hydration/compiled/client/reactivity.js +9 -11
- package/tests/hydration/compiled/client/return.js +11 -13
- package/tests/hydration/compiled/client/switch.js +4 -6
- package/tests/hydration/compiled/server/basic.js +0 -1
- package/tests/hydration/compiled/server/composite.js +0 -3
- package/tests/hydration/compiled/server/events.js +8 -12
- package/tests/hydration/compiled/server/for.js +20 -23
- package/tests/hydration/compiled/server/head.js +17 -19
- package/tests/hydration/compiled/server/hmr.js +1 -4
- package/tests/hydration/compiled/server/html.js +1 -35
- package/tests/hydration/compiled/server/if-children.js +7 -11
- package/tests/hydration/compiled/server/if.js +5 -7
- package/tests/hydration/compiled/server/mixed-control-flow.js +3 -5
- package/tests/hydration/compiled/server/portal.js +1 -9
- package/tests/hydration/compiled/server/reactivity.js +9 -11
- package/tests/hydration/compiled/server/return.js +11 -13
- package/tests/hydration/compiled/server/switch.js +4 -6
- package/tests/hydration/components/events.ripple +8 -9
- package/tests/hydration/components/for.ripple +20 -21
- package/tests/hydration/components/head.ripple +6 -8
- package/tests/hydration/components/hmr.ripple +1 -2
- package/tests/hydration/components/html.ripple +1 -3
- package/tests/hydration/components/if-children.ripple +7 -8
- package/tests/hydration/components/if.ripple +5 -6
- package/tests/hydration/components/mixed-control-flow.ripple +4 -6
- package/tests/hydration/components/portal.ripple +1 -1
- package/tests/hydration/components/reactivity.ripple +9 -10
- package/tests/hydration/components/return.ripple +11 -12
- package/tests/hydration/components/switch.ripple +6 -8
- package/tests/server/await.test.ripple +2 -2
- package/tests/server/basic.attributes.test.ripple +19 -21
- package/tests/server/basic.components.test.ripple +13 -7
- package/tests/server/basic.test.ripple +20 -21
- package/tests/server/compiler.test.ripple +5 -5
- package/tests/server/composite.props.test.ripple +6 -7
- package/tests/server/composite.test.ripple +4 -4
- package/tests/server/context.test.ripple +1 -3
- package/tests/server/dynamic-elements.test.ripple +24 -24
- package/tests/server/head.test.ripple +5 -7
- package/tests/server/style-identifier.test.ripple +16 -17
- package/types/index.d.ts +266 -62
- package/types/server.d.ts +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { flushSync } from 'ripple';
|
|
3
3
|
|
|
4
4
|
describe('basic client > rendering & text', () => {
|
|
5
5
|
it('renders static text', () => {
|
|
@@ -27,7 +27,7 @@ describe('basic client > rendering & text', () => {
|
|
|
27
27
|
|
|
28
28
|
it('renders dynamic text', () => {
|
|
29
29
|
component Basic() {
|
|
30
|
-
let text = track('Hello World');
|
|
30
|
+
let text = #ripple.track('Hello World');
|
|
31
31
|
|
|
32
32
|
<button
|
|
33
33
|
onClick={() => {
|
|
@@ -104,8 +104,8 @@ describe('basic client > rendering & text', () => {
|
|
|
104
104
|
|
|
105
105
|
it('renders with mixed static and dynamic content', () => {
|
|
106
106
|
component Basic() {
|
|
107
|
-
let name = track('World');
|
|
108
|
-
let count = track(0);
|
|
107
|
+
let name = #ripple.track('World');
|
|
108
|
+
let count = #ripple.track(0);
|
|
109
109
|
const staticMessage = 'Welcome to Ripple!';
|
|
110
110
|
|
|
111
111
|
<div class="mixed-content">
|
|
@@ -151,7 +151,7 @@ describe('basic client > rendering & text', () => {
|
|
|
151
151
|
|
|
152
152
|
it('basic operations', () => {
|
|
153
153
|
component App() {
|
|
154
|
-
let count = track(0);
|
|
154
|
+
let count = #ripple.track(0);
|
|
155
155
|
<div>{@count++}</div>
|
|
156
156
|
<div>{++@count}</div>
|
|
157
157
|
<div>{5}</div>
|
|
@@ -164,8 +164,8 @@ describe('basic client > rendering & text', () => {
|
|
|
164
164
|
|
|
165
165
|
it('renders with conditional rendering using if statements', () => {
|
|
166
166
|
component Basic() {
|
|
167
|
-
let showContent = track(false);
|
|
168
|
-
let userRole = track('guest');
|
|
167
|
+
let showContent = #ripple.track(false);
|
|
168
|
+
let userRole = #ripple.track('guest');
|
|
169
169
|
|
|
170
170
|
<button
|
|
171
171
|
onClick={() => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { tick } from 'ripple';
|
|
2
2
|
|
|
3
3
|
describe('basic client > utilities', () => {
|
|
4
4
|
it('tick function', async () => {
|
|
@@ -6,9 +6,9 @@ describe('basic client > utilities', () => {
|
|
|
6
6
|
const promise = new Promise<void>((res) => (resolve = res));
|
|
7
7
|
|
|
8
8
|
component Basic() {
|
|
9
|
-
let value = track(0);
|
|
10
|
-
effect(() => {
|
|
11
|
-
untrack(() => {
|
|
9
|
+
let value = #ripple.track(0);
|
|
10
|
+
#ripple.effect(() => {
|
|
11
|
+
#ripple.untrack(() => {
|
|
12
12
|
@value++;
|
|
13
13
|
tick().then(() => resolve());
|
|
14
14
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { flushSync,
|
|
1
|
+
import { flushSync, type Tracked } from 'ripple';
|
|
2
2
|
|
|
3
3
|
describe('passing reactivity between boundaries tests', () => {
|
|
4
4
|
it('can pass reactivity between functions with simple arrays and destructuring', () => {
|
|
5
5
|
let log: string[] = [];
|
|
6
6
|
|
|
7
7
|
function createDouble([count]: [Tracked<number>]) {
|
|
8
|
-
const double = track(() => @count * 2);
|
|
8
|
+
const double = #ripple.track(() => @count * 2);
|
|
9
9
|
|
|
10
|
-
effect(() => {
|
|
10
|
+
#ripple.effect(() => {
|
|
11
11
|
log.push('Count:' + @count);
|
|
12
12
|
});
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@ describe('passing reactivity between boundaries tests', () => {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
component App() {
|
|
18
|
-
let count = track(0);
|
|
18
|
+
let count = #ripple.track(0);
|
|
19
19
|
|
|
20
20
|
const [double] = createDouble([count]);
|
|
21
21
|
|
|
@@ -54,9 +54,9 @@ describe('passing reactivity between boundaries tests', () => {
|
|
|
54
54
|
let log: string[] = [];
|
|
55
55
|
|
|
56
56
|
function createDouble({ count }: { count: Tracked<number> }) {
|
|
57
|
-
const double = track(() => @count * 2);
|
|
57
|
+
const double = #ripple.track(() => @count * 2);
|
|
58
58
|
|
|
59
|
-
effect(() => {
|
|
59
|
+
#ripple.effect(() => {
|
|
60
60
|
log.push('Count:' + @count);
|
|
61
61
|
});
|
|
62
62
|
|
|
@@ -64,7 +64,7 @@ describe('passing reactivity between boundaries tests', () => {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
component App() {
|
|
67
|
-
let count = track(0);
|
|
67
|
+
let count = #ripple.track(0);
|
|
68
68
|
|
|
69
69
|
const { double } = createDouble({ count });
|
|
70
70
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
exports[`compiler > assignments > compiles tracked values in effect with assignment expression 1`] = `"state.count = _$_.get(count);"`;
|
|
4
4
|
|
|
5
5
|
exports[`compiler > assignments > compiles tracked values in effect with update expressions 1`] = `
|
|
6
|
-
"_$_.
|
|
6
|
+
"_$_.untrack(() => {
|
|
7
7
|
state.preIncrement = _$_.update_pre(count);
|
|
8
8
|
state.postIncrement = _$_.update(count);
|
|
9
9
|
state.preDecrement = _$_.update_pre(count, -1);
|
|
10
10
|
state.postDecrement = _$_.update(count, -1);
|
|
11
|
-
})
|
|
11
|
+
});"
|
|
12
12
|
`;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RippleArray } from 'ripple';
|
|
2
2
|
import { compile } from 'ripple/compiler';
|
|
3
3
|
|
|
4
|
+
const EFFECT_BODY_REGEX = /_\$\_\.effect\(\(\) => \{([\s\S]*?)\n\t\}\);/;
|
|
5
|
+
|
|
4
6
|
describe('compiler > assignments', () => {
|
|
5
7
|
it('properly handles JS assignments, reads and updates to array indices', () => {
|
|
6
8
|
const logs: (number | undefined)[] = [];
|
|
7
9
|
|
|
8
10
|
component App() {
|
|
9
11
|
let items: number[] = [];
|
|
10
|
-
let tracked_items = track<number[]>([]);
|
|
12
|
+
let tracked_items = #ripple.track<number[]>([]);
|
|
11
13
|
let items2 = new Array();
|
|
12
|
-
let items3
|
|
14
|
+
let items3: RippleArray<number> = #ripple[];
|
|
13
15
|
let i = 0;
|
|
14
16
|
|
|
15
17
|
logs.push(items[0]);
|
|
@@ -107,34 +109,32 @@ describe('compiler > assignments', () => {
|
|
|
107
109
|
|
|
108
110
|
it('compiles tracked values in effect with assignment expression', () => {
|
|
109
111
|
const source = `component App() {
|
|
110
|
-
let count = track(0);
|
|
112
|
+
let count = #ripple.track(0);
|
|
111
113
|
|
|
112
|
-
effect(() => {
|
|
113
|
-
|
|
114
|
-
})
|
|
114
|
+
#ripple.effect(() => {
|
|
115
|
+
state.count = @count;
|
|
116
|
+
});
|
|
115
117
|
}`;
|
|
116
118
|
const result = compile(source, 'test.ripple');
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
expect(effectMatch?.[1].trim()).toMatchSnapshot();
|
|
119
|
+
const effect_match = result.js.code.match(EFFECT_BODY_REGEX);
|
|
120
|
+
expect(effect_match?.[1].trim()).toMatchSnapshot();
|
|
120
121
|
});
|
|
121
122
|
|
|
122
123
|
it('compiles tracked values in effect with update expressions', () => {
|
|
123
124
|
const source = `component App() {
|
|
124
|
-
let count = track(5);
|
|
125
|
+
let count = #ripple.track(5);
|
|
125
126
|
|
|
126
|
-
effect(() => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
#ripple.effect(() => {
|
|
128
|
+
#ripple.untrack(() => {
|
|
129
|
+
state.preIncrement = ++@count;
|
|
130
|
+
state.postIncrement = @count++;
|
|
131
|
+
state.preDecrement = --@count;
|
|
132
|
+
state.postDecrement = @count--;
|
|
133
|
+
});
|
|
132
134
|
});
|
|
133
|
-
})
|
|
134
135
|
}`;
|
|
135
136
|
const result = compile(source, 'test.ripple');
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
expect(effectMatch?.[1].trim()).toMatchSnapshot();
|
|
137
|
+
const effect_match = result.js.code.match(EFFECT_BODY_REGEX);
|
|
138
|
+
expect(effect_match?.[1].trim()).toMatchSnapshot();
|
|
139
139
|
});
|
|
140
140
|
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { parse, compile, compile_to_volar_mappings } from 'ripple/compiler';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
obfuscate_identifier,
|
|
4
|
+
RIPPLE_NAMESPACE_IDENTIFIER,
|
|
5
|
+
} from 'ripple/compiler/internal/identifier/utils';
|
|
3
6
|
import type * as AST from 'estree';
|
|
4
7
|
|
|
5
8
|
function count_occurrences(string: string, subString: string): number {
|
|
@@ -19,25 +22,25 @@ describe('compiler > basics', () => {
|
|
|
19
22
|
const source = `export component App() {
|
|
20
23
|
<div id="myid" class="myclass">{"Hello World"}</div>
|
|
21
24
|
|
|
22
|
-
<style>#style</style>
|
|
25
|
+
<style>#ripple.style</style>
|
|
23
26
|
}`;
|
|
24
27
|
const style1 = '.myid {color: green }';
|
|
25
28
|
const style2 = '#myid {color: green }';
|
|
26
29
|
const style3 = 'div {color: green }';
|
|
27
30
|
|
|
28
|
-
let input = source.replace('#style', style1);
|
|
31
|
+
let input = source.replace('#ripple.style', style1);
|
|
29
32
|
let ast = parse(input);
|
|
30
33
|
expect(
|
|
31
34
|
((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
|
|
32
35
|
).toEqual(style1);
|
|
33
36
|
|
|
34
|
-
input = source.replace('#style', style2);
|
|
37
|
+
input = source.replace('#ripple.style', style2);
|
|
35
38
|
ast = parse(input);
|
|
36
39
|
expect(
|
|
37
40
|
((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
|
|
38
41
|
).toEqual(style2);
|
|
39
42
|
|
|
40
|
-
input = source.replace('#style', style3);
|
|
43
|
+
input = source.replace('#ripple.style', style3);
|
|
41
44
|
ast = parse(input);
|
|
42
45
|
expect(
|
|
43
46
|
((ast.body[0] as AST.ExportNamedDeclaration).declaration as unknown as AST.Component)?.css.source,
|
|
@@ -197,8 +200,8 @@ describe('compiler > basics', () => {
|
|
|
197
200
|
class Box<T> {
|
|
198
201
|
value: T;
|
|
199
202
|
|
|
200
|
-
method<T>():
|
|
201
|
-
return this.value;
|
|
203
|
+
method<U extends T>(): U {
|
|
204
|
+
return this.value as U;
|
|
202
205
|
}
|
|
203
206
|
|
|
204
207
|
constructor(value: T) {
|
|
@@ -247,91 +250,152 @@ describe('compiler > basics', () => {
|
|
|
247
250
|
expect(js.code).not.toMatch(/_\$_\.template\(`<!><!>`,\s*1,\s*3\)/);
|
|
248
251
|
});
|
|
249
252
|
|
|
250
|
-
it(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
import {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
253
|
+
// it(
|
|
254
|
+
// 'imports and uses only obfuscated Tracked imports when encountering only shorthand syntax',
|
|
255
|
+
// () => {
|
|
256
|
+
// const source = `
|
|
257
|
+
// import { RippleArray, RippleObject, RippleSet, RippleMap, createRefKey } from 'ripple';
|
|
258
|
+
// component App() {
|
|
259
|
+
// const items = #ripple[1, 2, 3];
|
|
260
|
+
// const obj = #ripple{ a: 1, b: 2, c: 3 };
|
|
261
|
+
// const set = #ripple.set([1, 2, 3]);
|
|
262
|
+
// const map = #ripple.map([['a', 1], ['b', 2], ['c', 3]]);
|
|
263
|
+
|
|
264
|
+
// <div {ref () => {}} />
|
|
265
|
+
// }
|
|
266
|
+
// `;
|
|
267
|
+
// const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
268
|
+
|
|
269
|
+
// expect(count_occurrences(result, 'RippleArray')).toBe(1);
|
|
270
|
+
// expect(count_occurrences(result, 'RippleObject')).toBe(1);
|
|
271
|
+
// expect(count_occurrences(result, 'RippleSet')).toBe(1);
|
|
272
|
+
// expect(count_occurrences(result, 'RippleMap')).toBe(1);
|
|
273
|
+
// expect(count_occurrences(result, 'createRefKey')).toBe(1);
|
|
274
|
+
// },
|
|
275
|
+
// );
|
|
276
|
+
|
|
277
|
+
// it(
|
|
278
|
+
// 'adds obfuscated imports and keeps renamed Tracked imports intact when encountering shorthand syntax',
|
|
279
|
+
// () => {
|
|
280
|
+
// const source = `
|
|
281
|
+
// import { RippleArray as TA, RippleObject as TO, RippleSet as TS, RippleMap as TM, createRefKey as crk } from 'ripple';
|
|
282
|
+
// component App() {
|
|
283
|
+
// const items = #ripple[1, 2, 3];
|
|
284
|
+
// const obj = #ripple{ a: 1, b: 2, c: 3 };
|
|
285
|
+
// const set = #ripple.set([1, 2, 3]);
|
|
286
|
+
// const map = #ripple.map([['a', 1], ['b', 2], ['c', 3]]);
|
|
287
|
+
|
|
288
|
+
// <div {ref () => {}} />
|
|
289
|
+
// }
|
|
290
|
+
// `;
|
|
291
|
+
// const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
292
|
+
|
|
293
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleArray'))).toBe(2);
|
|
294
|
+
// expect(count_occurrences(result, 'TA')).toBe(1);
|
|
295
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleObject'))).toBe(2);
|
|
296
|
+
// expect(count_occurrences(result, 'TO')).toBe(1);
|
|
297
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleSet'))).toBe(2);
|
|
298
|
+
// expect(count_occurrences(result, 'TS')).toBe(1);
|
|
299
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleMap'))).toBe(2);
|
|
300
|
+
// expect(count_occurrences(result, 'TM')).toBe(1);
|
|
301
|
+
// expect(count_occurrences(result, obfuscate_identifier('createRefKey'))).toBe(2);
|
|
302
|
+
// expect(count_occurrences(result, 'crk')).toBe(1);
|
|
303
|
+
// },
|
|
304
|
+
// );
|
|
305
|
+
|
|
306
|
+
// it('adds hidden obfuscated imports for shorthand syntax', () => {
|
|
307
|
+
// const source = `
|
|
308
|
+
// component App() {
|
|
309
|
+
// const items = #ripple[1, 2, 3];
|
|
310
|
+
// const obj = #ripple{ a: 1, b: 2, c: 3 };
|
|
311
|
+
// const set = #ripple.set([1, 2, 3]);
|
|
312
|
+
// const map = #ripple.map([['a', 1], ['b', 2], ['c', 3]]);
|
|
313
|
+
|
|
314
|
+
// <div {ref () => {}} />
|
|
315
|
+
// }
|
|
316
|
+
// `;
|
|
317
|
+
// const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
318
|
+
|
|
319
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleArray'))).toBe(2);
|
|
320
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleObject'))).toBe(2);
|
|
321
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleSet'))).toBe(2);
|
|
322
|
+
// expect(count_occurrences(result, obfuscate_identifier('RippleMap'))).toBe(2);
|
|
323
|
+
// expect(count_occurrences(result, obfuscate_identifier('createRefKey'))).toBe(2);
|
|
324
|
+
// });
|
|
325
|
+
|
|
326
|
+
it('prints longhand tracked property values in to_ts output while preserving [\'#v\']', () => {
|
|
327
|
+
const source = `
|
|
328
|
+
import { createRefKey } from 'ripple';
|
|
261
329
|
|
|
262
330
|
component App() {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
331
|
+
let value = #ripple.track('test');
|
|
332
|
+
function inputRef(node) {}
|
|
333
|
+
|
|
334
|
+
const props = {
|
|
335
|
+
id: 'example',
|
|
336
|
+
@value,
|
|
337
|
+
[createRefKey()]: inputRef,
|
|
338
|
+
};
|
|
269
339
|
}
|
|
270
340
|
`;
|
|
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, 'TrackedObject')).toBe(1);
|
|
275
|
-
expect(count_occurrences(result, 'TrackedSet')).toBe(1);
|
|
276
|
-
expect(count_occurrences(result, 'TrackedMap')).toBe(1);
|
|
277
|
-
expect(count_occurrences(result, 'createRefKey')).toBe(1);
|
|
278
|
-
},
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
it(
|
|
282
|
-
'adds obfuscated imports and keeps renamed Tracked imports intact when encountering shorthand syntax',
|
|
283
|
-
() => {
|
|
284
|
-
const source = `
|
|
285
|
-
import {
|
|
286
|
-
TrackedArray as TA,
|
|
287
|
-
TrackedObject as TO,
|
|
288
|
-
TrackedSet as TS,
|
|
289
|
-
TrackedMap as TM,
|
|
290
|
-
createRefKey as crk,
|
|
291
|
-
} from 'ripple';
|
|
292
341
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const map = new #Map([['a', 1], ['b', 2], ['c', 3]]);
|
|
342
|
+
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
343
|
+
|
|
344
|
+
expect(result).toMatch(/value:\s*value\??\.\['#v'\]/);
|
|
345
|
+
});
|
|
298
346
|
|
|
299
|
-
|
|
347
|
+
it('rewrites standalone #ripple to the hidden namespace identifier in to_ts output', () => {
|
|
348
|
+
const source = `
|
|
349
|
+
export component App() {
|
|
350
|
+
#ripple
|
|
300
351
|
}
|
|
301
352
|
`;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
expect(count_occurrences(result, 'TM')).toBe(1);
|
|
312
|
-
expect(count_occurrences(result, obfuscate_identifier('createRefKey'))).toBe(2);
|
|
313
|
-
expect(count_occurrences(result, 'crk')).toBe(1);
|
|
314
|
-
},
|
|
315
|
-
);
|
|
316
|
-
|
|
317
|
-
it('adds hidden obfuscated imports for shorthand syntax', () => {
|
|
353
|
+
|
|
354
|
+
const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true }).code;
|
|
355
|
+
|
|
356
|
+
expect(result).toContain(RIPPLE_NAMESPACE_IDENTIFIER);
|
|
357
|
+
expect(result).toContain(`${RIPPLE_NAMESPACE_IDENTIFIER};`);
|
|
358
|
+
expect(result).not.toContain('#ripple;');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('preserves generic type args in interface extends for Volar mappings', () => {
|
|
318
362
|
const source = `
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const set = new #Set([1, 2, 3]);
|
|
323
|
-
const map = new #Map([['a', 1], ['b', 2], ['c', 3]]);
|
|
363
|
+
interface PolymorphicProps<T extends keyof HTMLElementTagNameMap> {
|
|
364
|
+
as?: T;
|
|
365
|
+
}
|
|
324
366
|
|
|
325
|
-
|
|
367
|
+
interface Props extends PolymorphicProps<'div'> {
|
|
368
|
+
id: string;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export component App(props: Props) {
|
|
372
|
+
<div id={props.id} />
|
|
326
373
|
}
|
|
327
374
|
`;
|
|
375
|
+
|
|
328
376
|
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
329
377
|
|
|
330
|
-
expect(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
378
|
+
expect(result).toContain('extends PolymorphicProps<\'div\'>');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('handles if-else expression statements in Volar mappings', () => {
|
|
382
|
+
const source = `
|
|
383
|
+
export component App() {
|
|
384
|
+
let level = #ripple.track(1);
|
|
385
|
+
|
|
386
|
+
<button
|
|
387
|
+
onClick={() => {
|
|
388
|
+
if (@level === 1) @level = 2;
|
|
389
|
+
else if (@level === 2) @level = 3;
|
|
390
|
+
else @level = 1;
|
|
391
|
+
}}
|
|
392
|
+
>
|
|
393
|
+
{'Toggle'}
|
|
394
|
+
</button>
|
|
395
|
+
}
|
|
396
|
+
`;
|
|
397
|
+
|
|
398
|
+
expect(() => compile_to_volar_mappings(source, 'test.ripple')).not.toThrow();
|
|
335
399
|
});
|
|
336
400
|
|
|
337
401
|
it('should not error on having js below markup in the same scope', () => {
|
|
@@ -357,6 +421,23 @@ export component App() {
|
|
|
357
421
|
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
358
422
|
});
|
|
359
423
|
|
|
424
|
+
it('converts nested component children into props in Volar mappings', () => {
|
|
425
|
+
const source = `
|
|
426
|
+
export component App() {
|
|
427
|
+
<ark.div class="host-class" data-value="42">
|
|
428
|
+
component asChild({ children, href, ...rest }: { href: string; [key: string]: any }) {
|
|
429
|
+
<a id="aschild-anchor" {href} {...rest} data-extra="yes">{'Link'}</a>
|
|
430
|
+
}
|
|
431
|
+
</ark.div>
|
|
432
|
+
}
|
|
433
|
+
`;
|
|
434
|
+
const result = compile_to_volar_mappings(source, 'test.ripple').code;
|
|
435
|
+
|
|
436
|
+
expect(result).toContain('<ark.div class="host-class" data-value="42" asChild={');
|
|
437
|
+
expect(result).not.toContain('children={() =>');
|
|
438
|
+
expect(result).not.toContain('function asChild');
|
|
439
|
+
});
|
|
440
|
+
|
|
360
441
|
it('should not error on `this` MemberExpression with a UpdateExpression', () => {
|
|
361
442
|
const code = `
|
|
362
443
|
class Test {
|
|
@@ -374,14 +455,13 @@ export component App() {
|
|
|
374
455
|
expect(() => compile(code, 'test.ripple')).not.toThrow();
|
|
375
456
|
});
|
|
376
457
|
|
|
377
|
-
it('should inject __block for track() calls inside class constructors', () => {
|
|
458
|
+
it('should inject __block for #ripple.track() calls inside class constructors', () => {
|
|
378
459
|
const source = `
|
|
379
|
-
import { track } from 'ripple';
|
|
380
460
|
|
|
381
461
|
class Store {
|
|
382
462
|
constructor() {
|
|
383
|
-
this.count = track(0);
|
|
384
|
-
this.items = #[1, 2, 3];
|
|
463
|
+
this.count = #ripple.track(0);
|
|
464
|
+
this.items = #ripple[1, 2, 3];
|
|
385
465
|
}
|
|
386
466
|
}
|
|
387
467
|
|
|
@@ -397,4 +477,65 @@ export component App() {
|
|
|
397
477
|
expect(code).toContain('__block');
|
|
398
478
|
expect(code).toContain('_$_.scope()');
|
|
399
479
|
});
|
|
480
|
+
|
|
481
|
+
it('parses #ripple.effect and #ripple.untrack calls', () => {
|
|
482
|
+
const source = `
|
|
483
|
+
component App() {
|
|
484
|
+
let count = #ripple.track(0);
|
|
485
|
+
|
|
486
|
+
#ripple.effect(() => {
|
|
487
|
+
const snapshot = #ripple.untrack(() => @count);
|
|
488
|
+
console.log(snapshot);
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
`;
|
|
492
|
+
|
|
493
|
+
const ast = parse(source);
|
|
494
|
+
const ast_json = JSON.stringify(ast);
|
|
495
|
+
|
|
496
|
+
expect(ast_json).toContain('"source_name":"#ripple.effect"');
|
|
497
|
+
expect(ast_json).toContain('"source_name":"#ripple.untrack"');
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('collects duplicate declaration parser errors in loose mode', () => {
|
|
501
|
+
const source = `
|
|
502
|
+
export component App() {
|
|
503
|
+
let test = #ripple.track(false);
|
|
504
|
+
let test = 'hey';
|
|
505
|
+
}
|
|
506
|
+
`;
|
|
507
|
+
|
|
508
|
+
expect(() => compile(source, 'test.ripple')).toThrow(
|
|
509
|
+
'Identifier \'test\' has already been declared',
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true });
|
|
513
|
+
|
|
514
|
+
expect(
|
|
515
|
+
result.errors.some(
|
|
516
|
+
(item) => item.message.includes('Identifier \'test\' has already been declared'),
|
|
517
|
+
),
|
|
518
|
+
).toBe(true);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it('parses bare #ripple in loose mode for autocomplete recovery', () => {
|
|
522
|
+
const source = `
|
|
523
|
+
export component App() {
|
|
524
|
+
#ripple
|
|
525
|
+
}
|
|
526
|
+
`;
|
|
527
|
+
|
|
528
|
+
expect(() => compile_to_volar_mappings(source, 'test.ripple', { loose: true })).not.toThrow();
|
|
529
|
+
|
|
530
|
+
const result = compile_to_volar_mappings(source, 'test.ripple', { loose: true });
|
|
531
|
+
expect(result.errors).toEqual([]);
|
|
532
|
+
|
|
533
|
+
const ripple_offset = source.indexOf('#ripple');
|
|
534
|
+
const mapping = result.mappings.find(
|
|
535
|
+
(mapping) => mapping.sourceOffsets[0] === ripple_offset &&
|
|
536
|
+
mapping.lengths[0] === '#ripple'.length,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
expect(mapping?.data.customData.suppressedDiagnostics).toContain(18016);
|
|
540
|
+
});
|
|
400
541
|
});
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { compile } from 'ripple/compiler';
|
|
2
|
-
import { track } from 'ripple';
|
|
3
2
|
|
|
4
3
|
describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
5
4
|
it('should error on direct access to __v of a tracked object', () => {
|
|
6
5
|
const code = `
|
|
7
6
|
export default component App() {
|
|
8
|
-
let count = track(0);
|
|
7
|
+
let count = #ripple.track(0);
|
|
9
8
|
console.log(count.__v);
|
|
10
9
|
}
|
|
11
10
|
`;
|
|
@@ -17,7 +16,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
|
17
16
|
it('should error on direct access to "a" (get/set config) of a tracked object', () => {
|
|
18
17
|
const code = `
|
|
19
18
|
export default component App() {
|
|
20
|
-
let myTracked = track(0);
|
|
19
|
+
let myTracked = #ripple.track(0);
|
|
21
20
|
console.log(myTracked.a);
|
|
22
21
|
}
|
|
23
22
|
`;
|
|
@@ -29,7 +28,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
|
29
28
|
it('should error on direct access to "b" (block) of a tracked object', () => {
|
|
30
29
|
const code = `
|
|
31
30
|
export default component App() {
|
|
32
|
-
let myTracked = track(0);
|
|
31
|
+
let myTracked = #ripple.track(0);
|
|
33
32
|
console.log(myTracked.b);
|
|
34
33
|
}
|
|
35
34
|
`;
|
|
@@ -41,7 +40,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
|
41
40
|
it('should error on direct access to "c" (clock) of a tracked object', () => {
|
|
42
41
|
const code = `
|
|
43
42
|
export default component App() {
|
|
44
|
-
let myTracked = track(0);
|
|
43
|
+
let myTracked = #ripple.track(0);
|
|
45
44
|
console.log(myTracked.c);
|
|
46
45
|
}
|
|
47
46
|
`;
|
|
@@ -53,7 +52,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
|
53
52
|
it('should error on direct access to "f" (flags) of a tracked object', () => {
|
|
54
53
|
const code = `
|
|
55
54
|
export default component App() {
|
|
56
|
-
let myTracked = track(0);
|
|
55
|
+
let myTracked = #ripple.track(0);
|
|
57
56
|
console.log(myTracked.f);
|
|
58
57
|
}
|
|
59
58
|
`;
|
|
@@ -65,7 +64,7 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
|
65
64
|
it('should compile successfully with correct @ syntax access', () => {
|
|
66
65
|
const code = `
|
|
67
66
|
export default component App() {
|
|
68
|
-
let count = track(0);
|
|
67
|
+
let count = #ripple.track(0);
|
|
69
68
|
console.log(@count);
|
|
70
69
|
}
|
|
71
70
|
`;
|
|
@@ -74,9 +73,9 @@ describe('Compiler: Tracked Object Direct Access Checks', () => {
|
|
|
74
73
|
|
|
75
74
|
it('should compile successfully with correct get() function access', () => {
|
|
76
75
|
const code = `
|
|
77
|
-
|
|
76
|
+
import { get } from 'ripple';
|
|
78
77
|
export default component App() {
|
|
79
|
-
let count = track(0);
|
|
78
|
+
let count = #ripple.track(0);
|
|
80
79
|
console.log(get(count));
|
|
81
80
|
}
|
|
82
81
|
`;
|