ripple 0.2.88 → 0.2.90
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 +2 -1
- package/src/compiler/phases/1-parse/index.js +61 -7
- package/src/compiler/phases/3-transform/client/index.js +36 -6
- package/src/runtime/array.js +9 -242
- package/src/runtime/index-client.js +3 -1
- package/src/runtime/internal/client/blocks.js +15 -10
- package/src/runtime/internal/client/constants.js +2 -0
- package/src/runtime/internal/client/html.js +41 -0
- package/src/runtime/internal/client/index.js +5 -1
- package/src/runtime/internal/client/operations.js +0 -1
- package/src/runtime/internal/client/render.js +5 -5
- package/src/runtime/internal/client/runtime.js +2 -2
- package/src/runtime/internal/client/template.js +1 -1
- package/src/runtime/internal/client/utils.js +4 -0
- package/src/runtime/internal/server/index.js +5 -2
- package/src/runtime/object.js +29 -0
- package/src/runtime/proxy.js +341 -0
- package/src/utils/events.js +3 -3
- package/tests/client/__snapshots__/html.test.ripple.snap +40 -0
- package/tests/client/array.test.ripple +3 -3
- package/tests/client/basic.test.ripple +75 -0
- package/tests/client/head.test.ripple +196 -0
- package/tests/client/html.test.ripple +52 -0
- package/tests/client/object.test.ripple +183 -0
- package/types/index.d.ts +8 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Ripple is an elegant TypeScript UI framework",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.90",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"@jridgewell/sourcemap-codec": "^1.5.5",
|
|
67
67
|
"acorn": "^8.15.0",
|
|
68
68
|
"acorn-typescript": "^1.4.13",
|
|
69
|
+
"clsx": "^2.1.1",
|
|
69
70
|
"esrap": "^2.1.0",
|
|
70
71
|
"is-reference": "^3.0.3",
|
|
71
72
|
"magic-string": "^0.30.18",
|
|
@@ -80,13 +80,17 @@ function RipplePlugin(config) {
|
|
|
80
80
|
// Look ahead to see if this is followed by [ for tuple syntax
|
|
81
81
|
if (this.pos + 1 < this.input.length) {
|
|
82
82
|
const nextChar = this.input.charCodeAt(this.pos + 1);
|
|
83
|
-
if (nextChar === 91) {
|
|
84
|
-
// [ character
|
|
85
|
-
// This is a tuple literal #[
|
|
86
|
-
// Consume both # and [
|
|
83
|
+
if (nextChar === 91 || nextChar === 123) {
|
|
84
|
+
// [ or { character
|
|
85
|
+
// This is a tuple literal #[ or #{
|
|
86
|
+
// Consume both # and [ or {
|
|
87
87
|
++this.pos; // consume #
|
|
88
|
-
++this.pos; // consume [
|
|
89
|
-
|
|
88
|
+
++this.pos; // consume [ or {
|
|
89
|
+
if (nextChar === 123) {
|
|
90
|
+
return this.finishToken(tt.braceL, '#{');
|
|
91
|
+
} else {
|
|
92
|
+
return this.finishToken(tt.bracketL, '#[');
|
|
93
|
+
}
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
}
|
|
@@ -181,6 +185,8 @@ function RipplePlugin(config) {
|
|
|
181
185
|
// Check if this is a tuple literal starting with #[
|
|
182
186
|
if (this.type === tt.bracketL && this.value === '#[') {
|
|
183
187
|
return this.parseTrackedArrayExpression();
|
|
188
|
+
} else if (this.type === tt.braceL && this.value === '#{') {
|
|
189
|
+
return this.parseTrackedObjectExpression();
|
|
184
190
|
}
|
|
185
191
|
|
|
186
192
|
return super.parseExprAtom(refDestructuringErrors, forNew, forInit);
|
|
@@ -221,6 +227,38 @@ function RipplePlugin(config) {
|
|
|
221
227
|
return this.finishNode(node, 'TrackedArrayExpression');
|
|
222
228
|
}
|
|
223
229
|
|
|
230
|
+
parseTrackedObjectExpression() {
|
|
231
|
+
const node = this.startNode();
|
|
232
|
+
this.next(); // consume the '#{'
|
|
233
|
+
|
|
234
|
+
node.properties = [];
|
|
235
|
+
|
|
236
|
+
// Parse object properties similar to regular object parsing
|
|
237
|
+
let first = true;
|
|
238
|
+
while (!this.eat(tt.braceR)) {
|
|
239
|
+
if (!first) {
|
|
240
|
+
this.expect(tt.comma);
|
|
241
|
+
if (this.afterTrailingComma(tt.braceR)) break;
|
|
242
|
+
} else {
|
|
243
|
+
first = false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (this.type === tt.ellipsis) {
|
|
247
|
+
// Spread property
|
|
248
|
+
const prop = this.parseSpread();
|
|
249
|
+
node.properties.push(prop);
|
|
250
|
+
if (this.type === tt.comma && this.input.charCodeAt(this.pos) === 125) {
|
|
251
|
+
this.raise(this.pos, 'Trailing comma is not permitted after the rest element');
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
// Regular property
|
|
255
|
+
node.properties.push(this.parseProperty(false, {}));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return this.finishNode(node, 'TrackedObjectExpression');
|
|
260
|
+
}
|
|
261
|
+
|
|
224
262
|
parseExportDefaultDeclaration() {
|
|
225
263
|
// Check if this is "export default component"
|
|
226
264
|
if (this.value === 'component') {
|
|
@@ -417,10 +455,25 @@ function RipplePlugin(config) {
|
|
|
417
455
|
jsx_parseExpressionContainer() {
|
|
418
456
|
let node = this.startNode();
|
|
419
457
|
this.next();
|
|
458
|
+
let tracked = false;
|
|
459
|
+
|
|
460
|
+
if (this.value === 'html') {
|
|
461
|
+
node.html = true;
|
|
462
|
+
this.next();
|
|
463
|
+
if (this.type.label === '@') {
|
|
464
|
+
this.next(); // consume @
|
|
465
|
+
tracked = true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
420
468
|
|
|
421
469
|
node.expression =
|
|
422
470
|
this.type === tt.braceR ? this.jsx_parseEmptyExpression() : this.parseExpression();
|
|
423
471
|
this.expect(tt.braceR);
|
|
472
|
+
|
|
473
|
+
if (tracked && node.expression.type === 'Identifier') {
|
|
474
|
+
node.expression.tracked = true;
|
|
475
|
+
}
|
|
476
|
+
|
|
424
477
|
return this.finishNode(node, 'JSXExpressionContainer');
|
|
425
478
|
}
|
|
426
479
|
|
|
@@ -916,7 +969,8 @@ function RipplePlugin(config) {
|
|
|
916
969
|
|
|
917
970
|
if (this.type.label === '{') {
|
|
918
971
|
const node = this.jsx_parseExpressionContainer();
|
|
919
|
-
node.type = 'Text';
|
|
972
|
+
node.type = node.html ? 'Html' : 'Text';
|
|
973
|
+
delete node.html;
|
|
920
974
|
body.push(node);
|
|
921
975
|
} else if (this.type.label === '}') {
|
|
922
976
|
return;
|
|
@@ -133,7 +133,7 @@ function visit_title_element(node, context) {
|
|
|
133
133
|
),
|
|
134
134
|
);
|
|
135
135
|
} else {
|
|
136
|
-
|
|
136
|
+
context.state.init.push(b.stmt(b.assignment('=', b.id('_$_.document.title'), result)));
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
@@ -336,6 +336,25 @@ const visitors = {
|
|
|
336
336
|
);
|
|
337
337
|
},
|
|
338
338
|
|
|
339
|
+
TrackedObjectExpression(node, context) {
|
|
340
|
+
if (context.state.to_ts) {
|
|
341
|
+
if (!context.state.imports.has(`import { TrackedObject } from 'ripple'`)) {
|
|
342
|
+
context.state.imports.add(`import { TrackedObject } from 'ripple'`);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return b.new(
|
|
346
|
+
b.id('TrackedObject'),
|
|
347
|
+
b.object(node.properties.map((prop) => context.visit(prop))),
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return b.call(
|
|
352
|
+
'_$_.tracked_object',
|
|
353
|
+
b.object(node.properties.map((prop) => context.visit(prop))),
|
|
354
|
+
b.id('__block'),
|
|
355
|
+
);
|
|
356
|
+
},
|
|
357
|
+
|
|
339
358
|
MemberExpression(node, context) {
|
|
340
359
|
const parent = context.path.at(-1);
|
|
341
360
|
|
|
@@ -702,18 +721,19 @@ const visitors = {
|
|
|
702
721
|
const metadata = { tracking: false, await: false };
|
|
703
722
|
let expression = visit(class_attribute.value, { ...state, metadata });
|
|
704
723
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
724
|
+
const hash_arg =
|
|
725
|
+
node.metadata.scoped && state.component.css
|
|
726
|
+
? b.literal(state.component.css.hash)
|
|
727
|
+
: undefined;
|
|
708
728
|
const is_html = context.state.metadata.namespace === 'html' && node.id.name !== 'svg';
|
|
709
729
|
|
|
710
730
|
if (metadata.tracking) {
|
|
711
731
|
local_updates.push(
|
|
712
|
-
b.stmt(b.call('_$_.set_class', id, expression,
|
|
732
|
+
b.stmt(b.call('_$_.set_class', id, expression, hash_arg, b.literal(is_html))),
|
|
713
733
|
);
|
|
714
734
|
} else {
|
|
715
735
|
state.init.push(
|
|
716
|
-
b.stmt(b.call('_$_.set_class', id, expression,
|
|
736
|
+
b.stmt(b.call('_$_.set_class', id, expression, hash_arg, b.literal(is_html))),
|
|
717
737
|
);
|
|
718
738
|
}
|
|
719
739
|
}
|
|
@@ -1470,6 +1490,7 @@ function transform_children(children, context) {
|
|
|
1470
1490
|
node.type === 'IfStatement' ||
|
|
1471
1491
|
node.type === 'TryStatement' ||
|
|
1472
1492
|
node.type === 'ForOfStatement' ||
|
|
1493
|
+
node.type === 'Html' ||
|
|
1473
1494
|
(node.type === 'Element' &&
|
|
1474
1495
|
(node.id.type !== 'Identifier' || !is_element_dom_element(node))),
|
|
1475
1496
|
) ||
|
|
@@ -1564,6 +1585,14 @@ function transform_children(children, context) {
|
|
|
1564
1585
|
visit(node, { ...state, flush_node, namespace: state.namespace });
|
|
1565
1586
|
} else if (node.type === 'HeadElement') {
|
|
1566
1587
|
visit(node, { ...state, flush_node, namespace: state.namespace });
|
|
1588
|
+
} else if (node.type === 'Html') {
|
|
1589
|
+
const metadata = { tracking: false, await: false };
|
|
1590
|
+
const expression = visit(node.expression, { ...state, metadata });
|
|
1591
|
+
|
|
1592
|
+
context.state.template.push('<!>');
|
|
1593
|
+
|
|
1594
|
+
const id = flush_node();
|
|
1595
|
+
state.update.push(b.stmt(b.call('_$_.html', id, b.thunk(expression))));
|
|
1567
1596
|
} else if (node.type === 'Text') {
|
|
1568
1597
|
const metadata = { tracking: false, await: false };
|
|
1569
1598
|
const expression = visit(node.expression, { ...state, metadata });
|
|
@@ -1611,6 +1640,7 @@ function transform_children(children, context) {
|
|
|
1611
1640
|
for (const head_element of head_elements) {
|
|
1612
1641
|
visit_head_element(head_element, context);
|
|
1613
1642
|
}
|
|
1643
|
+
|
|
1614
1644
|
if (context.state.inside_head) {
|
|
1615
1645
|
const title_element = children.find(
|
|
1616
1646
|
(node) =>
|
package/src/runtime/array.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/** @import { Block } from '#client' */
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { get_descriptor } from './internal/client/utils.js';
|
|
2
|
+
import { safe_scope } from './internal/client/runtime.js';
|
|
3
|
+
import { array_proxy } from './proxy.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* @template T
|
|
@@ -15,8 +14,7 @@ export function TrackedArray(...elements) {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
var block = safe_scope();
|
|
18
|
-
|
|
19
|
-
return proxy({ elements, block });
|
|
17
|
+
return array_proxy({ elements, block });
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
/**
|
|
@@ -29,7 +27,7 @@ export function TrackedArray(...elements) {
|
|
|
29
27
|
TrackedArray.from = function (arrayLike, mapFn, thisArg) {
|
|
30
28
|
var block = safe_scope();
|
|
31
29
|
var elements = mapFn ? Array.from(arrayLike, mapFn, thisArg) : Array.from(arrayLike);
|
|
32
|
-
return
|
|
30
|
+
return array_proxy({ elements, block, from_static: true });
|
|
33
31
|
};
|
|
34
32
|
|
|
35
33
|
/**
|
|
@@ -40,7 +38,7 @@ TrackedArray.from = function (arrayLike, mapFn, thisArg) {
|
|
|
40
38
|
TrackedArray.of = function (...items) {
|
|
41
39
|
var block = safe_scope();
|
|
42
40
|
var elements = Array.of(...items);
|
|
43
|
-
return
|
|
41
|
+
return array_proxy({ elements, block, from_static: true });
|
|
44
42
|
};
|
|
45
43
|
|
|
46
44
|
/**
|
|
@@ -55,246 +53,15 @@ TrackedArray.fromAsync = async function (arrayLike, mapFn, thisArg) {
|
|
|
55
53
|
var elements = mapFn
|
|
56
54
|
? await Array.fromAsync(arrayLike, mapFn, thisArg)
|
|
57
55
|
: await Array.fromAsync(arrayLike);
|
|
58
|
-
return
|
|
56
|
+
return array_proxy({ elements, block, from_static: true });
|
|
59
57
|
};
|
|
60
58
|
|
|
61
59
|
/**
|
|
62
60
|
* @template T
|
|
63
|
-
* @param {
|
|
64
|
-
* elements: Iterable<T>,
|
|
65
|
-
* block: Block,
|
|
66
|
-
* from_static?: boolean,
|
|
67
|
-
* use_array?: boolean
|
|
68
|
-
* }} params
|
|
69
|
-
* @returns {TrackedArray<T>}
|
|
70
|
-
*/
|
|
71
|
-
function proxy({ elements, block, from_static = false, use_array = false }) {
|
|
72
|
-
/** @type {T[]} */
|
|
73
|
-
var arr;
|
|
74
|
-
var first;
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
from_static &&
|
|
78
|
-
(first = get_first_if_length(/** @type {Array<T>} */ (elements))) !== undefined
|
|
79
|
-
) {
|
|
80
|
-
arr = new Array();
|
|
81
|
-
arr[0] = /** @type {T} */ (/** @type {unknown} */ (first));
|
|
82
|
-
} else if (use_array) {
|
|
83
|
-
arr = /** @type {T[]} */ (elements);
|
|
84
|
-
} else {
|
|
85
|
-
arr = new Array(...elements);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
var tracked_elements = new Map();
|
|
89
|
-
var tracked_len = tracked(arr.length, block);
|
|
90
|
-
tracked_elements.set('length', tracked_len);
|
|
91
|
-
|
|
92
|
-
return new Proxy(arr, {
|
|
93
|
-
get(target, prop, receiver) {
|
|
94
|
-
var t = tracked_elements.get(prop);
|
|
95
|
-
var exists = prop in target;
|
|
96
|
-
|
|
97
|
-
if (t === undefined && (!exists || get_descriptor(target, prop)?.writable)) {
|
|
98
|
-
t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
|
|
99
|
-
tracked_elements.set(prop, t);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (t !== undefined) {
|
|
103
|
-
var v = get(t);
|
|
104
|
-
return v === UNINITIALIZED ? undefined : v;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
var result = Reflect.get(target, prop, receiver);
|
|
108
|
-
|
|
109
|
-
if (typeof result === 'function') {
|
|
110
|
-
if (methods_returning_arrays.has(/** @type {string} */ (prop))) {
|
|
111
|
-
/** @type {(this: any, ...args: any[]) => any} */
|
|
112
|
-
return function (...args) {
|
|
113
|
-
var output = Reflect.apply(result, receiver, args);
|
|
114
|
-
|
|
115
|
-
if (Array.isArray(output) && output !== target) {
|
|
116
|
-
return proxy({ elements: output, block, use_array: true });
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return output;
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// When generating an iterator, we need to ensure that length is tracked
|
|
124
|
-
if (prop === 'entries' || prop === 'values' || prop === 'keys') {
|
|
125
|
-
receiver.length;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return result;
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
set(target, prop, value, receiver) {
|
|
133
|
-
var t = tracked_elements.get(prop);
|
|
134
|
-
var exists = prop in target;
|
|
135
|
-
|
|
136
|
-
if (prop === 'length') {
|
|
137
|
-
for (var i = value; i < tracked_len.v; i += 1) {
|
|
138
|
-
var other_t = tracked_elements.get(i + '');
|
|
139
|
-
if (other_t !== undefined) {
|
|
140
|
-
set(other_t, UNINITIALIZED, block);
|
|
141
|
-
} else if (i in target) {
|
|
142
|
-
// If the item exists in the original, we need to create a uninitialized tracked,
|
|
143
|
-
// else a later read of the property would result in a tracked being created with
|
|
144
|
-
// the value of the original item at that index.
|
|
145
|
-
other_t = tracked(UNINITIALIZED, block);
|
|
146
|
-
tracked_elements.set(i + '', other_t);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// If we haven't yet created a tracked for this property, we need to ensure
|
|
152
|
-
// we do so otherwise if we read it later, then the write won't be tracked and
|
|
153
|
-
// the heuristics of effects will be different vs if we had read the proxied
|
|
154
|
-
// object property before writing to that property.
|
|
155
|
-
if (t === undefined) {
|
|
156
|
-
if (!exists || get_descriptor(target, prop)?.writable) {
|
|
157
|
-
t = tracked(undefined, block);
|
|
158
|
-
set(t, value, block);
|
|
159
|
-
|
|
160
|
-
tracked_elements.set(prop, t);
|
|
161
|
-
}
|
|
162
|
-
} else {
|
|
163
|
-
exists = t.v !== UNINITIALIZED;
|
|
164
|
-
|
|
165
|
-
set(t, value, block);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
var result = Reflect.set(target, prop, value, receiver);
|
|
169
|
-
|
|
170
|
-
if (!exists) {
|
|
171
|
-
// If we have mutated an array directly, we might need to
|
|
172
|
-
// signal that length has also changed. Do it before updating metadata
|
|
173
|
-
// to ensure that iterating over the array as a result of a metadata update
|
|
174
|
-
// will not cause the length to be out of sync.
|
|
175
|
-
if (typeof prop === 'string') {
|
|
176
|
-
var n = Number(prop);
|
|
177
|
-
|
|
178
|
-
if (Number.isInteger(n) && n >= tracked_len.v) {
|
|
179
|
-
set(tracked_len, n + 1, block);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return result;
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
setPrototypeOf() {
|
|
188
|
-
throw new Error(`Cannot set prototype of \`TrackedArray\``);
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
deleteProperty(target, prop) {
|
|
192
|
-
var t = tracked_elements.get(prop);
|
|
193
|
-
|
|
194
|
-
if (t === undefined) {
|
|
195
|
-
if (prop in target) {
|
|
196
|
-
const t = tracked(UNINITIALIZED, block);
|
|
197
|
-
tracked_elements.set(prop, t);
|
|
198
|
-
}
|
|
199
|
-
} else {
|
|
200
|
-
set(t, UNINITIALIZED, block);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return Reflect.deleteProperty(target, prop);
|
|
204
|
-
},
|
|
205
|
-
|
|
206
|
-
has(target, prop) {
|
|
207
|
-
if (prop === TRACKED_ARRAY) {
|
|
208
|
-
return true;
|
|
209
|
-
}
|
|
210
|
-
var t = tracked_elements.get(prop);
|
|
211
|
-
var exists = (t !== undefined && t.v !== UNINITIALIZED) || Reflect.has(target, prop);
|
|
212
|
-
|
|
213
|
-
if (t !== undefined || !exists || get_descriptor(target, prop)?.writable) {
|
|
214
|
-
if (t === undefined) {
|
|
215
|
-
t = tracked(exists ? /** @type {any} */ (target)[prop] : UNINITIALIZED, block);
|
|
216
|
-
|
|
217
|
-
tracked_elements.set(prop, t);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
var value = get(t);
|
|
221
|
-
if (value === UNINITIALIZED) {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return exists;
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
defineProperty(_, prop, descriptor) {
|
|
230
|
-
if (
|
|
231
|
-
!('value' in descriptor) ||
|
|
232
|
-
descriptor.configurable === false ||
|
|
233
|
-
descriptor.enumerable === false ||
|
|
234
|
-
descriptor.writable === false
|
|
235
|
-
) {
|
|
236
|
-
// we disallow non-basic descriptors, because unless they are applied to the
|
|
237
|
-
// target object — which we avoid, so that state can be forked — we will run
|
|
238
|
-
// afoul of the various invariants
|
|
239
|
-
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/getOwnPropertyDescriptor#invariants
|
|
240
|
-
throw new Error(
|
|
241
|
-
'Only basic property descriptors are supported with value and configurable, enumerable, and writable set to true',
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
var t = tracked_elements.get(prop);
|
|
246
|
-
|
|
247
|
-
if (t === undefined) {
|
|
248
|
-
t = tracked(descriptor.value, block);
|
|
249
|
-
tracked_elements.set(prop, t);
|
|
250
|
-
} else {
|
|
251
|
-
set(t, descriptor.value, block);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return true;
|
|
255
|
-
},
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* @template T
|
|
261
|
-
* @param {Array<T>} array
|
|
262
|
-
* @returns {number | void}
|
|
263
|
-
*/
|
|
264
|
-
function get_first_if_length(array) {
|
|
265
|
-
var first = array[0];
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
array.length === 1 &&
|
|
269
|
-
0 in array &&
|
|
270
|
-
Number.isInteger(first) &&
|
|
271
|
-
/** @type {number} */ (first) >= 0 &&
|
|
272
|
-
/** @type {number} */ (first) <= MAX_ARRAY_LENGTH
|
|
273
|
-
) {
|
|
274
|
-
return /** @type {number} */ (first);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const methods_returning_arrays = new Set([
|
|
279
|
-
'concat',
|
|
280
|
-
'filter',
|
|
281
|
-
'flat',
|
|
282
|
-
'flatMap',
|
|
283
|
-
'map',
|
|
284
|
-
'slice',
|
|
285
|
-
'splice',
|
|
286
|
-
'toReversed',
|
|
287
|
-
'toSorted',
|
|
288
|
-
'toSpliced',
|
|
289
|
-
'with',
|
|
290
|
-
]);
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* @template T
|
|
294
|
-
* @param {Iterable<T>} elements
|
|
61
|
+
* @param {Array<T>} elements
|
|
295
62
|
* @param {Block} block
|
|
296
63
|
* @returns {TrackedArray<T>}
|
|
297
64
|
*/
|
|
298
65
|
export function tracked_array(elements, block) {
|
|
299
|
-
return
|
|
300
|
-
}
|
|
66
|
+
return array_proxy({ elements, block, from_static: true });
|
|
67
|
+
}
|
|
@@ -45,12 +45,14 @@ export { create_context as createContext } from './internal/client/context.js';
|
|
|
45
45
|
export {
|
|
46
46
|
flush_sync as flushSync,
|
|
47
47
|
track,
|
|
48
|
-
trackSplit,
|
|
48
|
+
track_split as trackSplit,
|
|
49
49
|
untrack,
|
|
50
50
|
} from './internal/client/runtime.js';
|
|
51
51
|
|
|
52
52
|
export { TrackedArray } from './array.js';
|
|
53
53
|
|
|
54
|
+
export { TrackedObject } from './object.js';
|
|
55
|
+
|
|
54
56
|
export { TrackedSet } from './set.js';
|
|
55
57
|
|
|
56
58
|
export { TrackedMap } from './map.js';
|
|
@@ -318,6 +318,20 @@ export function is_destroyed(target_block) {
|
|
|
318
318
|
return true;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
/**
|
|
322
|
+
* @param {Node | null} node
|
|
323
|
+
* @param {Node} end
|
|
324
|
+
*/
|
|
325
|
+
export function remove_block_dom(node, end) {
|
|
326
|
+
while (node !== null) {
|
|
327
|
+
/** @type {Node | null} */
|
|
328
|
+
var next = node === end ? null : next_sibling(node);
|
|
329
|
+
|
|
330
|
+
/** @type {Element | Text | Comment} */ (node).remove();
|
|
331
|
+
node = next;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
321
335
|
/**
|
|
322
336
|
* @param {Block} block
|
|
323
337
|
* @param {boolean} [remove_dom]
|
|
@@ -330,16 +344,7 @@ export function destroy_block(block, remove_dom = true) {
|
|
|
330
344
|
|
|
331
345
|
if ((remove_dom && (f & (BRANCH_BLOCK | ROOT_BLOCK)) !== 0) || (f & HEAD_BLOCK) !== 0) {
|
|
332
346
|
var s = block.s;
|
|
333
|
-
|
|
334
|
-
var end = s.end;
|
|
335
|
-
|
|
336
|
-
while (node !== null) {
|
|
337
|
-
var next = node === end ? null : next_sibling(node);
|
|
338
|
-
|
|
339
|
-
node.remove();
|
|
340
|
-
node = next;
|
|
341
|
-
}
|
|
342
|
-
|
|
347
|
+
remove_block_dom(s.start, s.end);
|
|
343
348
|
removed = true;
|
|
344
349
|
}
|
|
345
350
|
|
|
@@ -22,6 +22,8 @@ export var CONTROL_FLOW_BLOCK = FOR_BLOCK | IF_BLOCK | TRY_BLOCK | COMPOSITE_BLO
|
|
|
22
22
|
export var UNINITIALIZED = Symbol();
|
|
23
23
|
/** @type {unique symbol} */
|
|
24
24
|
export const TRACKED_ARRAY = Symbol();
|
|
25
|
+
/** @type {unique symbol} */
|
|
26
|
+
export const TRACKED_OBJECT = Symbol();
|
|
25
27
|
export var COMPUTED_PROPERTY = Symbol();
|
|
26
28
|
export var REF_PROP = 'ref';
|
|
27
29
|
/** @type {unique symbol} */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** @import { Block } from '#client' */
|
|
2
|
+
|
|
3
|
+
import { remove_block_dom, render } from './blocks.js';
|
|
4
|
+
import { first_child } from './operations.js';
|
|
5
|
+
import { active_block } from './runtime.js';
|
|
6
|
+
import { assign_nodes, create_fragment_from_html } from './template.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Renders dynamic HTML content into the DOM by inserting it before the anchor node.
|
|
10
|
+
* Manages the lifecycle of HTML blocks, removing old content and inserting new content.
|
|
11
|
+
*
|
|
12
|
+
* TODO handle SVG/MathML
|
|
13
|
+
*
|
|
14
|
+
* @param {ChildNode} node
|
|
15
|
+
* @param {() => string} get_html
|
|
16
|
+
* @returns {void}
|
|
17
|
+
*/
|
|
18
|
+
export function html(node, get_html) {
|
|
19
|
+
/** @type {ChildNode} */
|
|
20
|
+
var anchor = node;
|
|
21
|
+
/** @type {string} */
|
|
22
|
+
var html = '';
|
|
23
|
+
|
|
24
|
+
render(() => {
|
|
25
|
+
var block = /** @type {Block} */ (active_block);
|
|
26
|
+
html = get_html() + '';
|
|
27
|
+
|
|
28
|
+
if (block.s !== null && block.s.start !== null) {
|
|
29
|
+
remove_block_dom(block.s.start, /** @type {Node} */ (block.s.end));
|
|
30
|
+
block.s.start = block.s.end = null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (html === '') return;
|
|
34
|
+
/** @type {DocumentFragment} */
|
|
35
|
+
var node = create_fragment_from_html(html);
|
|
36
|
+
|
|
37
|
+
assign_nodes(/** @type {Node } */ (first_child(node)), /** @type {Node} */ (node.lastChild));
|
|
38
|
+
|
|
39
|
+
anchor.before(node);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -60,6 +60,10 @@ export { template, append } from './template.js';
|
|
|
60
60
|
|
|
61
61
|
export { tracked_array } from '../../array.js';
|
|
62
62
|
|
|
63
|
+
export { tracked_object } from '../../object.js';
|
|
64
|
+
|
|
63
65
|
export { head } from './head.js';
|
|
64
66
|
|
|
65
|
-
export { script } from './script.js';
|
|
67
|
+
export { script } from './script.js';
|
|
68
|
+
|
|
69
|
+
export { html } from './html.js';
|
|
@@ -14,7 +14,6 @@ export var is_firefox;
|
|
|
14
14
|
export function init_operations() {
|
|
15
15
|
var node_prototype = Node.prototype;
|
|
16
16
|
var element_prototype = Element.prototype;
|
|
17
|
-
var object_prototype = Object.prototype;
|
|
18
17
|
var event_target_prototype = EventTarget.prototype;
|
|
19
18
|
|
|
20
19
|
is_firefox = /Firefox/.test(navigator.userAgent);
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
is_event_attribute,
|
|
16
16
|
} from '../../../utils/events.js';
|
|
17
17
|
import { get } from './runtime.js';
|
|
18
|
+
import { clsx } from 'clsx';
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* @param {Text} text
|
|
@@ -131,18 +132,17 @@ export function set_attributes(element, attributes) {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
/**
|
|
134
|
-
* @
|
|
135
|
-
* @param {V} value
|
|
135
|
+
* @param {import('clsx').ClassValue} value
|
|
136
136
|
* @param {string} [hash]
|
|
137
|
-
* @returns {string
|
|
137
|
+
* @returns {string}
|
|
138
138
|
*/
|
|
139
139
|
function to_class(value, hash) {
|
|
140
|
-
return
|
|
140
|
+
return value == null ? hash ?? '' : clsx([value, hash]);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* @param {HTMLElement} dom
|
|
145
|
-
* @param {
|
|
145
|
+
* @param {import('clsx').ClassValue} value
|
|
146
146
|
* @param {string} [hash]
|
|
147
147
|
* @param {boolean} [is_html]
|
|
148
148
|
* @returns {void}
|
|
@@ -325,7 +325,7 @@ export function track(v, get, set, b) {
|
|
|
325
325
|
* @param {Block} b
|
|
326
326
|
* @returns {Tracked[]}
|
|
327
327
|
*/
|
|
328
|
-
export function
|
|
328
|
+
export function track_split(v, l, b) {
|
|
329
329
|
var is_tracked = is_tracked_object(v);
|
|
330
330
|
|
|
331
331
|
if (is_tracked || typeof v !== 'object' || v === null || is_array(v)) {
|
|
@@ -1089,7 +1089,7 @@ export function exclude_from_object(obj, exclude_keys) {
|
|
|
1089
1089
|
var keys = object_keys(obj);
|
|
1090
1090
|
/** @type {Record<string | symbol, unknown>} */
|
|
1091
1091
|
var new_obj = {};
|
|
1092
|
-
|
|
1092
|
+
|
|
1093
1093
|
for (const key of keys) {
|
|
1094
1094
|
if (!exclude_keys.includes(key)) {
|
|
1095
1095
|
new_obj[key] = obj[key];
|
|
@@ -35,7 +35,7 @@ export function assign_nodes(start, end) {
|
|
|
35
35
|
* @param {boolean} use_mathml_namespace - Whether to use MathML namespace.
|
|
36
36
|
* @returns {DocumentFragment}
|
|
37
37
|
*/
|
|
38
|
-
function create_fragment_from_html(html, use_svg_namespace = false, use_mathml_namespace = false) {
|
|
38
|
+
export function create_fragment_from_html(html, use_svg_namespace = false, use_mathml_namespace = false) {
|
|
39
39
|
if (use_svg_namespace) {
|
|
40
40
|
return from_namespace(html, 'svg');
|
|
41
41
|
}
|