bobe 0.0.26 → 0.0.27

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Store, Scope, Effect, SignalNode } from 'aoye';
1
+ import { Store, Scope, Signal, Computed, Effect, SignalNode } from 'aoye';
2
2
  export * from 'aoye';
3
3
  import { Queue } from 'bobe-shared';
4
4
 
@@ -52,7 +52,8 @@ declare class Tokenizer {
52
52
  * for arr ; item index; item.key
53
53
  * @returns {boolean} 是否含有 key
54
54
  */
55
- forLoopSubExp(): boolean;
55
+ jsExp(): Token;
56
+ peekChar(): string;
56
57
  private assignment;
57
58
  private pipe;
58
59
  private dynamic;
@@ -101,8 +102,12 @@ declare class Interpreter {
101
102
  * */
102
103
  declaration(ctx: ProgramCtx): any;
103
104
  forDeclaration(): ForNode | ForItemNode;
105
+ insertForItem(forNode: ForNode, i: number, parentData: any, newChildren: ForItemNode[], before: any, snapshotForUpdate: any): void;
106
+ removeForItem(children: ForItemNode[], i: number): void;
107
+ reuseForItem(child: ForItemNode, data: any, itemExp: string | ((value: any) => any), i: number, indexName?: string): void;
104
108
  forItemId: number;
105
109
  createForItem(forNode: ForNode, i: number, parentData: any): ForItemNode;
110
+ getItemData(forNode: ForNode, i: number, parentData: any): Record<any, any>;
106
111
  getData(): any;
107
112
  /**
108
113
  * key 元素,组件的 key
@@ -254,10 +259,12 @@ type ForNode = LogicNode & {
254
259
  indexName?: string;
255
260
  getKey?: (data: any) => any;
256
261
  arr: any[];
262
+ arrSignal: Signal<any[]> | Computed<any[]>;
257
263
  effect: Effect;
258
264
  i: number;
259
265
  owner: ComponentNode | FragmentNode;
260
266
  prevSibling: any;
267
+ vars: string[];
261
268
  };
262
269
  type ForItemNode = LogicNode & {
263
270
  id: number;
package/dist/index.umd.js CHANGED
@@ -158,6 +158,45 @@
158
158
  }
159
159
  }
160
160
 
161
+ function macInc(arr) {
162
+ const len = arr.length;
163
+ let candyLast = [],
164
+ i = 0;
165
+ while (i < len) {
166
+ const it = arr[i];
167
+ if (it !== -1) {
168
+ candyLast = [i];
169
+ break;
170
+ }
171
+ i++;
172
+ }
173
+ if (i + 1 >= len) return candyLast;
174
+ const toPrev = new Int32Array(len);
175
+ while (i < len) {
176
+ const target = arr[i];
177
+ if (target === -1) continue;
178
+ let start = -1,
179
+ end = candyLast.length;
180
+ while (start + 1 < end) {
181
+ const mid = start + end >> 1;
182
+ if (arr[candyLast[mid]] < target) {
183
+ start = mid;
184
+ } else {
185
+ end = mid;
186
+ }
187
+ }
188
+ candyLast[end] = i;
189
+ toPrev[i] = candyLast[start];
190
+ i++;
191
+ }
192
+ let length = candyLast.length;
193
+ for (let j = length - 1; j > 0; j--) {
194
+ const prev = toPrev[candyLast[j]];
195
+ candyLast[j - 1] = prev;
196
+ }
197
+ return candyLast;
198
+ }
199
+
161
200
  const _excluded = ["dentStack", "isFirstToken"];
162
201
  class Interpreter {
163
202
  constructor(tokenizer) {
@@ -210,6 +249,9 @@
210
249
  if (ctx.current.__logicType) {
211
250
  if (isLogicNode) {
212
251
  aoye.setPulling(ctx.current.effect);
252
+ if (ctx.current.__logicType & FakeType.ForItem) {
253
+ ctx.prevSibling = ctx.current.realBefore;
254
+ }
213
255
  }
214
256
  } else {
215
257
  if (ctx.current) {
@@ -260,8 +302,8 @@
260
302
  this.tokenizer.resume(snapshot);
261
303
  this.tokenizer.nextToken();
262
304
  this.tokenizer.nextToken();
263
- ctx.prevSibling = parent;
264
305
  ctx.current = forNode.children[++forNode.i];
306
+ ctx.prevSibling = ctx.current.realBefore;
265
307
  continue;
266
308
  }
267
309
  ctx.prevSibling = forNode.prevSibling;
@@ -365,21 +407,25 @@
365
407
  this.tokenizer.nextToken();
366
408
  const itemToken = this.tokenizer.nextToken();
367
409
  const isDestruct = itemToken.type === TokenType.InsertionExp;
368
- let itemExp = itemToken.value;
410
+ let itemExp = itemToken.value,
411
+ vars;
369
412
  if (isDestruct) {
370
413
  itemExp = '{' + itemExp + '}';
371
- const vars = itemExp.match(bobeShared.jsVarRegexp).join(',');
372
- itemExp = new Function('item', `let ${vars}; (${itemExp}=item); return {${vars}};`);
414
+ vars = itemExp.match(bobeShared.jsVarRegexp);
415
+ const varStr = vars.join(',');
416
+ itemExp = new Function(itemExp, `return {${varStr}};`);
373
417
  }
374
- let indexName, keyExp;
375
- while (this.tokenizer.code[this.tokenizer.i] !== '\n') {
376
- const next = this.tokenizer.nextToken();
377
- if (next.type !== TokenType.Semicolon) {
378
- if (!indexName) {
379
- indexName = next.value;
380
- } else {
381
- keyExp = next.value;
382
- }
418
+ let indexName,
419
+ keyExp,
420
+ char = this.tokenizer.peekChar();
421
+ if (char === ';') {
422
+ this.tokenizer.nextToken();
423
+ if (this.tokenizer.peekChar() !== '\n') keyExp = this.tokenizer.jsExp().value;
424
+ } else if (char === '\n') ; else {
425
+ indexName = this.tokenizer.nextToken().value;
426
+ if (this.tokenizer.peekChar() === ';') {
427
+ this.tokenizer.nextToken();
428
+ if (this.tokenizer.peekChar() !== '\n') keyExp = this.tokenizer.jsExp().value;
383
429
  }
384
430
  }
385
431
  const owner = this.ctx.stack.peekByType(NodeSort.TokenizerSwitcher)?.node;
@@ -392,12 +438,14 @@
392
438
  realBefore: prevSibling?.realAfter || prevSibling,
393
439
  realAfter: null,
394
440
  arr: null,
441
+ arrSignal: null,
395
442
  itemExp,
396
443
  indexName,
397
444
  getKey: null,
398
445
  children: [],
399
446
  effect: null,
400
447
  owner,
448
+ vars,
401
449
  i: 0
402
450
  };
403
451
  if (keyExp) {
@@ -408,6 +456,7 @@
408
456
  const cells = data[aoye.Keys.Meta].cells;
409
457
  const hasArrExpKey = Reflect.has(data[aoye.Keys.Raw], arrExp);
410
458
  const arrSignal = hasArrExpKey ? (data[arrExp], cells.get(arrExp)) : new aoye.Computed(this.getFn(data, arrExp));
459
+ forNode.arrSignal = arrSignal;
411
460
  forNode.realAfter = this.insertAfterAnchor('for-after');
412
461
  const _forNode$snapshot = forNode.snapshot;
413
462
  _forNode$snapshot.dentStack;
@@ -415,26 +464,23 @@
415
464
  const snapshotForUpdate = _objectWithoutProperties(_forNode$snapshot, _excluded);
416
465
  let isFirstRender = true;
417
466
  forNode.effect = new aoye.Effect(() => {
418
- let arr = forNode.arr = arrSignal.get();
467
+ let arr = arrSignal.get();
419
468
  arr[aoye.Keys.Iterator];
420
- arr = aoye.toRaw(arr);
469
+ const prevCtx = aoye.getPulling();
470
+ aoye.setPulling(null);
471
+ forNode.arr = arr = aoye.toRaw(arr);
421
472
  const children = forNode.children;
422
473
  if (isFirstRender) {
423
474
  const len = arr.length;
424
475
  for (let i = len; i--;) {
425
- const nextItem = children[i + 1];
426
476
  const item = this.createForItem(forNode, i, data);
427
- const anchor = this.insertAfterAnchor('for-item-after');
428
- item.realAfter = anchor;
429
- if (nextItem) {
430
- nextItem.realBefore = anchor;
431
- }
477
+ item.realAfter = this.insertAfterAnchor('for-item-after');
478
+ item.realBefore = this.insertAfterAnchor('for-item-before');
432
479
  item.realParent = forNode.realParent;
433
480
  children[i] = item;
434
481
  }
435
482
  const firstInsert = children[0];
436
483
  if (firstInsert) {
437
- firstInsert.realBefore = forNode.realBefore;
438
484
  this.tokenizer.nextToken();
439
485
  this.tokenizer.nextToken();
440
486
  } else {
@@ -448,65 +494,210 @@
448
494
  if (!forNode.getKey) {
449
495
  if (newLen < oldLen) {
450
496
  for (let i = oldLen - 1; i >= newLen; i--) {
451
- const child = children[i];
452
- this.removeLogicNode(child);
453
- this.remove(child.realAfter);
454
- child.effect.dispose();
497
+ this.removeForItem(children, i);
455
498
  }
456
499
  }
457
500
  if (oldLen < newLen) {
458
501
  const lastAfter = children.at(-1)?.realAfter || forNode.realBefore;
459
502
  for (let i = newLen - 1; i >= oldLen; i--) {
460
- const item = this.createForItem(forNode, i, data);
461
- newChildren[i] = item;
462
- const nextItem = newChildren[i + 1];
463
- const anchor = this.createAnchor('for-item-after');
464
- this.insertAfter(forNode.realParent, anchor, lastAfter);
465
- item.realAfter = anchor;
466
- if (nextItem) {
467
- nextItem.realBefore = anchor;
468
- }
469
- item.realParent = forNode.realParent;
470
- this.tokenizer = owner.tokenizer;
471
- this.tokenizer.resume(snapshotForUpdate);
472
- this.tokenizer.useDedentAsEof = false;
473
- aoye.runWithPulling(() => {
474
- this.program(forNode.realParent, forNode.owner, lastAfter, item);
475
- }, item.effect);
476
- }
477
- const firstInsert = newChildren[oldLen];
478
- if (firstInsert) {
479
- firstInsert.realBefore = lastAfter;
503
+ this.insertForItem(forNode, i, data, newChildren, lastAfter, snapshotForUpdate);
480
504
  }
481
505
  }
482
506
  for (let i = minLen; i--;) {
483
507
  const child = children[i];
484
508
  newChildren[i] = child;
485
- if (typeof itemExp === 'string') {
486
- child.data[itemExp] = arr[i];
509
+ this.reuseForItem(child, arr[i], itemExp, i, indexName);
510
+ }
511
+ } else {
512
+ let s = 0,
513
+ e1 = oldLen - 1,
514
+ e2 = newLen - 1;
515
+ while (s <= e1 && s <= e2) {
516
+ const child = children[s];
517
+ const old = child.key;
518
+ const itemData = this.getItemData(forNode, s, data);
519
+ const key = forNode.getKey(itemData);
520
+ if (old === key) {
521
+ newChildren[s] = child;
522
+ this.reuseForItem(child, arr[s], itemExp, s, indexName);
523
+ s++;
487
524
  } else {
488
- Object.assign(child.data, itemExp(arr[i]));
525
+ break;
526
+ }
527
+ }
528
+ while (s <= e1 && s <= e2) {
529
+ const child = children[e1];
530
+ const old = child.key;
531
+ const itemData = this.getItemData(forNode, e2, data);
532
+ const key = forNode.getKey(itemData);
533
+ if (old === key) {
534
+ newChildren[e2] = child;
535
+ this.reuseForItem(child, arr[e2], itemExp, e2, indexName);
536
+ e1--;
537
+ e2--;
538
+ } else {
539
+ break;
540
+ }
541
+ }
542
+ if (s > e1) {
543
+ if (s <= e2) {
544
+ const lastAfter = children.at(-1)?.realAfter || forNode.realBefore;
545
+ for (let i = e2; i >= s; i--) {
546
+ this.insertForItem(forNode, i, data, newChildren, lastAfter, snapshotForUpdate);
547
+ }
548
+ }
549
+ } else if (s > e2) {
550
+ if (s <= e1) {
551
+ for (let i = e1; i >= s; i--) {
552
+ this.removeForItem(children, i);
553
+ }
554
+ }
555
+ } else {
556
+ let s1 = s,
557
+ s2 = s;
558
+ const mixLen = e2 - s2 + 1;
559
+ const key2new = new Map();
560
+ for (let i = s2; i <= e2; i++) {
561
+ const itemData = this.getItemData(forNode, i, data);
562
+ const key = forNode.getKey(itemData);
563
+ key2new.set(key, i);
564
+ }
565
+ let maxIncNewI = -1;
566
+ let hasMove = false;
567
+ const new2oldI = new Array(mixLen).fill(-1);
568
+ for (let i = s1; i <= e1; i++) {
569
+ const key = children[i].key;
570
+ const newI = key2new.get(key);
571
+ if (newI == null) {
572
+ this.removeForItem(children, i);
573
+ continue;
574
+ }
575
+ const child = children[i];
576
+ newChildren[newI] = child;
577
+ this.reuseForItem(child, arr[newI], itemExp, newI, indexName);
578
+ new2oldI[newI - s2] = i;
579
+ key2new.delete(key);
580
+ if (newI < maxIncNewI) {
581
+ hasMove = true;
582
+ } else {
583
+ maxIncNewI = newI;
584
+ }
585
+ }
586
+ if (!hasMove) {
587
+ key2new.forEach((i, key) => {
588
+ const before = i === 0 ? forNode.realBefore : newChildren[i - 1].realAfter;
589
+ this.insertForItem(forNode, i, data, newChildren, before, snapshotForUpdate);
590
+ });
591
+ } else {
592
+ const incI = macInc(new2oldI),
593
+ incLen = incI.length;
594
+ let p1, p2;
595
+ for (p1 = s2, p2 = 0; p1 <= e2; p1++) {
596
+ const oldI = new2oldI[p1];
597
+ if (oldI === -1) {
598
+ const before = p1 === 0 ? forNode.realBefore : newChildren[p1 - 1].realAfter;
599
+ this.insertForItem(forNode, p1, data, newChildren, before, snapshotForUpdate);
600
+ continue;
601
+ }
602
+ const staticIdx = incI[p2] + s2;
603
+ if (p1 === staticIdx) {
604
+ p2 <= incLen && p2++;
605
+ continue;
606
+ }
607
+ let before = p1 === 0 ? forNode.realBefore : newChildren[p1 - 1].realAfter;
608
+ const child = newChildren[p1];
609
+ const realBefore = child.realBefore,
610
+ realAfter = child.realAfter,
611
+ realParent = child.realParent;
612
+ let point = realBefore,
613
+ next;
614
+ do {
615
+ next = this.nextSib(point);
616
+ this.insertAfter(realParent, point, before);
617
+ before = point;
618
+ if (point === realAfter) break;
619
+ point = next;
620
+ } while (true);
621
+ }
489
622
  }
490
623
  }
491
- forNode.children = newChildren;
492
624
  }
625
+ forNode.children = newChildren;
493
626
  }
494
627
  isFirstRender = false;
628
+ aoye.setPulling(prevCtx);
629
+ return isDestroy => {
630
+ if (isDestroy) {
631
+ for (let i = 0; i < forNode.children.length; i++) {
632
+ const item = forNode.children[i];
633
+ item.effect.dispose();
634
+ }
635
+ }
636
+ };
495
637
  });
496
638
  return forNode.children[0] || forNode;
497
639
  }
640
+ insertForItem(forNode, i, parentData, newChildren, before, snapshotForUpdate) {
641
+ const item = this.createForItem(forNode, i, parentData);
642
+ newChildren[i] = item;
643
+ let realAfter = this.createAnchor('for-item-after');
644
+ this.handleInsert(forNode.realParent, realAfter, before);
645
+ let realBefore = this.createAnchor('for-item-before');
646
+ this.handleInsert(forNode.realParent, realBefore, before);
647
+ item.realBefore = realBefore;
648
+ item.realAfter = realAfter;
649
+ this.tokenizer = forNode.owner.tokenizer;
650
+ this.tokenizer.resume(snapshotForUpdate);
651
+ this.tokenizer.useDedentAsEof = false;
652
+ aoye.runWithPulling(() => {
653
+ this.program(forNode.realParent, forNode.owner, realBefore, item);
654
+ }, item.effect);
655
+ }
656
+ removeForItem(children, i) {
657
+ const child = children[i];
658
+ this.removeLogicNode(child);
659
+ this.remove(child.realBefore);
660
+ this.remove(child.realAfter);
661
+ child.effect.dispose();
662
+ }
663
+ reuseForItem(child, data, itemExp, i, indexName) {
664
+ if (typeof itemExp === 'string') {
665
+ child.data[itemExp] = data;
666
+ if (indexName) {
667
+ child.data[indexName] = i;
668
+ }
669
+ }
670
+ }
498
671
  forItemId = 0;
499
672
  createForItem(forNode, i, parentData) {
500
673
  let forItemNode;
674
+ let data;
501
675
  const scope = new aoye.Scope(() => {});
502
676
  scope.scope = null;
503
677
  aoye.runWithPulling(() => {
504
678
  scope.get();
505
679
  }, null);
680
+ data = this.getItemData(forNode, i, parentData);
681
+ forItemNode = {
682
+ id: this.forItemId++,
683
+ __logicType: FakeType.ForItem,
684
+ realParent: null,
685
+ realBefore: null,
686
+ realAfter: null,
687
+ forNode,
688
+ key: forNode.getKey?.(data),
689
+ effect: null,
690
+ data
691
+ };
692
+ forItemNode.effect = scope;
693
+ return forItemNode;
694
+ }
695
+ getItemData(forNode, i, parentData) {
506
696
  const arr = forNode.arr,
507
697
  itemExp = forNode.itemExp,
508
698
  indexName = forNode.indexName,
509
- getKey = forNode.getKey;
699
+ vars = forNode.vars,
700
+ arrSignal = forNode.arrSignal;
510
701
  let data;
511
702
  if (typeof itemExp === 'string') {
512
703
  data = aoye.deepSignal(indexName ? {
@@ -516,26 +707,18 @@
516
707
  [itemExp]: arr[i]
517
708
  }, aoye.getPulling());
518
709
  } else {
519
- const rawData = itemExp(arr[i]);
520
- if (indexName) {
521
- rawData[indexName] = i;
710
+ data = aoye.deepSignal(indexName ? {
711
+ [indexName]: i
712
+ } : {}, aoye.getPulling());
713
+ const computedData = new aoye.Computed(() => itemExp(arrSignal.get()[i]));
714
+ const cells = data[aoye.Keys.Meta].cells;
715
+ for (let i = 0; i < vars.length; i++) {
716
+ const name = vars[i];
717
+ cells.set(name, new aoye.Computed(() => computedData.get()[name]));
522
718
  }
523
- data = aoye.deepSignal(rawData, aoye.getPulling());
524
719
  }
525
720
  Object.setPrototypeOf(data, parentData);
526
- forItemNode = {
527
- id: this.forItemId++,
528
- __logicType: FakeType.ForItem,
529
- realParent: null,
530
- realBefore: null,
531
- realAfter: null,
532
- forNode,
533
- key: getKey?.(data),
534
- effect: null,
535
- data
536
- };
537
- forItemNode.effect = scope;
538
- return forItemNode;
721
+ return data;
539
722
  }
540
723
  getData() {
541
724
  const _this$ctx$stack$peekB = this.ctx.stack.peekByType(NodeSort.CtxProvider),
@@ -1034,36 +1217,27 @@
1034
1217
  this.setToken(TokenType.Identifier, value || true);
1035
1218
  return this.token;
1036
1219
  }
1037
- forLoopSubExp() {
1220
+ jsExp() {
1038
1221
  this.token = null;
1039
1222
  let value = '';
1040
- let count = 0;
1041
1223
  while (1) {
1042
1224
  const char = this.code[this.i];
1043
- const isSemicolon = char === ';';
1044
- if (isSemicolon || char === '\n') {
1045
- value = value.trim();
1046
- if (!this.token) {
1047
- this.setToken(TokenType.Identifier, value);
1048
- } else {
1049
- this.waitingTokens.push({
1050
- type: TokenType.Identifier,
1051
- typeName: TokenType[TokenType.Identifier],
1052
- value
1053
- });
1054
- }
1055
- value = '';
1056
- count++;
1057
- if (count > 3) {
1058
- throw SyntaxError(`for 循环最多可包含三个表达式, 分别为 arr ; item index [; key]`);
1059
- }
1060
- if (!isSemicolon) return count === 3;
1225
+ if (char === ';' || char === '\n') {
1226
+ this.setToken(TokenType.Identifier, value.trim());
1227
+ return this.token;
1061
1228
  } else {
1062
1229
  value += char;
1063
1230
  }
1064
1231
  this.i++;
1065
1232
  }
1066
1233
  }
1234
+ peekChar() {
1235
+ let i = this.i;
1236
+ while (this.code[i] === ' ' || this.code[i] === '\t') {
1237
+ i++;
1238
+ }
1239
+ return this.code[i];
1240
+ }
1067
1241
  assignment() {
1068
1242
  this.setToken(TokenType.Assign, '=');
1069
1243
  }