bobe 0.0.22 → 0.0.24

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/bobe.esm.js CHANGED
@@ -1,6 +1,6 @@
1
- import { getPulling, setPulling, Keys, $, effect, toRaw, scope, Store, deepSignal, shareSignal, runWithPulling } from 'aoye';
1
+ import { getPulling, setPulling, Keys, Computed, Effect, toRaw, Scope, runWithPulling, deepSignal, Store, shareSignal, effect } from 'aoye';
2
2
  export * from 'aoye';
3
- import { BaseEvent, jsVarRegexp, Queue, isNum, matchIdStart2 } from 'bobe-shared';
3
+ import { jsVarRegexp, Queue, isNum, matchIdStart2 } from 'bobe-shared';
4
4
 
5
5
  function _arrayLikeToArray(r, a) {
6
6
  (null == a || a > r.length) && (a = r.length);
@@ -71,54 +71,50 @@ function _unsupportedIterableToArray(r, a) {
71
71
  }
72
72
  }
73
73
 
74
- var TokenType = /* @__PURE__ */(TokenType2 => {
75
- TokenType2[TokenType2["NewLine"] = 1] = "NewLine";
76
- TokenType2[TokenType2["Indent"] = 2] = "Indent";
77
- TokenType2[TokenType2["Dedent"] = 4] = "Dedent";
78
- TokenType2[TokenType2["Identifier"] = 8] = "Identifier";
79
- TokenType2[TokenType2["Assign"] = 16] = "Assign";
80
- TokenType2[TokenType2["Pipe"] = 32] = "Pipe";
81
- TokenType2[TokenType2["Eof"] = 64] = "Eof";
82
- TokenType2[TokenType2["InsertionExp"] = 128] = "InsertionExp";
83
- TokenType2[TokenType2["Semicolon"] = 256] = "Semicolon";
84
- return TokenType2;
85
- })(TokenType || {});
86
- var FakeType = /* @__PURE__ */(FakeType2 => {
87
- FakeType2[FakeType2["If"] = 1] = "If";
88
- FakeType2[FakeType2["Fail"] = 2] = "Fail";
89
- FakeType2[FakeType2["Else"] = 4] = "Else";
90
- FakeType2[FakeType2["For"] = 8] = "For";
91
- FakeType2[FakeType2["Component"] = 16] = "Component";
92
- FakeType2[FakeType2["Fragment"] = 32] = "Fragment";
93
- FakeType2[FakeType2["ForItem"] = 64] = "ForItem";
94
- return FakeType2;
95
- })(FakeType || {});
96
- const CondBit = 1 /* If */ | 2 /* Fail */ | 4 /* Else */;
97
- const LogicalBit = 1 /* If */ | 2 /* Fail */ | 4 /* Else */ | 8 /* For */ | 64 /* ForItem */;
98
- const TokenizerSwitcherBit = 16 /* Component */ | 32 /* Fragment */;
99
- var NodeSort = /* @__PURE__ */(NodeSort2 => {
100
- NodeSort2[NodeSort2["Logic"] = 1] = "Logic";
101
- NodeSort2[NodeSort2["Real"] = 2] = "Real";
102
- NodeSort2[NodeSort2["Component"] = 4] = "Component";
103
- NodeSort2[NodeSort2["CtxProvider"] = 8] = "CtxProvider";
104
- NodeSort2[NodeSort2["TokenizerSwitcher"] = 16] = "TokenizerSwitcher";
105
- return NodeSort2;
106
- })(NodeSort || {});
74
+ let TokenType = function (TokenType) {
75
+ TokenType[TokenType["NewLine"] = 1] = "NewLine";
76
+ TokenType[TokenType["Indent"] = 2] = "Indent";
77
+ TokenType[TokenType["Dedent"] = 4] = "Dedent";
78
+ TokenType[TokenType["Identifier"] = 8] = "Identifier";
79
+ TokenType[TokenType["Assign"] = 16] = "Assign";
80
+ TokenType[TokenType["Pipe"] = 32] = "Pipe";
81
+ TokenType[TokenType["Eof"] = 64] = "Eof";
82
+ TokenType[TokenType["InsertionExp"] = 128] = "InsertionExp";
83
+ TokenType[TokenType["Semicolon"] = 256] = "Semicolon";
84
+ return TokenType;
85
+ }({});
86
+ let FakeType = function (FakeType) {
87
+ FakeType[FakeType["If"] = 1] = "If";
88
+ FakeType[FakeType["Fail"] = 2] = "Fail";
89
+ FakeType[FakeType["Else"] = 4] = "Else";
90
+ FakeType[FakeType["For"] = 8] = "For";
91
+ FakeType[FakeType["Component"] = 16] = "Component";
92
+ FakeType[FakeType["Fragment"] = 32] = "Fragment";
93
+ FakeType[FakeType["ForItem"] = 64] = "ForItem";
94
+ return FakeType;
95
+ }({});
96
+ const CondBit = FakeType.If | FakeType.Fail | FakeType.Else;
97
+ const LogicalBit = FakeType.If | FakeType.Fail | FakeType.Else | FakeType.For | FakeType.ForItem;
98
+ FakeType.If | FakeType.Fail | FakeType.Else | FakeType.For | FakeType.ForItem | FakeType.Component | FakeType.Fragment;
99
+ const TokenizerSwitcherBit = FakeType.Component | FakeType.Fragment;
100
+ let NodeSort = function (NodeSort) {
101
+ NodeSort[NodeSort["Logic"] = 1] = "Logic";
102
+ NodeSort[NodeSort["Real"] = 2] = "Real";
103
+ NodeSort[NodeSort["Component"] = 4] = "Component";
104
+ NodeSort[NodeSort["CtxProvider"] = 8] = "CtxProvider";
105
+ NodeSort[NodeSort["TokenizerSwitcher"] = 16] = "TokenizerSwitcher";
106
+ return NodeSort;
107
+ }({});
108
+ (function (TerpEvt) {
109
+ TerpEvt["AllAttrGot"] = "all-attr-got";
110
+ TerpEvt["HandledComponentNode"] = "handled-component-node";
111
+ return TerpEvt;
112
+ })({});
107
113
 
108
114
  class MultiTypeStack {
109
- constructor() {
110
- // 记录全局栈顶
111
- // private top: StackNode<T> | null = null;
112
- // 记录每个类别的当前最新节点(各分类的“栈顶”)
113
- this.typeTops = {};
114
- this.length = 0;
115
- this.stack = [];
116
- }
117
- /**
118
- * 入栈操作
119
- * @param value 数据
120
- * @param bits 该节点所属的类别数组
121
- */
115
+ typeTops = {};
116
+ length = 0;
117
+ stack = [];
122
118
  push(value, bits) {
123
119
  const newNode = {
124
120
  value,
@@ -130,18 +126,15 @@ class MultiTypeStack {
130
126
  bit = bits & ~bits + 1;
131
127
  if (!bit) break;
132
128
  bits &= ~bit;
133
- newNode.prevByType[bit] = this.typeTops[bit] || void 0;
129
+ newNode.prevByType[bit] = this.typeTops[bit] || undefined;
134
130
  this.typeTops[bit] = newNode;
135
131
  }
136
132
  this.stack[this.length++] = newNode;
137
133
  }
138
- /**
139
- * 出栈操作
140
- */
141
134
  pop() {
142
135
  const poppedNode = this.stack[this.length - 1];
143
136
  this.stack[--this.length] = null;
144
- if (!poppedNode) return void 0;
137
+ if (!poppedNode) return undefined;
145
138
  let bits = poppedNode.types;
146
139
  let bit;
147
140
  while (1) {
@@ -152,63 +145,26 @@ class MultiTypeStack {
152
145
  }
153
146
  return [poppedNode.value, poppedNode.types];
154
147
  }
155
- /**
156
- * 获取某个类别的当前“顶部”元素
157
- */
158
148
  peekByType(cat) {
159
149
  return this.typeTops[cat]?.value;
160
150
  }
161
151
  peekType() {
162
152
  return this.stack.at(-1).types;
163
153
  }
164
- /**
165
- * 获取全局栈顶
166
- */
167
154
  peek() {
168
155
  return this.stack.at(-1).value;
169
156
  }
170
- // /**
171
- // * 1. 全局向前遍历 (不分类)
172
- // * 从栈顶开始,沿着全局链条向栈底遍历
173
- // */
174
- // forEach(callback: (value: T, types: number) => any): void {
175
- // let current = this.top;
176
- // while (current !== null) {
177
- // // 执行回调,如果返回 false 则立即停止
178
- // const shouldBreak = callback(current.value, current.types);
179
- // if (shouldBreak) break;
180
- // current = current.prevGlobal;
181
- // }
182
- // }
183
- // /**
184
- // * 2. 按类别向前遍历
185
- // * 仅遍历属于指定类别 cat 的节点
186
- // */
187
- // forEachByType(cat: number, callback: (value: T) => any): void {
188
- // // 从该类别的当前“顶端”节点开始
189
- // let current = this.typeTops[cat];
190
- // while (current) {
191
- // const shouldBreak = callback(current.value);
192
- // if (shouldBreak) break;
193
- // // 关键点:直接跳向该节点记录的“上一个同类节点”
194
- // // 这比遍历全局栈再筛选类别要快得多 (O(m) vs O(n))
195
- // current = current.prevByType[cat];
196
- // }
197
- // }
198
157
  }
199
158
 
200
159
  const _excluded = ["dentStack", "isFirstToken"];
201
- new BaseEvent();
202
160
  class Interpreter {
203
161
  constructor(tokenizer) {
204
162
  this.tokenizer = tokenizer;
205
- this.rootComponent = null;
206
- this.forItemId = 0;
207
- this.oneRealPropParsed = this.onePropParsed.bind(this);
208
163
  }
209
164
  isLogicNode(node) {
210
165
  return node && node.__logicType & LogicalBit;
211
166
  }
167
+ rootComponent = null;
212
168
  program(root, componentNode, before, ctxProvider) {
213
169
  this.rootComponent = componentNode;
214
170
  this.tokenizer.nextToken();
@@ -251,7 +207,7 @@ class Interpreter {
251
207
  }, !ctx.current.__logicType ? NodeSort.Real : (ctx.current.__logicType & LogicalBit ? NodeSort.Logic : 0) | (ctx.current.__logicType & TokenizerSwitcherBit ? NodeSort.TokenizerSwitcher : 0) | (ctx.current.__logicType === FakeType.Component ? NodeSort.Component : 0) | NodeSort.CtxProvider);
252
208
  if (ctx.current.__logicType) {
253
209
  if (isLogicNode) {
254
- setPulling(ctx.current.effect.ins);
210
+ setPulling(ctx.current.effect);
255
211
  }
256
212
  } else {
257
213
  if (ctx.current) {
@@ -283,7 +239,7 @@ class Interpreter {
283
239
  if (sort & NodeSort.Logic) {
284
240
  const parentLogic = stack.peekByType(NodeSort.Logic)?.node;
285
241
  if (parentLogic) {
286
- setPulling(parentLogic.effect.ins);
242
+ setPulling(parentLogic.effect);
287
243
  } else {
288
244
  setPulling(rootPulling);
289
245
  }
@@ -293,7 +249,8 @@ class Interpreter {
293
249
  this.tokenizer = switcher.tokenizer;
294
250
  }
295
251
  if (parent.__logicType === FakeType.ForItem) {
296
- const forNode = parent.forNode;
252
+ const _ref = parent,
253
+ forNode = _ref.forNode;
297
254
  const i = forNode.i,
298
255
  arr = forNode.arr,
299
256
  snapshot = forNode.snapshot;
@@ -319,7 +276,7 @@ class Interpreter {
319
276
  }
320
277
  return componentNode;
321
278
  }
322
- insertAfterAnchor(name = "anchor") {
279
+ insertAfterAnchor(name = 'anchor') {
323
280
  const _this$ctx = this.ctx,
324
281
  realParent = _this$ctx.realParent,
325
282
  prevSibling = _this$ctx.prevSibling,
@@ -330,12 +287,6 @@ class Interpreter {
330
287
  this.handleInsert(realParent, afterAnchor, prevSibling);
331
288
  return afterAnchor;
332
289
  }
333
- /** 处理
334
- * 是逻辑 是普通
335
- * 父节点 将子节点加入 directList 调用 insert 方法挨个插入子节点
336
- * 子节点 仅插入到父逻辑节点 将本节点插入父节点
337
- * 理论上父节点不能是一个 逻辑节点,遇到if 时 Terp 会重新执行 program 这种情况下,会指定 root 为真实 dom
338
- */
339
290
  handleInsert(parent, child, prev, parentComponent) {
340
291
  if (!child.__logicType) {
341
292
  if (!prev || !prev.__logicType) {
@@ -354,7 +305,6 @@ class Interpreter {
354
305
  }
355
306
  }
356
307
  }
357
- /** 考虑到同级 逻辑模块 */
358
308
  getPrevRealSibling(prevSibling) {
359
309
  if (!prevSibling || !prevSibling.__logicType) {
360
310
  return prevSibling;
@@ -367,38 +317,33 @@ class Interpreter {
367
317
  point = point.anchor;
368
318
  }
369
319
  }
370
- /**
371
- * 声明部分:
372
- * 包含首行定义和(可选的)多行属性扩展
373
- * <declaration> ::= <tagName=token> <headerLine> <extensionLines>
374
- * */
375
320
  declaration(ctx) {
376
321
  const _this$tokenizer$_hook = this.tokenizer._hook({}),
377
322
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
378
323
  hookType = _this$tokenizer$_hook2[0],
379
324
  value = _this$tokenizer$_hook2[1];
380
325
  let _node;
381
- if (value === "if" || value === "else" || value === "fail") {
326
+ if (value === 'if' || value === 'else' || value === 'fail') {
382
327
  return this.condDeclaration(ctx);
383
- } else if (value === "for") {
328
+ } else if (value === 'for') {
384
329
  return this.forDeclaration();
385
330
  } else if (hookType) {
386
331
  const data = this.getData();
387
- if (hookType === "static") {
388
- if (typeof value === "function") {
332
+ if (hookType === 'static') {
333
+ if (typeof value === 'function') {
389
334
  _node = this.componentOrFragmentDeclaration(value, ctx);
390
335
  } else {
391
- throw new SyntaxError(`declaration \u4E0D\u652F\u6301 ${value} \u7C7B\u578B\u7684\u9759\u6001\u63D2\u503C`);
336
+ throw new SyntaxError(`declaration 不支持 ${value} 类型的静态插值`);
392
337
  }
393
338
  } else {
394
339
  const valueIsMapKey = Reflect.has(data[Keys.Raw], value);
395
340
  const val = data[Keys.Raw][value];
396
- if (typeof val === "function") {
341
+ if (typeof val === 'function') {
397
342
  _node = this.componentOrFragmentDeclaration(val, ctx);
398
343
  } else {
399
344
  const str = valueIsMapKey ? value : this.getFn(data, value);
400
- _node = this.createNode("text");
401
- this.onePropParsed(data, _node, "text", str, valueIsMapKey, false);
345
+ _node = this.createNode('text');
346
+ this.onePropParsed(data, _node, 'text', str, valueIsMapKey, false);
402
347
  }
403
348
  }
404
349
  } else {
@@ -420,12 +365,12 @@ class Interpreter {
420
365
  const isDestruct = itemToken.type === TokenType.InsertionExp;
421
366
  let itemExp = itemToken.value;
422
367
  if (isDestruct) {
423
- itemExp = "{" + itemExp + "}";
424
- const vars = itemExp.match(jsVarRegexp).join(",");
425
- itemExp = new Function("item", `let ${vars}; (${itemExp}=item); return {${vars}};`);
368
+ itemExp = '{' + itemExp + '}';
369
+ const vars = itemExp.match(jsVarRegexp).join(',');
370
+ itemExp = new Function('item', `let ${vars}; (${itemExp}=item); return {${vars}};`);
426
371
  }
427
372
  let indexName, keyExp;
428
- while (this.tokenizer.code[this.tokenizer.i] !== "\n") {
373
+ while (this.tokenizer.code[this.tokenizer.i] !== '\n') {
429
374
  const next = this.tokenizer.nextToken();
430
375
  if (next.type !== TokenType.Semicolon) {
431
376
  if (!indexName) {
@@ -439,7 +384,7 @@ class Interpreter {
439
384
  const prevSibling = this.ctx.prevSibling;
440
385
  const forNode = {
441
386
  __logicType: FakeType.For,
442
- snapshot: this.tokenizer.snapshot(["dentStack", "isFirstToken"]),
387
+ snapshot: this.tokenizer.snapshot(['dentStack', 'isFirstToken']),
443
388
  realParent: this.ctx.realParent,
444
389
  prevSibling,
445
390
  realBefore: prevSibling?.realAfter || prevSibling,
@@ -454,25 +399,21 @@ class Interpreter {
454
399
  i: 0
455
400
  };
456
401
  if (keyExp) {
457
- forNode.getKey = new Function("data", `let v;with(data){v=${keyExp}};return v;`);
402
+ forNode.getKey = new Function('data', `let v;with(data){v=${keyExp}};return v;`);
458
403
  }
459
- window["for1"] = forNode;
404
+ window['for1'] = forNode;
460
405
  const data = this.getData();
461
406
  const cells = data[Keys.Meta].cells;
462
407
  const hasArrExpKey = Reflect.has(data[Keys.Raw], arrExp);
463
- const arrSignal = hasArrExpKey ? (
464
- // key 直接拿
465
- data[arrExp], cells.get(arrExp)) :
466
- // 无key
467
- $(this.getFn(data, arrExp));
468
- forNode.realAfter = this.insertAfterAnchor("for-after");
408
+ const arrSignal = hasArrExpKey ? (data[arrExp], cells.get(arrExp)) : new Computed(this.getFn(data, arrExp));
409
+ forNode.realAfter = this.insertAfterAnchor('for-after');
469
410
  const _forNode$snapshot = forNode.snapshot;
470
411
  _forNode$snapshot.dentStack;
471
412
  _forNode$snapshot.isFirstToken;
472
413
  const snapshotForUpdate = _objectWithoutProperties(_forNode$snapshot, _excluded);
473
414
  let isFirstRender = true;
474
- forNode.effect = effect(() => {
475
- let arr = forNode.arr = arrSignal.v;
415
+ forNode.effect = new Effect(() => {
416
+ let arr = forNode.arr = arrSignal.get();
476
417
  arr[Keys.Iterator];
477
418
  arr = toRaw(arr);
478
419
  const children = forNode.children;
@@ -481,7 +422,7 @@ class Interpreter {
481
422
  for (let i = len; i--;) {
482
423
  const nextItem = children[i + 1];
483
424
  const item = this.createForItem(forNode, i, data);
484
- const anchor = this.insertAfterAnchor("for-item-after");
425
+ const anchor = this.insertAfterAnchor('for-item-after');
485
426
  item.realAfter = anchor;
486
427
  if (nextItem) {
487
428
  nextItem.realBefore = anchor;
@@ -508,7 +449,7 @@ class Interpreter {
508
449
  const child = children[i];
509
450
  this.removeLogicNode(child);
510
451
  this.remove(child.realAfter);
511
- child.effect();
452
+ child.effect.dispose();
512
453
  }
513
454
  }
514
455
  if (oldLen < newLen) {
@@ -517,7 +458,7 @@ class Interpreter {
517
458
  const item = this.createForItem(forNode, i, data);
518
459
  newChildren[i] = item;
519
460
  const nextItem = newChildren[i + 1];
520
- const anchor = this.createAnchor("for-item-after");
461
+ const anchor = this.createAnchor('for-item-after');
521
462
  this.insertAfter(forNode.realParent, anchor, lastAfter);
522
463
  item.realAfter = anchor;
523
464
  if (nextItem) {
@@ -529,7 +470,7 @@ class Interpreter {
529
470
  this.tokenizer.useDedentAsEof = false;
530
471
  runWithPulling(() => {
531
472
  this.program(forNode.realParent, forNode.owner, lastAfter, item);
532
- }, item.effect.ins);
473
+ }, item.effect);
533
474
  }
534
475
  const firstInsert = newChildren[oldLen];
535
476
  if (firstInsert) {
@@ -539,7 +480,7 @@ class Interpreter {
539
480
  for (let i = minLen; i--;) {
540
481
  const child = children[i];
541
482
  newChildren[i] = child;
542
- if (typeof itemExp === "string") {
483
+ if (typeof itemExp === 'string') {
543
484
  child.data[itemExp] = arr[i];
544
485
  } else {
545
486
  Object.assign(child.data, itemExp(arr[i]));
@@ -552,27 +493,32 @@ class Interpreter {
552
493
  });
553
494
  return forNode.children[0] || forNode;
554
495
  }
496
+ forItemId = 0;
555
497
  createForItem(forNode, i, parentData) {
556
498
  let forItemNode;
557
- const effect2 = scope(() => {}, null);
499
+ const scope = new Scope(() => {});
500
+ scope.scope = null;
501
+ runWithPulling(() => {
502
+ scope.get();
503
+ }, null);
558
504
  const arr = forNode.arr,
559
505
  itemExp = forNode.itemExp,
560
506
  indexName = forNode.indexName,
561
507
  getKey = forNode.getKey;
562
508
  let data;
563
- if (typeof itemExp === "string") {
564
- data = $(indexName ? {
509
+ if (typeof itemExp === 'string') {
510
+ data = deepSignal(indexName ? {
565
511
  [itemExp]: arr[i],
566
512
  [indexName]: i
567
513
  } : {
568
514
  [itemExp]: arr[i]
569
- });
515
+ }, getPulling());
570
516
  } else {
571
517
  const rawData = itemExp(arr[i]);
572
518
  if (indexName) {
573
519
  rawData[indexName] = i;
574
520
  }
575
- data = $(rawData);
521
+ data = deepSignal(rawData, getPulling());
576
522
  }
577
523
  Object.setPrototypeOf(data, parentData);
578
524
  forItemNode = {
@@ -586,7 +532,7 @@ class Interpreter {
586
532
  effect: null,
587
533
  data
588
534
  };
589
- forItemNode.effect = effect2;
535
+ forItemNode.effect = scope;
590
536
  return forItemNode;
591
537
  }
592
538
  getData() {
@@ -594,24 +540,16 @@ class Interpreter {
594
540
  node = _this$ctx$stack$peekB.node;
595
541
  return node.data || node.owner.data;
596
542
  }
597
- /**
598
- * key 元素,组件的 key
599
- * value
600
- * 1. 静态类型值
601
- * 2. 插值计算 函数,可以考虑 使用 effect 或 computed 做处理
602
- *
603
- * mapKey 映射, 对应子组件的属性
604
- * */
605
543
  onePropParsed(data, node, key, value, valueIsMapKey, isFn, hookI) {
606
544
  if (isFn) {
607
545
  this.setProp(node, key, value, hookI);
608
- } else if (typeof value === "function") {
609
- effect(() => {
546
+ } else if (typeof value === 'function') {
547
+ new Effect(() => {
610
548
  const res = value();
611
549
  this.setProp(node, key, res, hookI);
612
550
  });
613
551
  } else if (valueIsMapKey) {
614
- effect(() => {
552
+ new Effect(() => {
615
553
  const res = data[value];
616
554
  this.setProp(node, key, res, hookI);
617
555
  });
@@ -619,6 +557,7 @@ class Interpreter {
619
557
  this.setProp(node, key, value, hookI);
620
558
  }
621
559
  }
560
+ oneRealPropParsed = this.onePropParsed.bind(this);
622
561
  componentOrFragmentDeclaration(ComponentOrRender, ctx) {
623
562
  let Component, render, child;
624
563
  const isCC = ComponentOrRender.prototype instanceof Store;
@@ -637,7 +576,7 @@ class Interpreter {
637
576
  realBefore: null,
638
577
  realAfter: null,
639
578
  data: child,
640
- tokenizer: render ? render(true) : child["ui"](true)
579
+ tokenizer: render ? render(true) : child['ui'](true)
641
580
  };
642
581
  this.onePropParsed = (data, _, key, value, valueIsMapKey, isFn, hookI) => {
643
582
  if (isFn) {
@@ -647,32 +586,31 @@ class Interpreter {
647
586
  } else {
648
587
  const meta = child[Keys.Meta];
649
588
  const cells = meta.cells;
650
- if (typeof value === "function") {
651
- const computed = $(value);
589
+ if (typeof value === 'function') {
590
+ const computed = new Computed(value);
652
591
  cells.set(key, computed);
653
- child[Keys.Raw][key] = void 0;
592
+ child[Keys.Raw][key] = undefined;
654
593
  } else {
655
594
  cells.set(key, {
656
- v: value
595
+ get: () => value
657
596
  });
658
597
  child[Keys.Raw][key] = value;
659
598
  }
660
599
  }
661
600
  };
662
- node.realAfter = this.insertAfterAnchor("component-after");
601
+ node.realAfter = this.insertAfterAnchor('component-after');
663
602
  return node;
664
603
  }
665
604
  getFn(data, expression) {
666
- return new Function("data", `let v;with(data){v=${expression}};return v;`).bind(void 0, data);
605
+ return new Function('data', `let v;with(data){v=${expression}};return v;`).bind(undefined, data);
667
606
  }
668
- // TODO: 优化代码逻辑,拆分 if elseif else
669
607
  condDeclaration(ctx) {
670
608
  const prevSibling = ctx.prevSibling;
671
609
  const keyWord = this.tokenizer.token;
672
610
  const expToken = this.tokenizer.condExp();
673
611
  const value = expToken.value;
674
- const isElse = keyWord.value === "else";
675
- const isIf = keyWord.value === "if";
612
+ const isElse = keyWord.value === 'else';
613
+ const isIf = keyWord.value === 'if';
676
614
  const preIsCond = prevSibling?.__logicType & CondBit;
677
615
  const data = this.getData();
678
616
  const noCond = value === true;
@@ -680,7 +618,6 @@ class Interpreter {
680
618
  const owner = ctx.stack.peekByType(NodeSort.TokenizerSwitcher)?.node;
681
619
  const ifNode = {
682
620
  __logicType: isElse ? FakeType.Else : isIf ? FakeType.If : FakeType.Fail,
683
- // 此时 token 是 exp, 下次解析 从 \n 开始
684
621
  snapshot: this.tokenizer.snapshot(),
685
622
  realParent: null,
686
623
  realBefore: null,
@@ -693,22 +630,22 @@ class Interpreter {
693
630
  };
694
631
  let signal;
695
632
  switch (keyWord.value) {
696
- case "if":
633
+ case 'if':
697
634
  if (valueIsMapKey) {
698
635
  runWithPulling(() => data[value], null);
699
636
  const cells = data[Keys.Meta].cells;
700
637
  signal = cells.get(value);
701
638
  } else {
702
639
  const fn = this.getFn(data, value);
703
- signal = $(fn);
640
+ signal = new Computed(fn);
704
641
  }
705
642
  break;
706
- case "else":
643
+ case 'else':
707
644
  if (noCond) {
708
- signal = $(() => {
645
+ signal = new Computed(() => {
709
646
  let point = ifNode.preCond;
710
647
  while (point) {
711
- if (point.condition.v) {
648
+ if (point.condition.get()) {
712
649
  return false;
713
650
  }
714
651
  if (point.__logicType === FakeType.If) {
@@ -720,10 +657,10 @@ class Interpreter {
720
657
  });
721
658
  } else {
722
659
  const fn = valueIsMapKey ? null : this.getFn(data, value);
723
- signal = $(() => {
660
+ signal = new Computed(() => {
724
661
  let point = ifNode.preCond;
725
662
  while (point) {
726
- if (point.condition.v) {
663
+ if (point.condition.get()) {
727
664
  return false;
728
665
  }
729
666
  if (point.__logicType === FakeType.If) {
@@ -735,11 +672,11 @@ class Interpreter {
735
672
  });
736
673
  }
737
674
  break;
738
- case "fail":
739
- signal = $(() => {
675
+ case 'fail':
676
+ signal = new Computed(() => {
740
677
  let point = ifNode.preCond;
741
678
  while (point) {
742
- if (point.condition.v) {
679
+ if (point.condition.get()) {
743
680
  return false;
744
681
  }
745
682
  point = point.preCond;
@@ -750,7 +687,7 @@ class Interpreter {
750
687
  }
751
688
  ifNode.condition = signal;
752
689
  ifNode.realAfter = this.insertAfterAnchor(`${keyWord.value}-after`);
753
- ifNode.effect = effect(({
690
+ const ef = effect(({
754
691
  val
755
692
  }) => {
756
693
  if (val) {
@@ -772,6 +709,7 @@ class Interpreter {
772
709
  }
773
710
  ifNode.isFirstRender = false;
774
711
  }, [signal]);
712
+ ifNode.effect = ef.ins;
775
713
  return ifNode;
776
714
  }
777
715
  removeLogicNode(node) {
@@ -785,10 +723,6 @@ class Interpreter {
785
723
  point = next;
786
724
  }
787
725
  }
788
- /**
789
- * <extensionLines> ::= PIPE <attributeList> NEWLINE <extensionLines>
790
- * | ε
791
- */
792
726
  extensionLines(_node) {
793
727
  while (1) {
794
728
  if ((this.tokenizer.token.type & TokenType.Pipe) === 0) {
@@ -802,25 +736,10 @@ class Interpreter {
802
736
  this.tokenizer.nextToken();
803
737
  }
804
738
  }
805
- /**
806
- * 首行:
807
- * 节点名称 + 属性列表 + 换行
808
- * <headerLine> ::= <attributeList> NEWLINE
809
- */
810
739
  headerLine(_node) {
811
740
  this.attributeList(_node);
812
741
  this.tokenizer.nextToken();
813
742
  }
814
- /**
815
- * 属性列表:
816
- * 可以是空的,或者包含多个属性
817
- * <attributeList> ::= <attribute> <attributeList>
818
- * | ε
819
- *
820
- * <attribute> ::= <key> = <value>
821
- * 1. 普通节点 执行 setProps 🪝
822
- * 2. 组件节点 收集映射关系,或 产生 computed
823
- */
824
743
  attributeList(_node) {
825
744
  let key, eq;
826
745
  const data = this.getData();
@@ -828,7 +747,7 @@ class Interpreter {
828
747
  if (key == null) {
829
748
  key = this.tokenizer.token.value;
830
749
  } else if (eq == null) {
831
- eq = "=";
750
+ eq = '=';
832
751
  } else {
833
752
  const _this$tokenizer$_hook3 = this.tokenizer._hook({}),
834
753
  _this$tokenizer$_hook4 = _slicedToArray(_this$tokenizer$_hook3, 3),
@@ -836,12 +755,12 @@ class Interpreter {
836
755
  value = _this$tokenizer$_hook4[1],
837
756
  hookI = _this$tokenizer$_hook4[2];
838
757
  const rawVal = data[Keys.Raw][value];
839
- const isFn = typeof rawVal === "function";
840
- if (hookType === "dynamic") {
758
+ const isFn = typeof rawVal === 'function';
759
+ if (hookType === 'dynamic') {
841
760
  const valueIsMapKey = Reflect.has(data[Keys.Raw], value);
842
761
  const fn = isFn ? rawVal : valueIsMapKey ? value : this.getFn(data, value);
843
762
  this.onePropParsed(data, _node, key, fn, valueIsMapKey, isFn, hookI);
844
- } else if (hookType === "static") {
763
+ } else if (hookType === 'static') {
845
764
  this.onePropParsed(data, _node, key, value, false, isFn, hookI);
846
765
  } else {
847
766
  this.onePropParsed(data, _node, key, value, false, isFn, hookI);
@@ -892,7 +811,6 @@ class Interpreter {
892
811
  remove(node, parent, prev) {
893
812
  return this.defaultRemove(node, parent, prev);
894
813
  }
895
- // TODO: 默认改成 prevItem
896
814
  defaultRemove(node, parent, prevSibling) {
897
815
  const next = node.nextSibling;
898
816
  if (prevSibling) {
@@ -908,76 +826,33 @@ class Interpreter {
908
826
  }
909
827
 
910
828
  class Tokenizer {
829
+ TabSize = 2;
830
+ Tab = Array.from({
831
+ length: this.TabSize
832
+ }, () => ' ').join('');
833
+ static EofId = `__EOF__${Date.now()}`;
834
+ static DedentId = `__DEDENT__${Date.now()}`;
835
+ needIndent = false;
836
+ isFirstToken = true;
837
+ dentStack = [0];
838
+ i = 0;
839
+ handledTokens = [];
840
+ waitingTokens = new Queue();
911
841
  constructor(hook, useDedentAsEof) {
912
842
  this.hook = hook;
913
843
  this.useDedentAsEof = useDedentAsEof;
914
- /** 缩进大小 默认 2 */
915
- this.TabSize = 2;
916
- /** 缩进字符 */
917
- this.Tab = Array.from({
918
- length: this.TabSize
919
- }, () => " ").join("");
920
- /** 回车后需要判断缩进 */
921
- this.needIndent = false;
922
- /** 用于跳过第一个节点前的空白字符串,以及生成基础缩进 */
923
- this.isFirstToken = true;
924
- /** 记录历史缩进的长度,相对于行首 */
925
- this.dentStack = [0];
926
- /** 当前字符 index */
927
- this.i = 0;
928
- // TODO: 生产环境不需要这个,导致不必要的内存占用
929
- this.handledTokens = [];
930
- /**
931
- * 有些标识符能产生多个 token
932
- * 例如 dedent
933
- * parent1
934
- * child
935
- * subChild
936
- * parent2 <- 产生两个 dedent
937
- */
938
- this.waitingTokens = new Queue();
939
- /** 模板字符串动态节点的占位符 */
940
- this.HookId = "_h_o_o_k_";
941
- /** 模板字符串动态节点索引 */
942
- this.hookI = 0;
943
- this._hook = props => {
944
- const value = this.token.value;
945
- const isDynamicHook = this.token.type & TokenType.InsertionExp;
946
- const isStaticHook = typeof value === "string" && value.indexOf(this.HookId) === 0;
947
- const hookType = isDynamicHook ? "dynamic" : isStaticHook ? "static" : void 0;
948
- if (this.hook && isStaticHook) {
949
- const hookI = Number(value.slice(this.HookId.length));
950
- const res = this.hook({
951
- ...props,
952
- HookId: this.HookId,
953
- i: hookI
954
- });
955
- return [hookType, res, hookI];
956
- } else if (isDynamicHook) {
957
- return [hookType, value];
958
- }
959
- return [hookType, value];
960
- };
961
844
  if (useDedentAsEof) {
962
- this.setToken(TokenType.Indent, "");
845
+ this.setToken(TokenType.Indent, '');
963
846
  this.isFirstToken = true;
964
847
  }
965
848
  }
966
- static {
967
- /** Eof 标识符的值 */
968
- this.EofId = `__EOF__${Date.now()}`;
969
- }
970
- static {
971
- this.DedentId = `__DEDENT__${Date.now()}`;
972
- }
973
849
  consume() {
974
850
  const token = this.token;
975
851
  this.nextToken();
976
852
  return token;
977
853
  }
978
- // /** 恢复至某一个现场,进行 token 重算 */
979
854
  resume(_snapshot) {
980
- this.token = void 0;
855
+ this.token = undefined;
981
856
  this.needIndent = false;
982
857
  this.isFirstToken = true;
983
858
  this.dentStack = [0];
@@ -991,7 +866,7 @@ class Tokenizer {
991
866
  if (keys) {
992
867
  for (const k of keys) {
993
868
  snap[k] = this[k];
994
- if (k === "dentStack") {
869
+ if (k === 'dentStack') {
995
870
  snap[k] = this[k].slice();
996
871
  }
997
872
  }
@@ -1002,10 +877,10 @@ class Tokenizer {
1002
877
  const logicDentLen = this.dentStack[this.dentStack.length - 1];
1003
878
  let needIndent = false;
1004
879
  let skipFragment = ``;
1005
- this.token = void 0;
880
+ this.token = undefined;
1006
881
  while (1) {
1007
882
  const char = this.code[this.i];
1008
- if (char === "\n") {
883
+ if (char === '\n') {
1009
884
  needIndent = true;
1010
885
  skipFragment += char;
1011
886
  this.i++;
@@ -1029,7 +904,7 @@ class Tokenizer {
1029
904
  const expLen = this.dentStack[i];
1030
905
  if (currLen === expLen) break;
1031
906
  if (currLen > expLen) {
1032
- throw SyntaxError(`\u7F29\u8FDB\u9519\u8BEF\uFF0C\u7F29\u8FDB\u957F\u5EA6\u4E0D\u5339\u914D`);
907
+ throw SyntaxError(`缩进错误,缩进长度不匹配`);
1033
908
  }
1034
909
  if (this.shorterThanBaseDentEof()) {
1035
910
  break;
@@ -1054,13 +929,12 @@ class Tokenizer {
1054
929
  return skipFragment;
1055
930
  }
1056
931
  setCode(code) {
1057
- this.code = "\n" + code.trimEnd() + `
1058
- ${Tokenizer.EofId}`;
932
+ this.code = '\n' + code.trimEnd() + `\n${Tokenizer.EofId}`;
1059
933
  }
1060
934
  tokenize() {
1061
935
  do {
1062
936
  this.nextToken();
1063
- console.log("token:", TokenType[this.token?.type], JSON.stringify(this.token?.value || ""));
937
+ console.log('token:', TokenType[this.token?.type], JSON.stringify(this.token?.value || ''));
1064
938
  } while (!this.isEof());
1065
939
  }
1066
940
  isEof() {
@@ -1080,7 +954,7 @@ ${Tokenizer.EofId}`;
1080
954
  if (this.isEof()) {
1081
955
  return this.token;
1082
956
  }
1083
- this.token = void 0;
957
+ this.token = undefined;
1084
958
  if (this.waitingTokens.len) {
1085
959
  const item = this.waitingTokens.shift();
1086
960
  this.setToken(item.type, item.value);
@@ -1092,40 +966,39 @@ ${Tokenizer.EofId}`;
1092
966
  } else {
1093
967
  const char = this.code[this.i];
1094
968
  switch (char) {
1095
- case " ":
1096
- case " ":
969
+ case '\t':
970
+ case ' ':
1097
971
  break;
1098
- // 找后续所有 newLine
1099
- case "\n":
972
+ case '\n':
1100
973
  this.newLine();
1101
974
  this.needIndent = true;
1102
975
  break;
1103
- case "=":
976
+ case '=':
1104
977
  this.assignment();
1105
978
  break;
1106
- case "|":
979
+ case '|':
1107
980
  this.pipe();
1108
981
  break;
1109
982
  case "'":
1110
983
  case '"':
1111
984
  this.str(char);
1112
985
  break;
1113
- case "{":
986
+ case '{':
1114
987
  const braceToken = this.brace();
1115
988
  this.setToken(TokenType.InsertionExp, braceToken);
1116
989
  break;
1117
- case "$":
990
+ case '$':
1118
991
  const handled = this.dynamic(char);
1119
992
  if (handled) break;
1120
- case ";":
1121
- this.setToken(TokenType.Semicolon, ";");
993
+ case ';':
994
+ this.setToken(TokenType.Semicolon, ';');
1122
995
  break;
1123
996
  default:
1124
997
  if (isNum(char)) {
1125
998
  this.number(char);
1126
999
  break;
1127
1000
  }
1128
- if (typeof char === "string" && matchIdStart2(char, 0)) {
1001
+ if (typeof char === 'string' && matchIdStart2(char, 0)) {
1129
1002
  this.identifier(char);
1130
1003
  }
1131
1004
  break;
@@ -1145,11 +1018,11 @@ ${Tokenizer.EofId}`;
1145
1018
  }
1146
1019
  }
1147
1020
  condExp() {
1148
- let value = "";
1021
+ let value = '';
1149
1022
  this.token = null;
1150
1023
  while (1) {
1151
1024
  const char = this.code[this.i];
1152
- if (char === "\n") {
1025
+ if (char === '\n') {
1153
1026
  break;
1154
1027
  }
1155
1028
  value += char;
@@ -1159,21 +1032,14 @@ ${Tokenizer.EofId}`;
1159
1032
  this.setToken(TokenType.Identifier, value || true);
1160
1033
  return this.token;
1161
1034
  }
1162
- /**
1163
- * 解析到 for 时使用这个方法获取 for 后方的子表达式
1164
- * 表达式通过 “;” 分割
1165
- * // 最多可有三个表达式
1166
- * for arr ; item index; item.key
1167
- * @returns {boolean} 是否含有 key
1168
- */
1169
1035
  forLoopSubExp() {
1170
1036
  this.token = null;
1171
- let value = "";
1037
+ let value = '';
1172
1038
  let count = 0;
1173
1039
  while (1) {
1174
1040
  const char = this.code[this.i];
1175
- const isSemicolon = char === ";";
1176
- if (isSemicolon || char === "\n") {
1041
+ const isSemicolon = char === ';';
1042
+ if (isSemicolon || char === '\n') {
1177
1043
  value = value.trim();
1178
1044
  if (!this.token) {
1179
1045
  this.setToken(TokenType.Identifier, value);
@@ -1184,10 +1050,10 @@ ${Tokenizer.EofId}`;
1184
1050
  value
1185
1051
  });
1186
1052
  }
1187
- value = "";
1053
+ value = '';
1188
1054
  count++;
1189
1055
  if (count > 3) {
1190
- throw SyntaxError(`for \u5FAA\u73AF\u6700\u591A\u53EF\u5305\u542B\u4E09\u4E2A\u8868\u8FBE\u5F0F, \u5206\u522B\u4E3A arr ; item index [; key]`);
1056
+ throw SyntaxError(`for 循环最多可包含三个表达式, 分别为 arr ; item index [; key]`);
1191
1057
  }
1192
1058
  if (!isSemicolon) return count === 3;
1193
1059
  } else {
@@ -1197,27 +1063,27 @@ ${Tokenizer.EofId}`;
1197
1063
  }
1198
1064
  }
1199
1065
  assignment() {
1200
- this.setToken(TokenType.Assign, "=");
1066
+ this.setToken(TokenType.Assign, '=');
1201
1067
  }
1202
1068
  pipe() {
1203
- this.setToken(TokenType.Pipe, "|");
1069
+ this.setToken(TokenType.Pipe, '|');
1204
1070
  }
1205
1071
  dynamic(char) {
1206
1072
  let nextC = this.code[this.i + 1];
1207
- if (nextC !== "{") {
1073
+ if (nextC !== '{') {
1208
1074
  return false;
1209
1075
  }
1210
1076
  this.i++;
1211
- let value = "${";
1077
+ let value = '${';
1212
1078
  let innerBrace = 0;
1213
1079
  while (1) {
1214
1080
  nextC = this.code[this.i + 1];
1215
1081
  value += nextC;
1216
1082
  this.i++;
1217
- if (nextC === "{") {
1083
+ if (nextC === '{') {
1218
1084
  innerBrace++;
1219
1085
  }
1220
- if (nextC === "}") {
1086
+ if (nextC === '}') {
1221
1087
  if (!innerBrace) {
1222
1088
  break;
1223
1089
  }
@@ -1231,14 +1097,14 @@ ${Tokenizer.EofId}`;
1231
1097
  let inComment,
1232
1098
  inString,
1233
1099
  count = 0,
1234
- value = "",
1100
+ value = '',
1235
1101
  backslashCount = 0;
1236
1102
  while (1) {
1237
1103
  const char = this.code[this.i];
1238
1104
  const nextChar = this.code[this.i + 1];
1239
- if (inComment === "single" && char === "\n") {
1105
+ if (inComment === 'single' && char === '\n') {
1240
1106
  inComment = null;
1241
- } else if (inComment === "multi" && char === "*" && nextChar === "/") {
1107
+ } else if (inComment === 'multi' && char === '*' && nextChar === '/') {
1242
1108
  inComment = null;
1243
1109
  value += this.code[this.i];
1244
1110
  this.i++;
@@ -1246,21 +1112,21 @@ ${Tokenizer.EofId}`;
1246
1112
  if (char === inString && backslashCount % 2 === 0) {
1247
1113
  inString = null;
1248
1114
  }
1249
- backslashCount = char === "\\" ? backslashCount + 1 : 0;
1115
+ backslashCount = char === '\\' ? backslashCount + 1 : 0;
1250
1116
  } else {
1251
- if (char === "/" && nextChar === "/") {
1252
- inComment = "single";
1117
+ if (char === '/' && nextChar === '/') {
1118
+ inComment = 'single';
1253
1119
  value += this.code[this.i];
1254
1120
  this.i++;
1255
- } else if (char === "/" && nextChar === "*") {
1256
- inComment = "multi";
1121
+ } else if (char === '/' && nextChar === '*') {
1122
+ inComment = 'multi';
1257
1123
  value += this.code[this.i];
1258
1124
  this.i++;
1259
- } else if (char === "'" || char === '"' || char === "`") {
1125
+ } else if (char === "'" || char === '"' || char === '`') {
1260
1126
  inString = char;
1261
- } else if (char === "{") {
1127
+ } else if (char === '{') {
1262
1128
  count++;
1263
- } else if (char === "}") {
1129
+ } else if (char === '}') {
1264
1130
  count--;
1265
1131
  }
1266
1132
  }
@@ -1272,11 +1138,11 @@ ${Tokenizer.EofId}`;
1272
1138
  }
1273
1139
  }
1274
1140
  newLine() {
1275
- let value = "\n";
1141
+ let value = '\n';
1276
1142
  let nextC;
1277
1143
  while (1) {
1278
1144
  nextC = this.code[this.i + 1];
1279
- if (nextC !== "\n") {
1145
+ if (nextC !== '\n') {
1280
1146
  break;
1281
1147
  }
1282
1148
  value += nextC;
@@ -1288,26 +1154,26 @@ ${Tokenizer.EofId}`;
1288
1154
  this.setToken(TokenType.NewLine, value);
1289
1155
  }
1290
1156
  getDentValue() {
1291
- let value = "";
1157
+ let value = '';
1292
1158
  let nextC;
1293
1159
  let isEmptyLine = false;
1294
1160
  while (1) {
1295
1161
  const nextChar = this.code[this.i];
1296
1162
  switch (nextChar) {
1297
- case " ":
1163
+ case '\t':
1298
1164
  nextC = this.Tab;
1299
1165
  break;
1300
- case " ":
1301
- nextC = " ";
1166
+ case ' ':
1167
+ nextC = ' ';
1302
1168
  break;
1303
- case "\n":
1304
- nextC = "\n";
1169
+ case '\n':
1170
+ nextC = '\n';
1305
1171
  break;
1306
1172
  default:
1307
- nextC = "";
1173
+ nextC = '';
1308
1174
  break;
1309
1175
  }
1310
- if (nextC === "\n") {
1176
+ if (nextC === '\n') {
1311
1177
  isEmptyLine = true;
1312
1178
  break;
1313
1179
  }
@@ -1348,7 +1214,7 @@ ${Tokenizer.EofId}`;
1348
1214
  const expLen = this.dentStack[i];
1349
1215
  if (currLen === expLen) break;
1350
1216
  if (currLen > expLen) {
1351
- throw SyntaxError("\u7F29\u8FDB\u5927\u5C0F\u4E0D\u7EDF\u4E00");
1217
+ throw SyntaxError('缩进大小不统一');
1352
1218
  }
1353
1219
  if (this.shorterThanBaseDentEof()) {
1354
1220
  return;
@@ -1373,7 +1239,7 @@ ${Tokenizer.EofId}`;
1373
1239
  if (yes) {
1374
1240
  if (!this.token) {
1375
1241
  if (this.useDedentAsEof) {
1376
- this.setToken(TokenType.Dedent, "");
1242
+ this.setToken(TokenType.Dedent, '');
1377
1243
  } else {
1378
1244
  this.setToken(TokenType.Identifier, Tokenizer.EofId);
1379
1245
  }
@@ -1382,7 +1248,7 @@ ${Tokenizer.EofId}`;
1382
1248
  this.waitingTokens.push({
1383
1249
  type: TokenType.Dedent,
1384
1250
  typeName: TokenType[TokenType.Dedent],
1385
- value: ""
1251
+ value: ''
1386
1252
  });
1387
1253
  } else {
1388
1254
  this.waitingTokens.push({
@@ -1400,27 +1266,27 @@ ${Tokenizer.EofId}`;
1400
1266
  let nextC;
1401
1267
  while (1) {
1402
1268
  nextC = this.code[this.i + 1];
1403
- if (typeof nextC !== "string" || !matchIdStart2(nextC, 0)) {
1269
+ if (typeof nextC !== 'string' || !matchIdStart2(nextC, 0)) {
1404
1270
  break;
1405
1271
  }
1406
1272
  value += nextC;
1407
1273
  this.i++;
1408
1274
  }
1409
1275
  if (value === Tokenizer.EofId && this.useDedentAsEof) {
1410
- this.setToken(TokenType.Dedent, "");
1276
+ this.setToken(TokenType.Dedent, '');
1411
1277
  return;
1412
1278
  }
1413
- let realValue = value === "null" ? null : value === "undefined" ? void 0 : value === "false" ? false : value === "true" ? true : value;
1279
+ let realValue = value === 'null' ? null : value === 'undefined' ? undefined : value === 'false' ? false : value === 'true' ? true : value;
1414
1280
  this.setToken(TokenType.Identifier, realValue);
1415
1281
  }
1416
1282
  str(char) {
1417
- let value = "";
1283
+ let value = '';
1418
1284
  let nextC;
1419
1285
  let continuousBackslashCount = 0;
1420
1286
  while (1) {
1421
1287
  nextC = this.code[this.i + 1];
1422
1288
  const memoCount = continuousBackslashCount;
1423
- if (nextC === "\\") {
1289
+ if (nextC === '\\') {
1424
1290
  continuousBackslashCount++;
1425
1291
  } else {
1426
1292
  continuousBackslashCount = 0;
@@ -1447,13 +1313,33 @@ ${Tokenizer.EofId}`;
1447
1313
  this.setToken(TokenType.Identifier, Number(value));
1448
1314
  }
1449
1315
  eof() {
1450
- this.setToken(TokenType.Eof, "End Of File");
1451
- }
1316
+ this.setToken(TokenType.Eof, 'End Of File');
1317
+ }
1318
+ HookId = '_h_o_o_k_';
1319
+ hookI = 0;
1320
+ _hook = props => {
1321
+ const value = this.token.value;
1322
+ const isDynamicHook = this.token.type & TokenType.InsertionExp;
1323
+ const isStaticHook = typeof value === 'string' && value.indexOf(this.HookId) === 0;
1324
+ const hookType = isDynamicHook ? 'dynamic' : isStaticHook ? 'static' : undefined;
1325
+ if (this.hook && isStaticHook) {
1326
+ const hookI = Number(value.slice(this.HookId.length));
1327
+ const res = this.hook({
1328
+ ...props,
1329
+ HookId: this.HookId,
1330
+ i: hookI
1331
+ });
1332
+ return [hookType, res, hookI];
1333
+ } else if (isDynamicHook) {
1334
+ return [hookType, value];
1335
+ }
1336
+ return [hookType, value];
1337
+ };
1452
1338
  init(fragments) {
1453
- if (typeof fragments === "string") {
1339
+ if (typeof fragments === 'string') {
1454
1340
  this.setCode(fragments);
1455
1341
  } else {
1456
- let code = "";
1342
+ let code = '';
1457
1343
  for (let i = 0; i < fragments.length - 1; i++) {
1458
1344
  const fragment = fragments[i];
1459
1345
  code += fragment + `${this.HookId}${i}`;
@@ -1464,7 +1350,7 @@ ${Tokenizer.EofId}`;
1464
1350
  }
1465
1351
 
1466
1352
  function bobe(fragments, ...values) {
1467
- const ui = function ui2(isSub) {
1353
+ const ui = function ui(isSub) {
1468
1354
  const tokenizer = new Tokenizer(({
1469
1355
  i
1470
1356
  }) => {
@@ -1479,7 +1365,7 @@ function bobe(fragments, ...values) {
1479
1365
  function customRender(option) {
1480
1366
  return function render(Ctor, root) {
1481
1367
  const store = Ctor.new();
1482
- const tokenizer = store["ui"](false);
1368
+ const tokenizer = store['ui'](false);
1483
1369
  const terp = new Interpreter(tokenizer);
1484
1370
  terp.config(option);
1485
1371
  const componentNode = {