ripple 0.2.6 → 0.2.8

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.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/errors.js +20 -22
  4. package/src/compiler/phases/1-parse/index.js +21 -19
  5. package/src/compiler/phases/1-parse/style.js +27 -27
  6. package/src/compiler/phases/2-analyze/index.js +25 -25
  7. package/src/compiler/phases/2-analyze/prune.js +64 -27
  8. package/src/compiler/phases/3-transform/index.js +150 -113
  9. package/src/compiler/phases/3-transform/segments.js +25 -20
  10. package/src/compiler/phases/3-transform/stylesheet.js +28 -28
  11. package/src/compiler/scope.js +3 -3
  12. package/src/compiler/utils.js +7 -9
  13. package/src/constants.js +1 -2
  14. package/src/jsx-runtime.d.ts +59 -59
  15. package/src/jsx-runtime.js +13 -13
  16. package/src/runtime/array.js +15 -15
  17. package/src/runtime/index.js +2 -0
  18. package/src/runtime/internal/client/blocks.js +16 -5
  19. package/src/runtime/internal/client/constants.js +1 -1
  20. package/src/runtime/internal/client/events.js +2 -2
  21. package/src/runtime/internal/client/for.js +6 -7
  22. package/src/runtime/internal/client/operations.js +1 -1
  23. package/src/runtime/internal/client/runtime.js +41 -20
  24. package/src/runtime/internal/client/template.js +1 -1
  25. package/src/runtime/internal/client/try.js +2 -2
  26. package/src/utils/ast.js +9 -9
  27. package/src/utils/builders.js +66 -28
  28. package/tests/__snapshots__/for.test.ripple.snap +81 -0
  29. package/tests/basic.test.ripple +292 -263
  30. package/tests/composite.test.ripple +151 -0
  31. package/tests/for.test.ripple +58 -0
  32. package/tests/ref.test.ripple +52 -0
  33. package/tests/use.test.ripple +24 -22
  34. package/types/index.d.ts +7 -1
@@ -0,0 +1,151 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+
3
+ import { mount, flushSync } from 'ripple';
4
+
5
+ describe('composite components', () => {
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('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
+
50
+ it('renders composite components with object state', () => {
51
+ component Button({ obj }) {
52
+ <button class='count2' onClick={() => {
53
+ obj.$count++;
54
+ }}>{obj.$count}</button>
55
+ }
56
+
57
+ component App() {
58
+ <div>
59
+ let obj = {
60
+ $count: 0
61
+ };
62
+ let $button = true;
63
+
64
+ <span class='count'>{obj.$count}</span>
65
+ <span>{' '}</span>
66
+ <Button obj={obj} />
67
+ </div>
68
+ }
69
+
70
+ render(App);
71
+
72
+ const button = container.querySelector('button');
73
+
74
+ button.click();
75
+ flushSync();
76
+
77
+ expect(container.querySelector('.count').textContent).toBe('1');
78
+ expect(container.querySelector('.count2').textContent).toBe('1');
79
+ });
80
+
81
+ it('renders composite components with object state', () => {
82
+ component Button({ obj }) {
83
+ <button class='count2' onClick={() => {
84
+ obj.$count++;
85
+ }}>{obj.$count}</button>
86
+ }
87
+
88
+ component App() {
89
+ <div>
90
+ let obj = {
91
+ $count: 0
92
+ };
93
+
94
+ <span class='count'>{obj.$count}</span>
95
+ <Button obj={obj} />
96
+ </div>
97
+ }
98
+
99
+ render(App);
100
+
101
+ const button = container.querySelector('button');
102
+
103
+ button.click();
104
+ flushSync();
105
+
106
+ expect(container.querySelector('.count').textContent).toBe('1');
107
+ expect(container.querySelector('.count2').textContent).toBe('1');
108
+ });
109
+
110
+ it('renders composite components with object state wrapped in an if statement', () => {
111
+ component Button({ obj }) {
112
+ <button class='count2' onClick={() => {
113
+ obj.$count++;
114
+ }}>{obj.$count}</button>
115
+ }
116
+
117
+ component OtherComponent({ obj }) {
118
+ <div class='count3'>{obj.$count}</div>
119
+ }
120
+
121
+ component App() {
122
+ <div>
123
+ let obj = {
124
+ $count: 0
125
+ };
126
+ let $button = true;
127
+
128
+ <span class='count'>{obj.$count}</span>
129
+ <span>{' '}</span>
130
+ if (obj) {
131
+ <Button obj={obj} />
132
+ }
133
+
134
+ if (obj) {
135
+ <OtherComponent obj={obj} />
136
+ }
137
+ </div>
138
+ }
139
+
140
+ render(App);
141
+
142
+ const button = container.querySelector('button');
143
+
144
+ button.click();
145
+ flushSync();
146
+
147
+ expect(container.querySelector('.count').textContent).toBe('1');
148
+ expect(container.querySelector('.count2').textContent).toBe('1');
149
+ expect(container.querySelector('.count3').textContent).toBe('1');
150
+ });
151
+ });
@@ -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
+ });
@@ -1,32 +1,34 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+
2
3
  import { mount, flushSync } from 'ripple';
3
4
 
4
5
  describe('@use element decorators', () => {
5
- let container;
6
-
7
- function render(component) {
8
- mount(component, { target: container });
9
- }
6
+ let container;
10
7
 
11
- beforeEach(() => {
12
- container = document.createElement('div');
13
- document.body.appendChild(container);
14
- });
8
+ function render(component) {
9
+ mount(component, {
10
+ target: container
11
+ });
12
+ }
15
13
 
16
- afterEach(() => {
17
- document.body.removeChild(container);
18
- container = null;
19
- });
14
+ beforeEach(() => {
15
+ container = document.createElement('div');
16
+ document.body.appendChild(container);
17
+ });
20
18
 
21
- it('capture a <div>', () => {
22
- let div;
23
-
24
- component Component() {
25
- <div {@use (node) => { div = node; }}>{"Hello World"}</div>
26
- }
19
+ afterEach(() => {
20
+ document.body.removeChild(container);
21
+ container = null;
22
+ });
27
23
 
28
- render(Component);
24
+ it('capture a <div>', () => {
25
+ let div;
29
26
 
30
- expect(div.textContent).toBe('Hello World');
31
- });
27
+ component Component() {
28
+ <div {@use (node) => { div = node; }}>{'Hello World'}</div>
29
+ }
30
+ render(Component);
31
+ flushSync();
32
+ expect(div.textContent).toBe('Hello World');
33
+ });
32
34
  });
package/types/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export type Component<T = Record<string, any>> = (props: T) => void;
2
2
 
3
3
  export declare function mount(
4
4
  component: () => void,
5
- options: { target: HTMLElement; props?: Record<string, any> }
5
+ options: { target: HTMLElement; props?: Record<string, any> },
6
6
  ): () => void;
7
7
 
8
8
  export declare function untrack<T>(fn: () => T): T;
@@ -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>;