ripple 0.2.100 → 0.2.102
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 +3 -3
- package/src/compiler/index.d.ts +66 -0
- package/src/compiler/phases/3-transform/client/index.js +2 -7
- package/src/compiler/types/index.d.ts +1 -5
- package/src/constants.js +0 -1
- package/src/runtime/internal/client/for.js +318 -67
- package/src/runtime/internal/client/index.js +1 -1
- package/tests/client/__snapshots__/for.test.ripple.snap +40 -0
- package/tests/client/for.test.ripple +35 -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.102",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/runtime/index-client.js",
|
|
9
9
|
"main": "src/runtime/index-client.js",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"default": "./src/server/index.js"
|
|
35
35
|
},
|
|
36
36
|
"./compiler": {
|
|
37
|
-
"types": "./
|
|
38
|
-
"require": "./compiler/index.js",
|
|
37
|
+
"types": "./src/compiler/index.d.ts",
|
|
38
|
+
"require": "./src/compiler/index.js",
|
|
39
39
|
"default": "./src/compiler/index.js"
|
|
40
40
|
},
|
|
41
41
|
"./validator": {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Program } from 'estree';
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Compiler API Exports
|
|
5
|
+
// ============================================================================
|
|
6
|
+
/**
|
|
7
|
+
* Result of compilation operation
|
|
8
|
+
*/
|
|
9
|
+
export interface CompileResult {
|
|
10
|
+
/** The transformed AST */
|
|
11
|
+
ast: Program;
|
|
12
|
+
/** The generated JavaScript code with source map */
|
|
13
|
+
js: {
|
|
14
|
+
code: string;
|
|
15
|
+
map: any;
|
|
16
|
+
};
|
|
17
|
+
/** The generated CSS */
|
|
18
|
+
css: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Result of Volar mappings compilation
|
|
23
|
+
*/
|
|
24
|
+
export interface VolarMappingsResult {
|
|
25
|
+
/** Array of code mappings for Volar integration */
|
|
26
|
+
[key: string]: any;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Compilation options
|
|
31
|
+
*/
|
|
32
|
+
export interface CompileOptions {
|
|
33
|
+
/** Compilation mode: 'client' or 'server' */
|
|
34
|
+
mode?: 'client' | 'server';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parse Ripple source code to ESTree AST
|
|
39
|
+
* @param source - The Ripple source code to parse
|
|
40
|
+
* @returns The parsed ESTree Program AST
|
|
41
|
+
*/
|
|
42
|
+
export function parse(source: string): Program;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Compile Ripple source code to JS/CSS output
|
|
46
|
+
* @param source - The Ripple source code to compile
|
|
47
|
+
* @param filename - The filename for source map generation
|
|
48
|
+
* @param options - Compilation options (mode: 'client' or 'server')
|
|
49
|
+
* @returns The compilation result with AST, JS, and CSS
|
|
50
|
+
*/
|
|
51
|
+
export function compile(
|
|
52
|
+
source: string,
|
|
53
|
+
filename: string,
|
|
54
|
+
options?: CompileOptions,
|
|
55
|
+
): CompileResult;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Compile Ripple source to Volar mappings for editor integration
|
|
59
|
+
* @param source - The Ripple source code
|
|
60
|
+
* @param filename - The filename for source map generation
|
|
61
|
+
* @returns Volar mappings object for editor integration
|
|
62
|
+
*/
|
|
63
|
+
export function compile_to_volar_mappings(
|
|
64
|
+
source: string,
|
|
65
|
+
filename: string,
|
|
66
|
+
): VolarMappingsResult;
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
TEMPLATE_FRAGMENT,
|
|
12
12
|
TEMPLATE_SVG_NAMESPACE,
|
|
13
13
|
TEMPLATE_MATHML_NAMESPACE,
|
|
14
|
-
IS_KEYED,
|
|
15
14
|
} from '../../../../constants.js';
|
|
16
15
|
import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js';
|
|
17
16
|
import {
|
|
@@ -1027,9 +1026,6 @@ const visitors = {
|
|
|
1027
1026
|
if (index != null) {
|
|
1028
1027
|
flags |= IS_INDEXED;
|
|
1029
1028
|
}
|
|
1030
|
-
if (key != null) {
|
|
1031
|
-
flags |= IS_KEYED;
|
|
1032
|
-
}
|
|
1033
1029
|
|
|
1034
1030
|
// do only if not controller
|
|
1035
1031
|
if (!is_controlled) {
|
|
@@ -1039,12 +1035,11 @@ const visitors = {
|
|
|
1039
1035
|
const id = context.state.flush_node(is_controlled);
|
|
1040
1036
|
const pattern = node.left.declarations[0].id;
|
|
1041
1037
|
const body_scope = context.state.scopes.get(node.body);
|
|
1042
|
-
const is_keyed = key && (flags & IS_KEYED) !== 0;
|
|
1043
1038
|
|
|
1044
1039
|
context.state.init.push(
|
|
1045
1040
|
b.stmt(
|
|
1046
1041
|
b.call(
|
|
1047
|
-
'_$_.for',
|
|
1042
|
+
key != null ? '_$_.for_keyed' : '_$_.for',
|
|
1048
1043
|
id,
|
|
1049
1044
|
b.thunk(context.visit(node.right)),
|
|
1050
1045
|
b.arrow(
|
|
@@ -1057,7 +1052,7 @@ const visitors = {
|
|
|
1057
1052
|
),
|
|
1058
1053
|
),
|
|
1059
1054
|
b.literal(flags),
|
|
1060
|
-
|
|
1055
|
+
key != null ? b.arrow(index ? [pattern, index] : [pattern], context.visit(key)) : undefined,
|
|
1061
1056
|
),
|
|
1062
1057
|
),
|
|
1063
1058
|
);
|
|
@@ -32,10 +32,6 @@ import type {
|
|
|
32
32
|
Pattern,
|
|
33
33
|
} from 'estree';
|
|
34
34
|
|
|
35
|
-
export interface CompileResult {
|
|
36
|
-
// TODO
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
/**
|
|
40
36
|
* Parse error information
|
|
41
37
|
*/
|
|
@@ -298,4 +294,4 @@ export interface DelegatedEventResult {
|
|
|
298
294
|
hoisted: boolean;
|
|
299
295
|
/** The hoisted function */
|
|
300
296
|
function?: FunctionExpression | FunctionDeclaration | ArrowFunctionExpression;
|
|
301
|
-
}
|
|
297
|
+
}
|
package/src/constants.js
CHANGED
|
@@ -2,7 +2,6 @@ export const TEMPLATE_FRAGMENT = 1;
|
|
|
2
2
|
export const TEMPLATE_USE_IMPORT_NODE = 1 << 1;
|
|
3
3
|
export const IS_CONTROLLED = 1 << 2;
|
|
4
4
|
export const IS_INDEXED = 1 << 3;
|
|
5
|
-
export const IS_KEYED = 1 << 4;
|
|
6
5
|
export const TEMPLATE_SVG_NAMESPACE = 1 << 5;
|
|
7
6
|
export const TEMPLATE_MATHML_NAMESPACE = 1 << 6;
|
|
8
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/** @import { Block
|
|
1
|
+
/** @import { Block } from '#client' */
|
|
2
2
|
|
|
3
|
-
import { IS_CONTROLLED, IS_INDEXED
|
|
3
|
+
import { IS_CONTROLLED, IS_INDEXED } from '../../../constants.js';
|
|
4
4
|
import { branch, destroy_block, destroy_block_children, render } from './blocks.js';
|
|
5
5
|
import { FOR_BLOCK, TRACKED_ARRAY } from './constants.js';
|
|
6
6
|
import { create_text, next_sibling } from './operations.js';
|
|
@@ -69,21 +69,48 @@ function move(block, anchor) {
|
|
|
69
69
|
/**
|
|
70
70
|
* @template V
|
|
71
71
|
* @param {V[] | Iterable<V>} collection
|
|
72
|
-
* @param {boolean} clone
|
|
73
72
|
* @returns {V[]}
|
|
74
73
|
*/
|
|
75
|
-
function collection_to_array(collection
|
|
74
|
+
function collection_to_array(collection) {
|
|
76
75
|
var array = is_array(collection) ? collection : collection == null ? [] : array_from(collection);
|
|
77
76
|
|
|
78
77
|
// If we are working with a tracked array, then we need to get a copy of
|
|
79
78
|
// the elements, as the array itself is proxied, and not useful in diffing
|
|
80
|
-
if (
|
|
79
|
+
if (TRACKED_ARRAY in array) {
|
|
81
80
|
array = array_from(array);
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
return array;
|
|
85
84
|
}
|
|
86
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @template V
|
|
88
|
+
* @param {Element} node
|
|
89
|
+
* @param {() => V[] | Iterable<V>} get_collection
|
|
90
|
+
* @param {(anchor: Node, value: V, index?: any) => Block} render_fn
|
|
91
|
+
* @param {number} flags
|
|
92
|
+
* @returns {void}
|
|
93
|
+
*/
|
|
94
|
+
export function for_block(node, get_collection, render_fn, flags) {
|
|
95
|
+
var is_controlled = (flags & IS_CONTROLLED) !== 0;
|
|
96
|
+
var is_indexed = (flags & IS_INDEXED) !== 0;
|
|
97
|
+
var anchor = /** @type {Element | Text} */ (node);
|
|
98
|
+
|
|
99
|
+
if (is_controlled) {
|
|
100
|
+
anchor = node.appendChild(create_text());
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
render(() => {
|
|
104
|
+
var block = /** @type {Block} */ (active_block);
|
|
105
|
+
var collection = get_collection();
|
|
106
|
+
var array = collection_to_array(collection);
|
|
107
|
+
|
|
108
|
+
untrack(() => {
|
|
109
|
+
reconcile_by_ref(anchor, block, array, render_fn, is_controlled, is_indexed);
|
|
110
|
+
});
|
|
111
|
+
}, FOR_BLOCK);
|
|
112
|
+
}
|
|
113
|
+
|
|
87
114
|
/**
|
|
88
115
|
* @template V
|
|
89
116
|
* @template K
|
|
@@ -94,10 +121,9 @@ function collection_to_array(collection, clone) {
|
|
|
94
121
|
* @param {(item: V) => K} [get_key]
|
|
95
122
|
* @returns {void}
|
|
96
123
|
*/
|
|
97
|
-
export function
|
|
124
|
+
export function for_block_keyed(node, get_collection, render_fn, flags, get_key) {
|
|
98
125
|
var is_controlled = (flags & IS_CONTROLLED) !== 0;
|
|
99
126
|
var is_indexed = (flags & IS_INDEXED) !== 0;
|
|
100
|
-
var is_keyed = (flags & IS_KEYED) !== 0;
|
|
101
127
|
var anchor = /** @type {Element | Text} */ (node);
|
|
102
128
|
|
|
103
129
|
if (is_controlled) {
|
|
@@ -107,13 +133,18 @@ export function for_block(node, get_collection, render_fn, flags, get_key) {
|
|
|
107
133
|
render(() => {
|
|
108
134
|
var block = /** @type {Block} */ (active_block);
|
|
109
135
|
var collection = get_collection();
|
|
110
|
-
var array = collection_to_array(collection
|
|
111
|
-
if (is_keyed) {
|
|
112
|
-
array = keyed(block, array, /** @type {(item: V) => K} */ (get_key));
|
|
113
|
-
}
|
|
136
|
+
var array = collection_to_array(collection);
|
|
114
137
|
|
|
115
138
|
untrack(() => {
|
|
116
|
-
|
|
139
|
+
reconcile_by_key(
|
|
140
|
+
anchor,
|
|
141
|
+
block,
|
|
142
|
+
array,
|
|
143
|
+
render_fn,
|
|
144
|
+
is_controlled,
|
|
145
|
+
is_indexed,
|
|
146
|
+
/** @type {(item: V) => K} */ (get_key),
|
|
147
|
+
);
|
|
117
148
|
});
|
|
118
149
|
}, FOR_BLOCK);
|
|
119
150
|
}
|
|
@@ -146,15 +177,17 @@ function update_index(block, index) {
|
|
|
146
177
|
|
|
147
178
|
/**
|
|
148
179
|
* @template V
|
|
180
|
+
* @template K
|
|
149
181
|
* @param {Element | Text} anchor
|
|
150
182
|
* @param {Block} block
|
|
151
183
|
* @param {V[]} b
|
|
152
184
|
* @param {(anchor: Node, value: V, index?: any) => Block} render_fn
|
|
153
185
|
* @param {boolean} is_controlled
|
|
154
186
|
* @param {boolean} is_indexed
|
|
187
|
+
* @param {(item: V) => K} get_key
|
|
155
188
|
* @returns {void}
|
|
156
189
|
*/
|
|
157
|
-
function
|
|
190
|
+
function reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed, get_key) {
|
|
158
191
|
var state = block.s;
|
|
159
192
|
|
|
160
193
|
// Variables used in conditional branches - declare with initial values
|
|
@@ -181,6 +214,277 @@ function reconcile(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
|
181
214
|
state = block.s = {
|
|
182
215
|
array: [],
|
|
183
216
|
blocks: [],
|
|
217
|
+
keys: null,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
var a = state.array;
|
|
222
|
+
var a_length = a.length;
|
|
223
|
+
var b_length = b.length;
|
|
224
|
+
var j = 0;
|
|
225
|
+
|
|
226
|
+
// Fast-path for clear
|
|
227
|
+
if (is_controlled && b_length === 0) {
|
|
228
|
+
if (a_length > 0) {
|
|
229
|
+
reconcile_fast_clear(anchor, block, b);
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
var b_blocks = Array(b_length);
|
|
234
|
+
var b_keys = b.map(get_key);
|
|
235
|
+
|
|
236
|
+
// Fast-path for create
|
|
237
|
+
if (a_length === 0) {
|
|
238
|
+
for (; j < b_length; j++) {
|
|
239
|
+
b_blocks[j] = create_item(anchor, b[j], j, render_fn, is_indexed);
|
|
240
|
+
}
|
|
241
|
+
state.array = b;
|
|
242
|
+
state.blocks = b_blocks;
|
|
243
|
+
state.keys = b_keys;
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
var a_blocks = state.blocks;
|
|
248
|
+
var a_keys = state.keys;
|
|
249
|
+
var a_val = a[j];
|
|
250
|
+
var b_val = b[j];
|
|
251
|
+
var a_key = a_keys[j];
|
|
252
|
+
var b_key = b_keys[j];
|
|
253
|
+
var a_end = a_length - 1;
|
|
254
|
+
var b_end = b_length - 1;
|
|
255
|
+
var b_block;
|
|
256
|
+
|
|
257
|
+
outer: {
|
|
258
|
+
while (a_key === b_key) {
|
|
259
|
+
a[j] = b_val;
|
|
260
|
+
b_block = b_blocks[j] = a_blocks[j];
|
|
261
|
+
if (is_indexed) {
|
|
262
|
+
update_index(b_block, j);
|
|
263
|
+
}
|
|
264
|
+
++j;
|
|
265
|
+
if (j > a_end || j > b_end) {
|
|
266
|
+
break outer;
|
|
267
|
+
}
|
|
268
|
+
a_val = a[j];
|
|
269
|
+
b_val = b[j];
|
|
270
|
+
a_key = a_keys[j];
|
|
271
|
+
b_key = b_keys[j];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
a_val = a[a_end];
|
|
275
|
+
b_val = b[b_end];
|
|
276
|
+
a_key = a_keys[a_end];
|
|
277
|
+
b_key = b_keys[b_end];
|
|
278
|
+
|
|
279
|
+
while (a_key === b_key) {
|
|
280
|
+
a[a_end] = b_val;
|
|
281
|
+
b_block = b_blocks[b_end] = a_blocks[a_end];
|
|
282
|
+
if (is_indexed) {
|
|
283
|
+
update_index(b_block, b_end);
|
|
284
|
+
}
|
|
285
|
+
a_end--;
|
|
286
|
+
b_end--;
|
|
287
|
+
if (j > a_end || j > b_end) {
|
|
288
|
+
break outer;
|
|
289
|
+
}
|
|
290
|
+
a_val = a[a_end];
|
|
291
|
+
b_val = b[b_end];
|
|
292
|
+
a_key = a_keys[a_end];
|
|
293
|
+
b_key = b_keys[b_end];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
var fast_path_removal = false;
|
|
298
|
+
|
|
299
|
+
if (j > a_end) {
|
|
300
|
+
if (j <= b_end) {
|
|
301
|
+
while (j <= b_end) {
|
|
302
|
+
b_val = b[j];
|
|
303
|
+
var target = j >= a_length ? anchor : a_blocks[j].s.start;
|
|
304
|
+
b_blocks[j] = create_item(target, b_val, j, render_fn, is_indexed);
|
|
305
|
+
j++;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} else if (j > b_end) {
|
|
309
|
+
while (j <= a_end) {
|
|
310
|
+
destroy_block(a_blocks[j++]);
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
a_start = j;
|
|
314
|
+
b_start = j;
|
|
315
|
+
a_left = a_end - j + 1;
|
|
316
|
+
b_left = b_end - j + 1;
|
|
317
|
+
sources = new Int32Array(b_left + 1);
|
|
318
|
+
moved = false;
|
|
319
|
+
pos = 0;
|
|
320
|
+
patched = 0;
|
|
321
|
+
i = 0;
|
|
322
|
+
|
|
323
|
+
fast_path_removal = is_controlled && a_left === a_length;
|
|
324
|
+
|
|
325
|
+
// When sizes are small, just loop them through
|
|
326
|
+
if (b_length < 4 || (a_left | b_left) < 32) {
|
|
327
|
+
for (i = a_start; i <= a_end; ++i) {
|
|
328
|
+
a_val = a[i];
|
|
329
|
+
a_key = a_keys[i];
|
|
330
|
+
if (patched < b_left) {
|
|
331
|
+
for (j = b_start; j <= b_end; j++) {
|
|
332
|
+
b_val = b[j];
|
|
333
|
+
b_key = b_keys[j];
|
|
334
|
+
if (a_key === b_key) {
|
|
335
|
+
sources[j - b_start] = i + 1;
|
|
336
|
+
if (fast_path_removal) {
|
|
337
|
+
fast_path_removal = false;
|
|
338
|
+
while (a_start < i) {
|
|
339
|
+
destroy_block(a_blocks[a_start++]);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (pos > j) {
|
|
343
|
+
moved = true;
|
|
344
|
+
} else {
|
|
345
|
+
pos = j;
|
|
346
|
+
}
|
|
347
|
+
b_block = b_blocks[j] = a_blocks[i];
|
|
348
|
+
if (is_indexed) {
|
|
349
|
+
update_index(b_block, j);
|
|
350
|
+
}
|
|
351
|
+
++patched;
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (!fast_path_removal && j > b_end) {
|
|
356
|
+
destroy_block(a_blocks[i]);
|
|
357
|
+
}
|
|
358
|
+
} else if (!fast_path_removal) {
|
|
359
|
+
destroy_block(a_blocks[i]);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
var map = new Map();
|
|
364
|
+
|
|
365
|
+
for (i = b_start; i <= b_end; ++i) {
|
|
366
|
+
map.set(b_keys[i], i);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
for (i = a_start; i <= a_end; ++i) {
|
|
370
|
+
a_val = a[i];
|
|
371
|
+
a_key = a_keys[i];
|
|
372
|
+
|
|
373
|
+
if (patched < b_left) {
|
|
374
|
+
j = map.get(a_key);
|
|
375
|
+
|
|
376
|
+
if (j !== undefined) {
|
|
377
|
+
if (fast_path_removal) {
|
|
378
|
+
fast_path_removal = false;
|
|
379
|
+
while (i > a_start) {
|
|
380
|
+
destroy_block(a[a_start++]);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
sources[j - b_start] = i + 1;
|
|
384
|
+
if (pos > j) {
|
|
385
|
+
moved = true;
|
|
386
|
+
} else {
|
|
387
|
+
pos = j;
|
|
388
|
+
}
|
|
389
|
+
block = b_blocks[j] = a_blocks[i];
|
|
390
|
+
if (is_indexed) {
|
|
391
|
+
update_index(block, j);
|
|
392
|
+
}
|
|
393
|
+
++patched;
|
|
394
|
+
} else if (!fast_path_removal) {
|
|
395
|
+
destroy_block(a_blocks[i]);
|
|
396
|
+
}
|
|
397
|
+
} else if (!fast_path_removal) {
|
|
398
|
+
destroy_block(a_blocks[i]);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (fast_path_removal) {
|
|
405
|
+
reconcile_fast_clear(anchor, block, []);
|
|
406
|
+
reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed, get_key);
|
|
407
|
+
return;
|
|
408
|
+
} else if (moved) {
|
|
409
|
+
var next_pos = 0;
|
|
410
|
+
var seq = lis_algorithm(sources);
|
|
411
|
+
j = seq.length - 1;
|
|
412
|
+
|
|
413
|
+
for (i = b_left - 1; i >= 0; i--) {
|
|
414
|
+
if (sources[i] === 0) {
|
|
415
|
+
pos = i + b_start;
|
|
416
|
+
b_val = b[pos];
|
|
417
|
+
next_pos = pos + 1;
|
|
418
|
+
|
|
419
|
+
var target = next_pos < b_length ? b_blocks[next_pos].s.start : anchor;
|
|
420
|
+
b_blocks[pos] = create_item(target, b_val, pos, render_fn, is_indexed);
|
|
421
|
+
} else if (j < 0 || i !== seq[j]) {
|
|
422
|
+
pos = i + b_start;
|
|
423
|
+
b_val = b[pos];
|
|
424
|
+
next_pos = pos + 1;
|
|
425
|
+
|
|
426
|
+
var target = next_pos < b_length ? b_blocks[next_pos].s.start : anchor;
|
|
427
|
+
move(b_blocks[pos], target);
|
|
428
|
+
} else {
|
|
429
|
+
j--;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
} else if (patched !== b_left) {
|
|
433
|
+
for (i = b_left - 1; i >= 0; i--) {
|
|
434
|
+
if (sources[i] === 0) {
|
|
435
|
+
pos = i + b_start;
|
|
436
|
+
b_val = b[pos];
|
|
437
|
+
next_pos = pos + 1;
|
|
438
|
+
|
|
439
|
+
var target = next_pos < b_length ? b_blocks[next_pos].s.start : anchor;
|
|
440
|
+
b_blocks[pos] = create_item(target, b_val, pos, render_fn, is_indexed);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
state.array = b;
|
|
446
|
+
state.blocks = b_blocks;
|
|
447
|
+
state.keys = b_keys;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* @template V
|
|
452
|
+
* @param {Element | Text} anchor
|
|
453
|
+
* @param {Block} block
|
|
454
|
+
* @param {V[]} b
|
|
455
|
+
* @param {(anchor: Node, value: V, index?: any) => Block} render_fn
|
|
456
|
+
* @param {boolean} is_controlled
|
|
457
|
+
* @param {boolean} is_indexed
|
|
458
|
+
* @returns {void}
|
|
459
|
+
*/
|
|
460
|
+
function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
461
|
+
var state = block.s;
|
|
462
|
+
|
|
463
|
+
// Variables used in conditional branches - declare with initial values
|
|
464
|
+
/** @type {number} */
|
|
465
|
+
var a_start = 0;
|
|
466
|
+
/** @type {number} */
|
|
467
|
+
var b_start = 0;
|
|
468
|
+
/** @type {number} */
|
|
469
|
+
var a_left = 0;
|
|
470
|
+
/** @type {number} */
|
|
471
|
+
var b_left = 0;
|
|
472
|
+
/** @type {Int32Array} */
|
|
473
|
+
var sources = new Int32Array(0);
|
|
474
|
+
/** @type {boolean} */
|
|
475
|
+
var moved = false;
|
|
476
|
+
/** @type {number} */
|
|
477
|
+
var pos = 0;
|
|
478
|
+
/** @type {number} */
|
|
479
|
+
var patched = 0;
|
|
480
|
+
/** @type {number} */
|
|
481
|
+
var i = 0;
|
|
482
|
+
|
|
483
|
+
if (state === null) {
|
|
484
|
+
state = block.s = {
|
|
485
|
+
array: [],
|
|
486
|
+
blocks: [],
|
|
487
|
+
keys: null,
|
|
184
488
|
};
|
|
185
489
|
}
|
|
186
490
|
|
|
@@ -338,7 +642,6 @@ function reconcile(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
|
338
642
|
} else {
|
|
339
643
|
pos = j;
|
|
340
644
|
}
|
|
341
|
-
b_val = b[j];
|
|
342
645
|
block = b_blocks[j] = a_blocks[i];
|
|
343
646
|
if (is_indexed) {
|
|
344
647
|
update_index(block, j);
|
|
@@ -356,7 +659,7 @@ function reconcile(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
|
356
659
|
|
|
357
660
|
if (fast_path_removal) {
|
|
358
661
|
reconcile_fast_clear(anchor, block, []);
|
|
359
|
-
|
|
662
|
+
reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed);
|
|
360
663
|
return;
|
|
361
664
|
} else if (moved) {
|
|
362
665
|
var next_pos = 0;
|
|
@@ -469,55 +772,3 @@ function lis_algorithm(arr) {
|
|
|
469
772
|
|
|
470
773
|
return seq;
|
|
471
774
|
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* @template V
|
|
475
|
-
* @template K
|
|
476
|
-
* @param {Block} block
|
|
477
|
-
* @param {V[]} b_array
|
|
478
|
-
* @param {(item: V) => K} key_fn
|
|
479
|
-
* @returns {V[]}
|
|
480
|
-
*/
|
|
481
|
-
function keyed(block, b_array, key_fn) {
|
|
482
|
-
var b_keys = b_array.map(key_fn);
|
|
483
|
-
|
|
484
|
-
// We only need to do this in DEV
|
|
485
|
-
var b = new Set(b_keys);
|
|
486
|
-
if (b.size !== b_keys.length) {
|
|
487
|
-
throw new Error('Duplicate keys are not allowed');
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
var state = block.s;
|
|
491
|
-
|
|
492
|
-
if (state === null) {
|
|
493
|
-
// Make a clone of it so we don't mutate the original thereafter
|
|
494
|
-
return b_array;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
var a_array = state.array;
|
|
498
|
-
var a_keys = a_array.map(key_fn);
|
|
499
|
-
var a = new Map();
|
|
500
|
-
|
|
501
|
-
for (var i = 0; i < a_keys.length; i++) {
|
|
502
|
-
a.set(a_keys[i], i);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
if (a.size !== a_keys.length) {
|
|
506
|
-
throw new Error('Duplicate keys are not allowed');
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
for (var i = 0; i < b_keys.length; i++) {
|
|
510
|
-
var b_val = b_keys[i];
|
|
511
|
-
// if the index is the key, skip
|
|
512
|
-
if (b_val === i) {
|
|
513
|
-
continue;
|
|
514
|
-
}
|
|
515
|
-
var index = a.get(b_val);
|
|
516
|
-
|
|
517
|
-
if (index !== undefined) {
|
|
518
|
-
b_array[i] = a_array[index];
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
return b_array;
|
|
523
|
-
}
|
|
@@ -198,6 +198,46 @@ exports[`for statements > correctly handles the index in a for...of loop 3`] = `
|
|
|
198
198
|
</div>
|
|
199
199
|
`;
|
|
200
200
|
|
|
201
|
+
exports[`for statements > handles reversing an array manually 1`] = `
|
|
202
|
+
<div>
|
|
203
|
+
<!---->
|
|
204
|
+
<div>
|
|
205
|
+
0:Item 1
|
|
206
|
+
</div>
|
|
207
|
+
<div>
|
|
208
|
+
1:Item 2
|
|
209
|
+
</div>
|
|
210
|
+
<div>
|
|
211
|
+
2:Item 3
|
|
212
|
+
</div>
|
|
213
|
+
<!---->
|
|
214
|
+
<button>
|
|
215
|
+
Reverse
|
|
216
|
+
</button>
|
|
217
|
+
|
|
218
|
+
</div>
|
|
219
|
+
`;
|
|
220
|
+
|
|
221
|
+
exports[`for statements > handles reversing an array manually 2`] = `
|
|
222
|
+
<div>
|
|
223
|
+
<!---->
|
|
224
|
+
<div>
|
|
225
|
+
0:Item 3
|
|
226
|
+
</div>
|
|
227
|
+
<div>
|
|
228
|
+
1:Item 2
|
|
229
|
+
</div>
|
|
230
|
+
<div>
|
|
231
|
+
2:Item 1
|
|
232
|
+
</div>
|
|
233
|
+
<!---->
|
|
234
|
+
<button>
|
|
235
|
+
Reverse
|
|
236
|
+
</button>
|
|
237
|
+
|
|
238
|
+
</div>
|
|
239
|
+
`;
|
|
240
|
+
|
|
201
241
|
exports[`for statements > render a simple dynamic array 1`] = `
|
|
202
242
|
<div>
|
|
203
243
|
<!---->
|
|
@@ -144,4 +144,39 @@ describe('for statements', () => {
|
|
|
144
144
|
|
|
145
145
|
expect(container).toMatchSnapshot();
|
|
146
146
|
});
|
|
147
|
+
|
|
148
|
+
it('handles reversing an array manually', () => {
|
|
149
|
+
component App() {
|
|
150
|
+
let items = track([
|
|
151
|
+
#{ id: 1, text: 'Item 1' },
|
|
152
|
+
#{ id: 2, text: 'Item 2' },
|
|
153
|
+
#{ id: 3, text: 'Item 3' },
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
for (let item of @items; index i; key item.id) {
|
|
157
|
+
<div>{i + ':' + item.text}</div>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
<button onClick={() => {
|
|
161
|
+
@items[0].id = 3;
|
|
162
|
+
@items[1].id = 2;
|
|
163
|
+
@items[2].id = 1;
|
|
164
|
+
|
|
165
|
+
@items = [
|
|
166
|
+
@items[0],
|
|
167
|
+
@items[1],
|
|
168
|
+
@items[2],
|
|
169
|
+
];
|
|
170
|
+
}}>{"Reverse"}</button>
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
render(App);
|
|
174
|
+
expect(container).toMatchSnapshot();
|
|
175
|
+
|
|
176
|
+
const button = container.querySelector('button');
|
|
177
|
+
button.click();
|
|
178
|
+
flushSync();
|
|
179
|
+
|
|
180
|
+
expect(container).toMatchSnapshot();
|
|
181
|
+
})
|
|
147
182
|
});
|