ripple 0.2.99 → 0.2.101
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 +313 -65
- 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.101",
|
|
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';
|
|
@@ -83,6 +83,34 @@ function collection_to_array(collection) {
|
|
|
83
83
|
return array;
|
|
84
84
|
}
|
|
85
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
|
+
|
|
86
114
|
/**
|
|
87
115
|
* @template V
|
|
88
116
|
* @template K
|
|
@@ -93,10 +121,9 @@ function collection_to_array(collection) {
|
|
|
93
121
|
* @param {(item: V) => K} [get_key]
|
|
94
122
|
* @returns {void}
|
|
95
123
|
*/
|
|
96
|
-
export function
|
|
124
|
+
export function for_block_keyed(node, get_collection, render_fn, flags, get_key) {
|
|
97
125
|
var is_controlled = (flags & IS_CONTROLLED) !== 0;
|
|
98
126
|
var is_indexed = (flags & IS_INDEXED) !== 0;
|
|
99
|
-
var is_keyed = (flags & IS_KEYED) !== 0;
|
|
100
127
|
var anchor = /** @type {Element | Text} */ (node);
|
|
101
128
|
|
|
102
129
|
if (is_controlled) {
|
|
@@ -107,12 +134,17 @@ export function for_block(node, get_collection, render_fn, flags, get_key) {
|
|
|
107
134
|
var block = /** @type {Block} */ (active_block);
|
|
108
135
|
var collection = get_collection();
|
|
109
136
|
var array = collection_to_array(collection);
|
|
110
|
-
if (is_keyed) {
|
|
111
|
-
array = keyed(block, array, /** @type {(item: V) => K} */ (get_key));
|
|
112
|
-
}
|
|
113
137
|
|
|
114
138
|
untrack(() => {
|
|
115
|
-
|
|
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
|
+
);
|
|
116
148
|
});
|
|
117
149
|
}, FOR_BLOCK);
|
|
118
150
|
}
|
|
@@ -145,15 +177,17 @@ function update_index(block, index) {
|
|
|
145
177
|
|
|
146
178
|
/**
|
|
147
179
|
* @template V
|
|
180
|
+
* @template K
|
|
148
181
|
* @param {Element | Text} anchor
|
|
149
182
|
* @param {Block} block
|
|
150
183
|
* @param {V[]} b
|
|
151
184
|
* @param {(anchor: Node, value: V, index?: any) => Block} render_fn
|
|
152
185
|
* @param {boolean} is_controlled
|
|
153
186
|
* @param {boolean} is_indexed
|
|
187
|
+
* @param {(item: V) => K} get_key
|
|
154
188
|
* @returns {void}
|
|
155
189
|
*/
|
|
156
|
-
function
|
|
190
|
+
function reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed, get_key) {
|
|
157
191
|
var state = block.s;
|
|
158
192
|
|
|
159
193
|
// Variables used in conditional branches - declare with initial values
|
|
@@ -180,6 +214,275 @@ function reconcile(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
|
180
214
|
state = block.s = {
|
|
181
215
|
array: [],
|
|
182
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
|
+
}
|
|
271
|
+
|
|
272
|
+
a_val = a[a_end];
|
|
273
|
+
b_val = b[b_end];
|
|
274
|
+
a_key = a_keys[a_end];
|
|
275
|
+
b_key = b_keys[b_end];
|
|
276
|
+
|
|
277
|
+
while (a_key === b_key) {
|
|
278
|
+
a[a_end] = b_val;
|
|
279
|
+
b_block = b_blocks[b_end] = a_blocks[a_end];
|
|
280
|
+
if (is_indexed) {
|
|
281
|
+
update_index(b_block, b_end);
|
|
282
|
+
}
|
|
283
|
+
a_end--;
|
|
284
|
+
b_end--;
|
|
285
|
+
if (j > a_end || j > b_end) {
|
|
286
|
+
break outer;
|
|
287
|
+
}
|
|
288
|
+
a_val = a[a_end];
|
|
289
|
+
b_val = b[b_end];
|
|
290
|
+
a_key = a_keys[a_end];
|
|
291
|
+
b_key = b_keys[b_end];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
var fast_path_removal = false;
|
|
296
|
+
|
|
297
|
+
if (j > a_end) {
|
|
298
|
+
if (j <= b_end) {
|
|
299
|
+
while (j <= b_end) {
|
|
300
|
+
b_val = b[j];
|
|
301
|
+
var target = j >= a_length ? anchor : a_blocks[j].s.start;
|
|
302
|
+
b_blocks[j] = create_item(target, b_val, j, render_fn, is_indexed);
|
|
303
|
+
j++;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} else if (j > b_end) {
|
|
307
|
+
while (j <= a_end) {
|
|
308
|
+
destroy_block(a_blocks[j++]);
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
a_start = j;
|
|
312
|
+
b_start = j;
|
|
313
|
+
a_left = a_end - j + 1;
|
|
314
|
+
b_left = b_end - j + 1;
|
|
315
|
+
sources = new Int32Array(b_left + 1);
|
|
316
|
+
moved = false;
|
|
317
|
+
pos = 0;
|
|
318
|
+
patched = 0;
|
|
319
|
+
i = 0;
|
|
320
|
+
|
|
321
|
+
fast_path_removal = is_controlled && a_left === a_length;
|
|
322
|
+
|
|
323
|
+
// When sizes are small, just loop them through
|
|
324
|
+
if (b_length < 4 || (a_left | b_left) < 32) {
|
|
325
|
+
for (i = a_start; i <= a_end; ++i) {
|
|
326
|
+
a_val = a[i];
|
|
327
|
+
a_key = a_keys[i];
|
|
328
|
+
if (patched < b_left) {
|
|
329
|
+
for (j = b_start; j <= b_end; j++) {
|
|
330
|
+
b_val = b[j];
|
|
331
|
+
b_key = b_keys[j];
|
|
332
|
+
if (a_key === b_key) {
|
|
333
|
+
sources[j - b_start] = i + 1;
|
|
334
|
+
if (fast_path_removal) {
|
|
335
|
+
fast_path_removal = false;
|
|
336
|
+
while (a_start < i) {
|
|
337
|
+
destroy_block(a_blocks[a_start++]);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (pos > j) {
|
|
341
|
+
moved = true;
|
|
342
|
+
} else {
|
|
343
|
+
pos = j;
|
|
344
|
+
}
|
|
345
|
+
b_block = b_blocks[j] = a_blocks[i];
|
|
346
|
+
if (is_indexed) {
|
|
347
|
+
update_index(b_block, j);
|
|
348
|
+
}
|
|
349
|
+
++patched;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!fast_path_removal && j > b_end) {
|
|
354
|
+
destroy_block(a_blocks[i]);
|
|
355
|
+
}
|
|
356
|
+
} else if (!fast_path_removal) {
|
|
357
|
+
destroy_block(a_blocks[i]);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
var map = new Map();
|
|
362
|
+
|
|
363
|
+
for (i = b_start; i <= b_end; ++i) {
|
|
364
|
+
map.set(b_keys[i], i);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for (i = a_start; i <= a_end; ++i) {
|
|
368
|
+
a_val = a[i];
|
|
369
|
+
a_key = a_keys[i];
|
|
370
|
+
|
|
371
|
+
if (patched < b_left) {
|
|
372
|
+
j = map.get(a_key);
|
|
373
|
+
|
|
374
|
+
if (j !== undefined) {
|
|
375
|
+
if (fast_path_removal) {
|
|
376
|
+
fast_path_removal = false;
|
|
377
|
+
while (i > a_start) {
|
|
378
|
+
destroy_block(a[a_start++]);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
sources[j - b_start] = i + 1;
|
|
382
|
+
if (pos > j) {
|
|
383
|
+
moved = true;
|
|
384
|
+
} else {
|
|
385
|
+
pos = j;
|
|
386
|
+
}
|
|
387
|
+
block = b_blocks[j] = a_blocks[i];
|
|
388
|
+
if (is_indexed) {
|
|
389
|
+
update_index(block, j);
|
|
390
|
+
}
|
|
391
|
+
++patched;
|
|
392
|
+
} else if (!fast_path_removal) {
|
|
393
|
+
destroy_block(a_blocks[i]);
|
|
394
|
+
}
|
|
395
|
+
} else if (!fast_path_removal) {
|
|
396
|
+
destroy_block(a_blocks[i]);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (fast_path_removal) {
|
|
403
|
+
reconcile_fast_clear(anchor, block, []);
|
|
404
|
+
reconcile_by_key(anchor, block, b, render_fn, is_controlled, is_indexed, get_key);
|
|
405
|
+
return;
|
|
406
|
+
} else if (moved) {
|
|
407
|
+
var next_pos = 0;
|
|
408
|
+
var seq = lis_algorithm(sources);
|
|
409
|
+
j = seq.length - 1;
|
|
410
|
+
|
|
411
|
+
for (i = b_left - 1; i >= 0; i--) {
|
|
412
|
+
if (sources[i] === 0) {
|
|
413
|
+
pos = i + b_start;
|
|
414
|
+
b_val = b[pos];
|
|
415
|
+
next_pos = pos + 1;
|
|
416
|
+
|
|
417
|
+
var target = next_pos < b_length ? b_blocks[next_pos].s.start : anchor;
|
|
418
|
+
b_blocks[pos] = create_item(target, b_val, pos, render_fn, is_indexed);
|
|
419
|
+
} else if (j < 0 || i !== seq[j]) {
|
|
420
|
+
pos = i + b_start;
|
|
421
|
+
b_val = b[pos];
|
|
422
|
+
next_pos = pos + 1;
|
|
423
|
+
|
|
424
|
+
var target = next_pos < b_length ? b_blocks[next_pos].s.start : anchor;
|
|
425
|
+
move(b_blocks[pos], target);
|
|
426
|
+
} else {
|
|
427
|
+
j--;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
} else if (patched !== b_left) {
|
|
431
|
+
for (i = b_left - 1; i >= 0; i--) {
|
|
432
|
+
if (sources[i] === 0) {
|
|
433
|
+
pos = i + b_start;
|
|
434
|
+
b_val = b[pos];
|
|
435
|
+
next_pos = pos + 1;
|
|
436
|
+
|
|
437
|
+
var target = next_pos < b_length ? b_blocks[next_pos].s.start : anchor;
|
|
438
|
+
b_blocks[pos] = create_item(target, b_val, pos, render_fn, is_indexed);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
state.array = b;
|
|
444
|
+
state.blocks = b_blocks;
|
|
445
|
+
state.keys = b_keys;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @template V
|
|
450
|
+
* @param {Element | Text} anchor
|
|
451
|
+
* @param {Block} block
|
|
452
|
+
* @param {V[]} b
|
|
453
|
+
* @param {(anchor: Node, value: V, index?: any) => Block} render_fn
|
|
454
|
+
* @param {boolean} is_controlled
|
|
455
|
+
* @param {boolean} is_indexed
|
|
456
|
+
* @returns {void}
|
|
457
|
+
*/
|
|
458
|
+
function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
459
|
+
var state = block.s;
|
|
460
|
+
|
|
461
|
+
// Variables used in conditional branches - declare with initial values
|
|
462
|
+
/** @type {number} */
|
|
463
|
+
var a_start = 0;
|
|
464
|
+
/** @type {number} */
|
|
465
|
+
var b_start = 0;
|
|
466
|
+
/** @type {number} */
|
|
467
|
+
var a_left = 0;
|
|
468
|
+
/** @type {number} */
|
|
469
|
+
var b_left = 0;
|
|
470
|
+
/** @type {Int32Array} */
|
|
471
|
+
var sources = new Int32Array(0);
|
|
472
|
+
/** @type {boolean} */
|
|
473
|
+
var moved = false;
|
|
474
|
+
/** @type {number} */
|
|
475
|
+
var pos = 0;
|
|
476
|
+
/** @type {number} */
|
|
477
|
+
var patched = 0;
|
|
478
|
+
/** @type {number} */
|
|
479
|
+
var i = 0;
|
|
480
|
+
|
|
481
|
+
if (state === null) {
|
|
482
|
+
state = block.s = {
|
|
483
|
+
array: [],
|
|
484
|
+
blocks: [],
|
|
485
|
+
keys: null,
|
|
183
486
|
};
|
|
184
487
|
}
|
|
185
488
|
|
|
@@ -337,7 +640,6 @@ function reconcile(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
|
337
640
|
} else {
|
|
338
641
|
pos = j;
|
|
339
642
|
}
|
|
340
|
-
b_val = b[j];
|
|
341
643
|
block = b_blocks[j] = a_blocks[i];
|
|
342
644
|
if (is_indexed) {
|
|
343
645
|
update_index(block, j);
|
|
@@ -355,7 +657,7 @@ function reconcile(anchor, block, b, render_fn, is_controlled, is_indexed) {
|
|
|
355
657
|
|
|
356
658
|
if (fast_path_removal) {
|
|
357
659
|
reconcile_fast_clear(anchor, block, []);
|
|
358
|
-
|
|
660
|
+
reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed);
|
|
359
661
|
return;
|
|
360
662
|
} else if (moved) {
|
|
361
663
|
var next_pos = 0;
|
|
@@ -468,57 +770,3 @@ function lis_algorithm(arr) {
|
|
|
468
770
|
|
|
469
771
|
return seq;
|
|
470
772
|
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* @template V
|
|
474
|
-
* @template K
|
|
475
|
-
* @param {Block} block
|
|
476
|
-
* @param {V[] | Iterable<V>} collection
|
|
477
|
-
* @param {(item: V) => K} key_fn
|
|
478
|
-
* @returns {V[]}
|
|
479
|
-
*/
|
|
480
|
-
function keyed(block, collection, key_fn) {
|
|
481
|
-
var b_array = collection_to_array(collection);
|
|
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.slice();
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
var a_array = state.array;
|
|
498
|
-
var a_blocks = state.blocks;
|
|
499
|
-
var a_keys = a_array.map(key_fn);
|
|
500
|
-
var a = new Map();
|
|
501
|
-
|
|
502
|
-
for (var i = 0; i < a_keys.length; i++) {
|
|
503
|
-
a.set(a_keys[i], i);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (a.size !== a_keys.length) {
|
|
507
|
-
throw new Error('Duplicate keys are not allowed');
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
for (var i = 0; i < b_keys.length; i++) {
|
|
511
|
-
var b_val = b_keys[i];
|
|
512
|
-
// if the index is the key, skip
|
|
513
|
-
if (b_val === i) {
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
var index = a.get(b_val);
|
|
517
|
-
|
|
518
|
-
if (index !== undefined) {
|
|
519
|
-
b_array[i] = a_array[index];
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
return b_array;
|
|
524
|
-
}
|
|
@@ -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
|
});
|