ripple 0.2.7 → 0.2.9
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/package.json +1 -1
- package/src/compiler/phases/1-parse/index.js +13 -15
- package/src/compiler/phases/3-transform/index.js +6 -4
- package/src/runtime/index.js +2 -0
- package/src/runtime/internal/client/blocks.js +15 -4
- package/src/runtime/internal/client/for.js +1 -2
- package/src/runtime/internal/client/runtime.js +13 -0
- package/tests/__snapshots__/for.test.ripple.snap +81 -0
- package/tests/composite.test.ripple +26 -0
- package/tests/for.test.ripple +58 -0
- package/tests/ref.test.ripple +52 -0
- package/types/index.d.ts +6 -0
package/package.json
CHANGED
|
@@ -67,7 +67,6 @@ function RipplePlugin(config) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
jsx_parseExpressionContainer() {
|
|
70
|
-
const tok = this.acornTypeScript.tokTypes;
|
|
71
70
|
let node = this.startNode();
|
|
72
71
|
this.next();
|
|
73
72
|
|
|
@@ -136,13 +135,6 @@ function RipplePlugin(config) {
|
|
|
136
135
|
this.raise(t.start, 'attributes must only be assigned a non-empty expression'),
|
|
137
136
|
t
|
|
138
137
|
);
|
|
139
|
-
// case tt.bracketL:
|
|
140
|
-
// var t = this.jsx_parseTupleContainer();
|
|
141
|
-
// return (
|
|
142
|
-
// 'JSXEmptyExpression' === t.expression.type &&
|
|
143
|
-
// this.raise(t.start, 'attributes must only be assigned a non-empty expression'),
|
|
144
|
-
// t
|
|
145
|
-
// );
|
|
146
138
|
case tok.jsxTagStart:
|
|
147
139
|
case tt.string:
|
|
148
140
|
return this.parseExprAtom();
|
|
@@ -294,7 +286,10 @@ function RipplePlugin(config) {
|
|
|
294
286
|
case 62: // '>'
|
|
295
287
|
case 125: {
|
|
296
288
|
// '}'
|
|
297
|
-
if (
|
|
289
|
+
if (
|
|
290
|
+
ch === 125 &&
|
|
291
|
+
(this.#path.length === 0 || this.#path.at(-1)?.type === 'Component')
|
|
292
|
+
) {
|
|
298
293
|
return original.readToken.call(this, ch);
|
|
299
294
|
}
|
|
300
295
|
this.raise(
|
|
@@ -364,12 +359,8 @@ function RipplePlugin(config) {
|
|
|
364
359
|
|
|
365
360
|
if (element.selfClosing) {
|
|
366
361
|
this.#path.pop();
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
this.type?.keyword !== 'for' &&
|
|
370
|
-
this.type?.keyword !== 'try'
|
|
371
|
-
) {
|
|
372
|
-
// Eat the closing `/>`
|
|
362
|
+
|
|
363
|
+
if (this.type.label === '</>/<=/>=') {
|
|
373
364
|
this.pos--;
|
|
374
365
|
this.next();
|
|
375
366
|
}
|
|
@@ -400,6 +391,13 @@ function RipplePlugin(config) {
|
|
|
400
391
|
} else {
|
|
401
392
|
this.parseTemplateBody(element.children);
|
|
402
393
|
}
|
|
394
|
+
const tokContexts = this.acornTypeScript.tokContexts;
|
|
395
|
+
|
|
396
|
+
const curContext = this.curContext();
|
|
397
|
+
|
|
398
|
+
if (curContext === tokContexts.tc_expr) {
|
|
399
|
+
this.context.pop();
|
|
400
|
+
}
|
|
403
401
|
}
|
|
404
402
|
|
|
405
403
|
this.finishNode(element, 'Element');
|
|
@@ -806,10 +806,12 @@ const visitors = {
|
|
|
806
806
|
const binding = context.state.scope.get(left.name);
|
|
807
807
|
const transformers = left && binding?.transform;
|
|
808
808
|
|
|
809
|
-
if (left === argument
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
809
|
+
if (left === argument ) {
|
|
810
|
+
if (transformers?.update) {
|
|
811
|
+
return transformers.update(node);
|
|
812
|
+
} else if (binding.kind === 'prop') {
|
|
813
|
+
throw new Error('Cannot update component prop property, component props are not writable');
|
|
814
|
+
}
|
|
813
815
|
}
|
|
814
816
|
|
|
815
817
|
context.next();
|
package/src/runtime/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
run_block,
|
|
20
20
|
run_teardown,
|
|
21
21
|
schedule_update,
|
|
22
|
+
set_property,
|
|
22
23
|
} from './runtime';
|
|
23
24
|
import { suspend } from './try';
|
|
24
25
|
|
|
@@ -69,19 +70,29 @@ export function async(fn) {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
export function use(element, get_fn) {
|
|
72
|
-
var
|
|
73
|
+
var use_obj = undefined;
|
|
73
74
|
var e;
|
|
75
|
+
var current_block = active_block;
|
|
74
76
|
|
|
75
77
|
return block(RENDER_BLOCK, () => {
|
|
76
|
-
if (
|
|
78
|
+
if (use_obj !== (use_obj = get_fn())) {
|
|
77
79
|
if (e) {
|
|
78
80
|
destroy_block(e);
|
|
79
81
|
e = null;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
if (
|
|
84
|
+
if (use_obj) {
|
|
83
85
|
e = branch(() => {
|
|
84
|
-
effect(() =>
|
|
86
|
+
effect(() => {
|
|
87
|
+
if (typeof use_obj === 'function') {
|
|
88
|
+
return use_obj(element);
|
|
89
|
+
} else {
|
|
90
|
+
set_property(use_obj, '$current', element, current_block);
|
|
91
|
+
return () => {
|
|
92
|
+
set_property(use_obj, '$current', null, current_block);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
});
|
|
85
96
|
});
|
|
86
97
|
}
|
|
87
98
|
}
|
|
@@ -46,8 +46,7 @@ export function for_block(node, get_collection, render_fn, flags) {
|
|
|
46
46
|
: array_from(collection);
|
|
47
47
|
|
|
48
48
|
if (array[TRACKED_OBJECT] !== undefined) {
|
|
49
|
-
|
|
50
|
-
get_all_elements(collection);
|
|
49
|
+
array = get_all_elements(collection);
|
|
51
50
|
collection.$length;
|
|
52
51
|
}
|
|
53
52
|
|
|
@@ -936,3 +936,16 @@ export function pop_component() {
|
|
|
936
936
|
export function use_prop() {
|
|
937
937
|
return Symbol(USE_PROP);
|
|
938
938
|
}
|
|
939
|
+
|
|
940
|
+
export function ref(value) {
|
|
941
|
+
var block = active_block || active_scope;
|
|
942
|
+
if (!block) {
|
|
943
|
+
throw new Error('ref() must be called within a component or reactive context');
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Create a tracked object with a $current property
|
|
947
|
+
var ref_obj = { $current: value };
|
|
948
|
+
|
|
949
|
+
// Make the $current property reactive
|
|
950
|
+
return tracked_object(ref_obj, ['$current'], block);
|
|
951
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`for statements > render a simple dynamic array 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<!---->
|
|
6
|
+
<div
|
|
7
|
+
class="Item 1"
|
|
8
|
+
>
|
|
9
|
+
Item 1
|
|
10
|
+
</div>
|
|
11
|
+
<div
|
|
12
|
+
class="Item 2"
|
|
13
|
+
>
|
|
14
|
+
Item 2
|
|
15
|
+
</div>
|
|
16
|
+
<div
|
|
17
|
+
class="Item 3"
|
|
18
|
+
>
|
|
19
|
+
Item 3
|
|
20
|
+
</div>
|
|
21
|
+
<!---->
|
|
22
|
+
<button>
|
|
23
|
+
Add Item
|
|
24
|
+
</button>
|
|
25
|
+
|
|
26
|
+
</div>
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
exports[`for statements > render a simple dynamic array 2`] = `
|
|
30
|
+
<div>
|
|
31
|
+
<!---->
|
|
32
|
+
<div
|
|
33
|
+
class="Item 1"
|
|
34
|
+
>
|
|
35
|
+
Item 1
|
|
36
|
+
</div>
|
|
37
|
+
<div
|
|
38
|
+
class="Item 2"
|
|
39
|
+
>
|
|
40
|
+
Item 2
|
|
41
|
+
</div>
|
|
42
|
+
<div
|
|
43
|
+
class="Item 3"
|
|
44
|
+
>
|
|
45
|
+
Item 3
|
|
46
|
+
</div>
|
|
47
|
+
<div
|
|
48
|
+
class="Item 4"
|
|
49
|
+
>
|
|
50
|
+
Item 4
|
|
51
|
+
</div>
|
|
52
|
+
<!---->
|
|
53
|
+
<button>
|
|
54
|
+
Add Item
|
|
55
|
+
</button>
|
|
56
|
+
|
|
57
|
+
</div>
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
exports[`for statements > render a simple static array 1`] = `
|
|
61
|
+
<div>
|
|
62
|
+
<!---->
|
|
63
|
+
<div
|
|
64
|
+
class="Item 1"
|
|
65
|
+
>
|
|
66
|
+
Item 1
|
|
67
|
+
</div>
|
|
68
|
+
<div
|
|
69
|
+
class="Item 2"
|
|
70
|
+
>
|
|
71
|
+
Item 2
|
|
72
|
+
</div>
|
|
73
|
+
<div
|
|
74
|
+
class="Item 3"
|
|
75
|
+
>
|
|
76
|
+
Item 3
|
|
77
|
+
</div>
|
|
78
|
+
<!---->
|
|
79
|
+
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
@@ -21,6 +21,32 @@ describe('composite components', () => {
|
|
|
21
21
|
container = null;
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
+
it('renders composite components', () => {
|
|
25
|
+
component Button({ $count }) {
|
|
26
|
+
<div>{$count}</div>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
component App() {
|
|
30
|
+
let $count = 0;
|
|
31
|
+
<button onClick={() => $count++}>{'Increment'}</button>
|
|
32
|
+
<Button $count={$count} />
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
render(App);
|
|
36
|
+
|
|
37
|
+
const button = container.querySelector('button');
|
|
38
|
+
|
|
39
|
+
button.click();
|
|
40
|
+
flushSync();
|
|
41
|
+
|
|
42
|
+
expect(container.querySelector('div').textContent).toBe('1');
|
|
43
|
+
|
|
44
|
+
button.click();
|
|
45
|
+
flushSync();
|
|
46
|
+
|
|
47
|
+
expect(container.querySelector('div').textContent).toBe('2');
|
|
48
|
+
});
|
|
49
|
+
|
|
24
50
|
it('renders composite components with object state', () => {
|
|
25
51
|
component Button({ obj }) {
|
|
26
52
|
<button class='count2' onClick={() => {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mount, flushSync, array } from 'ripple';
|
|
4
|
+
|
|
5
|
+
describe('for statements', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
function render(component) {
|
|
9
|
+
mount(component, {
|
|
10
|
+
target: container
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
container = document.createElement('div');
|
|
16
|
+
document.body.appendChild(container);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
document.body.removeChild(container);
|
|
21
|
+
container = null;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('render a simple static array', () => {
|
|
25
|
+
component App() {
|
|
26
|
+
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
27
|
+
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
<div class={item}>{item}</div>
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
render(App);
|
|
34
|
+
|
|
35
|
+
expect(container).toMatchSnapshot();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('render a simple dynamic array', () => {
|
|
39
|
+
component App() {
|
|
40
|
+
const items = array('Item 1', 'Item 2', 'Item 3');
|
|
41
|
+
|
|
42
|
+
for (const item of items) {
|
|
43
|
+
<div class={item}>{item}</div>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
<button onClick={() => items.push(`Item ${items.$length + 1}`)}>{"Add Item"}</button>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
render(App);
|
|
50
|
+
expect(container).toMatchSnapshot();
|
|
51
|
+
|
|
52
|
+
const button = container.querySelector('button');
|
|
53
|
+
button.click();
|
|
54
|
+
flushSync();
|
|
55
|
+
|
|
56
|
+
expect(container).toMatchSnapshot();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { mount, flushSync, ref } from 'ripple';
|
|
4
|
+
|
|
5
|
+
describe('ref', () => {
|
|
6
|
+
let container;
|
|
7
|
+
|
|
8
|
+
function render(component) {
|
|
9
|
+
mount(component, {
|
|
10
|
+
target: container
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
container = document.createElement('div');
|
|
16
|
+
document.body.appendChild(container);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
document.body.removeChild(container);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('creates a reactive ref with initial value', () => {
|
|
24
|
+
component TestRef() {
|
|
25
|
+
let $count = 5;
|
|
26
|
+
<div><span id='count'>{$count}</span></div>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
render(TestRef);
|
|
30
|
+
|
|
31
|
+
expect(container.querySelector('#count').textContent).toBe('5');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('updates when ref value changes', () => {
|
|
35
|
+
component TestRef() {
|
|
36
|
+
let $count = 0;
|
|
37
|
+
<div>
|
|
38
|
+
<span id='count'>{$count}</span>
|
|
39
|
+
<button id='btn' onClick={() => $count++}>{'Increment'}</button>
|
|
40
|
+
</div>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
render(TestRef);
|
|
44
|
+
|
|
45
|
+
expect(container.querySelector('#count').textContent).toBe('0');
|
|
46
|
+
|
|
47
|
+
container.querySelector('#btn').click();
|
|
48
|
+
flushSync();
|
|
49
|
+
|
|
50
|
+
expect(container.querySelector('#count').textContent).toBe('1');
|
|
51
|
+
});
|
|
52
|
+
});
|
package/types/index.d.ts
CHANGED
|
@@ -10,3 +10,9 @@ export declare function untrack<T>(fn: () => T): T;
|
|
|
10
10
|
export declare function flushSync<T>(fn: () => T): T;
|
|
11
11
|
|
|
12
12
|
export declare function effect(fn: (() => void) | (() => () => void)): void;
|
|
13
|
+
|
|
14
|
+
export interface Ref<T> {
|
|
15
|
+
$current: T;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export declare function ref<T>(value: T): Ref<T>;
|