bobe 0.0.34 → 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.cjs.js CHANGED
@@ -43,6 +43,13 @@ let NodeSort = function (NodeSort) {
43
43
  TerpEvt["HandledComponentNode"] = "handled-component-node";
44
44
  return TerpEvt;
45
45
  })({});
46
+ class ParseSyntaxError extends SyntaxError {
47
+ constructor(code, message, loc) {
48
+ super(message);
49
+ this.code = code;
50
+ this.loc = loc;
51
+ }
52
+ }
46
53
 
47
54
  class Tokenizer {
48
55
  TabSize = 2;
@@ -81,6 +88,25 @@ class Tokenizer {
81
88
  column: this.column
82
89
  };
83
90
  }
91
+ unclosedLoc(startOffset, startLine, startCol) {
92
+ const end = this.code.length - 1;
93
+ return {
94
+ start: {
95
+ offset: startOffset,
96
+ line: startLine,
97
+ column: startCol
98
+ },
99
+ end: {
100
+ offset: end,
101
+ line: this.line,
102
+ column: this.column
103
+ },
104
+ source: this.code.slice(startOffset, end)
105
+ };
106
+ }
107
+ throwUnclosed(code, message, startOffset, startLine, startCol) {
108
+ throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
109
+ }
84
110
  resume(_snapshot) {
85
111
  this.token = undefined;
86
112
  this.needIndent = false;
@@ -249,8 +275,7 @@ class Tokenizer {
249
275
  }
250
276
  return this.token;
251
277
  } catch (error) {
252
- console.error(error);
253
- return this.token;
278
+ throw error;
254
279
  } finally {
255
280
  this.handledTokens.push(this.token);
256
281
  }
@@ -313,6 +338,9 @@ class Tokenizer {
313
338
  this.setToken(TokenType.Pipe, '|');
314
339
  }
315
340
  staticIns() {
341
+ const startOffset = this.preI,
342
+ startLine = this.line,
343
+ startCol = this.preCol;
316
344
  let nextC = this.code[this.i + 1];
317
345
  if (nextC !== '{') {
318
346
  return false;
@@ -322,6 +350,9 @@ class Tokenizer {
322
350
  let innerBrace = 0;
323
351
  while (1) {
324
352
  nextC = this.code[this.i + 1];
353
+ if (nextC === undefined) {
354
+ this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
355
+ }
325
356
  value += nextC;
326
357
  this.next();
327
358
  if (nextC === '{') {
@@ -338,6 +369,9 @@ class Tokenizer {
338
369
  return true;
339
370
  }
340
371
  brace() {
372
+ const startOffset = this.preI,
373
+ startLine = this.line,
374
+ startCol = this.preCol;
341
375
  let inComment,
342
376
  inString,
343
377
  count = 0,
@@ -345,6 +379,9 @@ class Tokenizer {
345
379
  backslashCount = 0;
346
380
  while (1) {
347
381
  const char = this.code[this.i];
382
+ if (char === undefined) {
383
+ this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
384
+ }
348
385
  const nextChar = this.code[this.i + 1];
349
386
  if (inComment === 'single' && char === '\n') {
350
387
  inComment = null;
@@ -527,11 +564,17 @@ class Tokenizer {
527
564
  this.setToken(TokenType.Identifier, realValue);
528
565
  }
529
566
  str(char) {
567
+ const startOffset = this.preI,
568
+ startLine = this.line,
569
+ startCol = this.preCol;
530
570
  let value = '';
531
571
  let nextC;
532
572
  let continuousBackslashCount = 0;
533
573
  while (1) {
534
574
  nextC = this.code[this.i + 1];
575
+ if (nextC === undefined) {
576
+ this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
577
+ }
535
578
  const memoCount = continuousBackslashCount;
536
579
  if (nextC === '\\') {
537
580
  continuousBackslashCount++;
@@ -599,24 +642,6 @@ class Tokenizer {
599
642
  }
600
643
  }
601
644
 
602
- var NodeType = function (NodeType) {
603
- NodeType["Element"] = "Element";
604
- NodeType["Text"] = "Text";
605
- NodeType["Interpolation"] = "Interpolation";
606
- NodeType["Property"] = "Property";
607
- NodeType["PropertyKey"] = "PropertyKey";
608
- NodeType["StaticValue"] = "StaticValue";
609
- NodeType["DynamicValue"] = "DynamicValue";
610
- NodeType["Program"] = "Program";
611
- NodeType["If"] = "If";
612
- NodeType["Else"] = "Else";
613
- NodeType["Fail"] = "Fail";
614
- NodeType["For"] = "For";
615
- NodeType["Component"] = "Component";
616
- NodeType["Fragment"] = "Fragment";
617
- return NodeType;
618
- }(NodeType || {});
619
-
620
645
  function _applyDecs2311(e, t, n, r, o, i) {
621
646
  var a,
622
647
  c,
@@ -833,24 +858,71 @@ function _unsupportedIterableToArray(r, a) {
833
858
  }
834
859
  }
835
860
 
861
+ var NodeType = function (NodeType) {
862
+ NodeType["Element"] = "Element";
863
+ NodeType["Text"] = "Text";
864
+ NodeType["Interpolation"] = "Interpolation";
865
+ NodeType["Property"] = "Property";
866
+ NodeType["PropertyKey"] = "PropertyKey";
867
+ NodeType["StaticValue"] = "StaticValue";
868
+ NodeType["DynamicValue"] = "DynamicValue";
869
+ NodeType["Program"] = "Program";
870
+ NodeType["If"] = "If";
871
+ NodeType["Else"] = "Else";
872
+ NodeType["Fail"] = "Fail";
873
+ NodeType["For"] = "For";
874
+ NodeType["Component"] = "Component";
875
+ NodeType["Fragment"] = "Fragment";
876
+ return NodeType;
877
+ }(NodeType || {});
878
+
836
879
  let _initProto;
837
880
  class Compiler {
838
881
  static {
839
- 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"]]).e, 1);
882
+ 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);
840
883
  _initProto = _applyDecs$e[0];
841
884
  }
885
+ errors = (_initProto(this), []);
842
886
  constructor(tokenizer, hooks = {}) {
843
887
  this.tokenizer = tokenizer;
844
888
  this.hooks = hooks;
845
- _initProto(this);
889
+ }
890
+ addError(code, message, loc) {
891
+ this.errors.push({
892
+ code,
893
+ message,
894
+ loc
895
+ });
896
+ }
897
+ emptyLoc() {
898
+ const pos = this.tokenizer.getCurrentPos();
899
+ return {
900
+ start: pos,
901
+ end: {
902
+ offset: pos.offset + 1,
903
+ line: pos.line,
904
+ column: pos.column + 1
905
+ },
906
+ source: ' '
907
+ };
846
908
  }
847
909
  parseProgram() {
848
- this.tokenizer.nextToken();
849
910
  const body = [];
850
- while (!this.tokenizer.isEof()) {
851
- const node = this.templateNode();
852
- if (node) {
853
- body.push(node);
911
+ try {
912
+ this.tokenizer.nextToken();
913
+ while (!this.tokenizer.isEof()) {
914
+ const node = this.templateNode(body);
915
+ if (node) {
916
+ body.push(node);
917
+ }
918
+ }
919
+ } catch (error) {
920
+ if (error instanceof ParseSyntaxError) {
921
+ this.addError(error.code, error.message, error.loc);
922
+ } else if (error instanceof SyntaxError) {
923
+ const knownCodes = ['INCONSISTENT_INDENT', 'INDENT_MISMATCH'];
924
+ const code = knownCodes.includes(error.message) ? error.message : 'INCONSISTENT_INDENT';
925
+ this.addError(code, error.message, this.emptyLoc());
854
926
  }
855
927
  }
856
928
  return {
@@ -876,7 +948,7 @@ class Compiler {
876
948
  if (this.tokenizer.token.type & TokenType.Indent) {
877
949
  this.tokenizer.nextToken();
878
950
  while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
879
- const child = this.templateNode();
951
+ const child = this.templateNode(children);
880
952
  if (child) {
881
953
  children.push(child);
882
954
  }
@@ -887,13 +959,25 @@ class Compiler {
887
959
  }
888
960
  return children;
889
961
  }
890
- templateNode() {
891
- this.tokenizer.token;
962
+ templateNode(siblings) {
963
+ const token = this.tokenizer.token;
964
+ if (token.type & TokenType.Pipe) {
965
+ this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
966
+ this.tokenizer.nextToken();
967
+ return null;
968
+ }
892
969
  const _this$tokenizer$_hook = this.tokenizer._hook({}),
893
970
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
894
971
  hookType = _this$tokenizer$_hook2[0],
895
972
  value = _this$tokenizer$_hook2[1];
896
973
  if (value === 'if' || value === 'else' || value === 'fail') {
974
+ if (value === 'else' || value === 'fail') {
975
+ const lastSibling = siblings[siblings.length - 1];
976
+ const lastType = lastSibling?.type;
977
+ if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
978
+ this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
979
+ }
980
+ }
897
981
  return this.parseConditionalNode();
898
982
  }
899
983
  if (value === 'for') {
@@ -905,12 +989,11 @@ class Compiler {
905
989
  return this.parseElementNode();
906
990
  }
907
991
  parseComponentNode(node) {
908
- const tagToken = this.tokenizer.token;
909
- const tagName = tagToken.value;
992
+ const name = this.parseName();
910
993
  this.tokenizer.nextToken();
911
994
  const props = this.headerLineAndExtensions();
912
995
  node.type = NodeType.Component;
913
- node.componentName = tagName;
996
+ node.componentName = name;
914
997
  node.props = props;
915
998
  this.hooks.parseComponentNode?.propsAdded?.call(this, node);
916
999
  const children = this.handleChildren();
@@ -919,6 +1002,13 @@ class Compiler {
919
1002
  }
920
1003
  parseElementNode(node) {
921
1004
  const tagToken = this.tokenizer.token;
1005
+ if (!(tagToken.type & TokenType.Identifier)) {
1006
+ this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
1007
+ while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1008
+ this.tokenizer.nextToken();
1009
+ }
1010
+ return null;
1011
+ }
922
1012
  const tagName = tagToken.value;
923
1013
  this.tokenizer.nextToken();
924
1014
  const props = this.headerLineAndExtensions();
@@ -944,15 +1034,25 @@ class Compiler {
944
1034
  return node;
945
1035
  }
946
1036
  parseLoopNode(node) {
1037
+ const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
947
1038
  this.tokenizer.nextToken();
948
1039
  const collection = this.parsePropertyValue();
949
- this.tokenizer.nextToken();
1040
+ if (!collection.value && collection.value !== 0) {
1041
+ this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
1042
+ }
1043
+ const semicolonToken = this.tokenizer.nextToken();
1044
+ if (!(semicolonToken.type & TokenType.Semicolon)) {
1045
+ this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
1046
+ }
950
1047
  const itemToken = this.tokenizer.nextToken();
951
1048
  const isDestruct = itemToken.type === TokenType.InsertionExp;
952
1049
  if (isDestruct) {
953
1050
  itemToken.value = '{' + itemToken.value + '}';
954
1051
  }
955
1052
  const item = this.parsePropertyValue();
1053
+ if (!item.value && item.value !== 0) {
1054
+ this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
1055
+ }
956
1056
  let char = this.tokenizer.peekChar(),
957
1057
  key,
958
1058
  index;
@@ -1015,6 +1115,8 @@ class Compiler {
1015
1115
  this.tokenizer.nextToken();
1016
1116
  node.value = this.parsePropertyValue();
1017
1117
  this.tokenizer.nextToken();
1118
+ } else {
1119
+ this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
1018
1120
  }
1019
1121
  node.loc.start = node.key.loc.start;
1020
1122
  node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
@@ -1035,6 +1137,15 @@ class Compiler {
1035
1137
  node.value = value;
1036
1138
  return node;
1037
1139
  }
1140
+ parseName(node) {
1141
+ const _this$tokenizer$_hook5 = this.tokenizer._hook({}),
1142
+ _this$tokenizer$_hook6 = _slicedToArray(_this$tokenizer$_hook5, 2),
1143
+ hookType = _this$tokenizer$_hook6[0],
1144
+ value = _this$tokenizer$_hook6[1];
1145
+ node.type = hookType === 'dynamic' ? NodeType.DynamicValue : NodeType.StaticValue;
1146
+ node.value = value;
1147
+ return node;
1148
+ }
1038
1149
  }
1039
1150
  function NodeLoc(target, context) {
1040
1151
  return function (_node) {
@@ -2005,6 +2116,7 @@ function customRender(option) {
2005
2116
 
2006
2117
  exports.Compiler = Compiler;
2007
2118
  exports.NodeType = NodeType;
2119
+ exports.ParseSyntaxError = ParseSyntaxError;
2008
2120
  exports.Tokenizer = Tokenizer;
2009
2121
  exports.bobe = bobe;
2010
2122
  exports.customRender = customRender;