bobe 0.0.35 → 0.0.36

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
@@ -42,6 +42,13 @@ let NodeSort = function (NodeSort) {
42
42
  TerpEvt["HandledComponentNode"] = "handled-component-node";
43
43
  return TerpEvt;
44
44
  })({});
45
+ class ParseSyntaxError extends SyntaxError {
46
+ constructor(code, message, loc) {
47
+ super(message);
48
+ this.code = code;
49
+ this.loc = loc;
50
+ }
51
+ }
45
52
 
46
53
  class Tokenizer {
47
54
  TabSize = 2;
@@ -80,6 +87,25 @@ class Tokenizer {
80
87
  column: this.column
81
88
  };
82
89
  }
90
+ unclosedLoc(startOffset, startLine, startCol) {
91
+ const end = this.code.length - 1;
92
+ return {
93
+ start: {
94
+ offset: startOffset,
95
+ line: startLine,
96
+ column: startCol
97
+ },
98
+ end: {
99
+ offset: end,
100
+ line: this.line,
101
+ column: this.column
102
+ },
103
+ source: this.code.slice(startOffset, end)
104
+ };
105
+ }
106
+ throwUnclosed(code, message, startOffset, startLine, startCol) {
107
+ throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
108
+ }
83
109
  resume(_snapshot) {
84
110
  this.token = undefined;
85
111
  this.needIndent = false;
@@ -248,8 +274,7 @@ class Tokenizer {
248
274
  }
249
275
  return this.token;
250
276
  } catch (error) {
251
- console.error(error);
252
- return this.token;
277
+ throw error;
253
278
  } finally {
254
279
  this.handledTokens.push(this.token);
255
280
  }
@@ -312,6 +337,9 @@ class Tokenizer {
312
337
  this.setToken(TokenType.Pipe, '|');
313
338
  }
314
339
  staticIns() {
340
+ const startOffset = this.preI,
341
+ startLine = this.line,
342
+ startCol = this.preCol;
315
343
  let nextC = this.code[this.i + 1];
316
344
  if (nextC !== '{') {
317
345
  return false;
@@ -321,6 +349,9 @@ class Tokenizer {
321
349
  let innerBrace = 0;
322
350
  while (1) {
323
351
  nextC = this.code[this.i + 1];
352
+ if (nextC === undefined) {
353
+ this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
354
+ }
324
355
  value += nextC;
325
356
  this.next();
326
357
  if (nextC === '{') {
@@ -337,6 +368,9 @@ class Tokenizer {
337
368
  return true;
338
369
  }
339
370
  brace() {
371
+ const startOffset = this.preI,
372
+ startLine = this.line,
373
+ startCol = this.preCol;
340
374
  let inComment,
341
375
  inString,
342
376
  count = 0,
@@ -344,6 +378,9 @@ class Tokenizer {
344
378
  backslashCount = 0;
345
379
  while (1) {
346
380
  const char = this.code[this.i];
381
+ if (char === undefined) {
382
+ this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
383
+ }
347
384
  const nextChar = this.code[this.i + 1];
348
385
  if (inComment === 'single' && char === '\n') {
349
386
  inComment = null;
@@ -526,11 +563,17 @@ class Tokenizer {
526
563
  this.setToken(TokenType.Identifier, realValue);
527
564
  }
528
565
  str(char) {
566
+ const startOffset = this.preI,
567
+ startLine = this.line,
568
+ startCol = this.preCol;
529
569
  let value = '';
530
570
  let nextC;
531
571
  let continuousBackslashCount = 0;
532
572
  while (1) {
533
573
  nextC = this.code[this.i + 1];
574
+ if (nextC === undefined) {
575
+ this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
576
+ }
534
577
  const memoCount = continuousBackslashCount;
535
578
  if (nextC === '\\') {
536
579
  continuousBackslashCount++;
@@ -838,18 +881,47 @@ class Compiler {
838
881
  var _applyDecs$e = _slicedToArray(_applyDecs2311(this, [], [[NodeHook, 2, "parseProgram"], [[NodeHook, NodeLoc], 2, "parseComponentNode"], [[NodeHook, NodeLoc], 2, "parseElementNode"], [[NodeHook, NodeLoc], 2, "parseConditionalNode"], [[NodeHook, NodeLoc], 2, "parseLoopNode"], [NodeHook, 2, "parseProperty"], [[NodeHook, TokenLoc], 2, "parsePropertyKey"], [[NodeHook, TokenLoc], 2, "parsePropertyValue"], [[NodeHook, TokenLoc], 2, "parseName"]]).e, 1);
839
882
  _initProto = _applyDecs$e[0];
840
883
  }
884
+ errors = (_initProto(this), []);
841
885
  constructor(tokenizer, hooks = {}) {
842
886
  this.tokenizer = tokenizer;
843
887
  this.hooks = hooks;
844
- _initProto(this);
888
+ }
889
+ addError(code, message, loc) {
890
+ this.errors.push({
891
+ code,
892
+ message,
893
+ loc
894
+ });
895
+ }
896
+ emptyLoc() {
897
+ const pos = this.tokenizer.getCurrentPos();
898
+ return {
899
+ start: pos,
900
+ end: {
901
+ offset: pos.offset + 1,
902
+ line: pos.line,
903
+ column: pos.column + 1
904
+ },
905
+ source: ' '
906
+ };
845
907
  }
846
908
  parseProgram() {
847
- this.tokenizer.nextToken();
848
909
  const body = [];
849
- while (!this.tokenizer.isEof()) {
850
- const node = this.templateNode();
851
- if (node) {
852
- body.push(node);
910
+ try {
911
+ this.tokenizer.nextToken();
912
+ while (!this.tokenizer.isEof()) {
913
+ const node = this.templateNode(body);
914
+ if (node) {
915
+ body.push(node);
916
+ }
917
+ }
918
+ } catch (error) {
919
+ if (error instanceof ParseSyntaxError) {
920
+ this.addError(error.code, error.message, error.loc);
921
+ } else if (error instanceof SyntaxError) {
922
+ const knownCodes = ['INCONSISTENT_INDENT', 'INDENT_MISMATCH'];
923
+ const code = knownCodes.includes(error.message) ? error.message : 'INCONSISTENT_INDENT';
924
+ this.addError(code, error.message, this.emptyLoc());
853
925
  }
854
926
  }
855
927
  return {
@@ -875,7 +947,7 @@ class Compiler {
875
947
  if (this.tokenizer.token.type & TokenType.Indent) {
876
948
  this.tokenizer.nextToken();
877
949
  while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
878
- const child = this.templateNode();
950
+ const child = this.templateNode(children);
879
951
  if (child) {
880
952
  children.push(child);
881
953
  }
@@ -886,13 +958,25 @@ class Compiler {
886
958
  }
887
959
  return children;
888
960
  }
889
- templateNode() {
890
- this.tokenizer.token;
961
+ templateNode(siblings) {
962
+ const token = this.tokenizer.token;
963
+ if (token.type & TokenType.Pipe) {
964
+ this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
965
+ this.tokenizer.nextToken();
966
+ return null;
967
+ }
891
968
  const _this$tokenizer$_hook = this.tokenizer._hook({}),
892
969
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
893
970
  hookType = _this$tokenizer$_hook2[0],
894
971
  value = _this$tokenizer$_hook2[1];
895
972
  if (value === 'if' || value === 'else' || value === 'fail') {
973
+ if (value === 'else' || value === 'fail') {
974
+ const lastSibling = siblings[siblings.length - 1];
975
+ const lastType = lastSibling?.type;
976
+ if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
977
+ this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
978
+ }
979
+ }
896
980
  return this.parseConditionalNode();
897
981
  }
898
982
  if (value === 'for') {
@@ -917,6 +1001,13 @@ class Compiler {
917
1001
  }
918
1002
  parseElementNode(node) {
919
1003
  const tagToken = this.tokenizer.token;
1004
+ if (!(tagToken.type & TokenType.Identifier)) {
1005
+ this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
1006
+ while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1007
+ this.tokenizer.nextToken();
1008
+ }
1009
+ return null;
1010
+ }
920
1011
  const tagName = tagToken.value;
921
1012
  this.tokenizer.nextToken();
922
1013
  const props = this.headerLineAndExtensions();
@@ -942,15 +1033,25 @@ class Compiler {
942
1033
  return node;
943
1034
  }
944
1035
  parseLoopNode(node) {
1036
+ const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
945
1037
  this.tokenizer.nextToken();
946
1038
  const collection = this.parsePropertyValue();
947
- this.tokenizer.nextToken();
1039
+ if (!collection.value && collection.value !== 0) {
1040
+ this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
1041
+ }
1042
+ const semicolonToken = this.tokenizer.nextToken();
1043
+ if (!(semicolonToken.type & TokenType.Semicolon)) {
1044
+ this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
1045
+ }
948
1046
  const itemToken = this.tokenizer.nextToken();
949
1047
  const isDestruct = itemToken.type === TokenType.InsertionExp;
950
1048
  if (isDestruct) {
951
1049
  itemToken.value = '{' + itemToken.value + '}';
952
1050
  }
953
1051
  const item = this.parsePropertyValue();
1052
+ if (!item.value && item.value !== 0) {
1053
+ this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
1054
+ }
954
1055
  let char = this.tokenizer.peekChar(),
955
1056
  key,
956
1057
  index;
@@ -1013,6 +1114,8 @@ class Compiler {
1013
1114
  this.tokenizer.nextToken();
1014
1115
  node.value = this.parsePropertyValue();
1015
1116
  this.tokenizer.nextToken();
1117
+ } else {
1118
+ this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
1016
1119
  }
1017
1120
  node.loc.start = node.key.loc.start;
1018
1121
  node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
@@ -2010,5 +2113,5 @@ function customRender(option) {
2010
2113
  };
2011
2114
  }
2012
2115
 
2013
- export { Compiler, NodeType, Tokenizer, bobe, customRender };
2116
+ export { Compiler, NodeType, ParseSyntaxError, Tokenizer, bobe, customRender };
2014
2117
  //# sourceMappingURL=bobe.esm.js.map