summitjs 0.1.0 → 0.2.0

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/summit.js CHANGED
@@ -339,6 +339,35 @@ function createScope(raw) {
339
339
  }
340
340
 
341
341
  // src/evaluator/lexer.ts
342
+ var REGEX_PRECEDING_KEYWORDS = /* @__PURE__ */ new Set([
343
+ "return",
344
+ "typeof",
345
+ "void",
346
+ "in",
347
+ "instanceof",
348
+ "new",
349
+ "of",
350
+ "delete",
351
+ "do",
352
+ "else"
353
+ ]);
354
+ function regexAllowed(prev) {
355
+ if (!prev) return true;
356
+ switch (prev.type) {
357
+ case "num":
358
+ case "str":
359
+ case "tmpl":
360
+ case "regex":
361
+ return false;
362
+ // a value precedes -> division
363
+ case "ident":
364
+ return REGEX_PRECEDING_KEYWORDS.has(prev.value);
365
+ case "punc":
366
+ return prev.value !== ")" && prev.value !== "]";
367
+ default:
368
+ return true;
369
+ }
370
+ }
342
371
  var PUNCTUATORS = [
343
372
  "...",
344
373
  "===",
@@ -485,6 +514,35 @@ function tokenize(input) {
485
514
  tokens2.push({ type: "ident", value: input.slice(start2, i), start: start2, end: i });
486
515
  continue;
487
516
  }
517
+ if (ch === "/" && regexAllowed(tokens2[tokens2.length - 1])) {
518
+ const start2 = i;
519
+ i++;
520
+ let body = "";
521
+ let inClass = false;
522
+ while (i < n) {
523
+ const c = input[i];
524
+ if (c === "\\") {
525
+ body += c + (input[i + 1] ?? "");
526
+ i += 2;
527
+ continue;
528
+ }
529
+ if (c === "\n") throw new LexError(`Unterminated regex in expression: ${input}`);
530
+ if (c === "[") inClass = true;
531
+ else if (c === "]") inClass = false;
532
+ else if (c === "/" && !inClass) break;
533
+ body += c;
534
+ i++;
535
+ }
536
+ if (input[i] !== "/") throw new LexError(`Unterminated regex in expression: ${input}`);
537
+ i++;
538
+ let flags = "";
539
+ while (i < n && /[a-z]/i.test(input[i])) {
540
+ flags += input[i];
541
+ i++;
542
+ }
543
+ tokens2.push({ type: "regex", value: body, flags, start: start2, end: i });
544
+ continue;
545
+ }
488
546
  let matched = false;
489
547
  for (const p of PUNCTUATORS) {
490
548
  if (input.startsWith(p, i)) {
@@ -828,8 +886,34 @@ var Parser = class {
828
886
  }
829
887
  return node;
830
888
  }
889
+ parseNewExpr() {
890
+ this.pos++;
891
+ let callee = this.parsePrimary();
892
+ for (; ; ) {
893
+ if (this.isPunc(".")) {
894
+ this.pos++;
895
+ const name = this.next();
896
+ callee = {
897
+ type: "MemberExpression",
898
+ object: callee,
899
+ property: { type: "Identifier", name: name.value },
900
+ computed: false,
901
+ optional: false
902
+ };
903
+ } else if (this.isPunc("[")) {
904
+ this.pos++;
905
+ const property = this.parseExpression();
906
+ this.eatPunc("]");
907
+ callee = { type: "MemberExpression", object: callee, property, computed: true, optional: false };
908
+ } else {
909
+ break;
910
+ }
911
+ }
912
+ const args = this.isPunc("(") ? this.parseArguments() : [];
913
+ return { type: "NewExpression", callee, arguments: args };
914
+ }
831
915
  parseCallMember() {
832
- let node = this.parsePrimary();
916
+ let node = this.isKeyword("new") ? this.parseNewExpr() : this.parsePrimary();
833
917
  for (; ; ) {
834
918
  if (this.isPunc(".")) {
835
919
  this.pos++;
@@ -904,6 +988,10 @@ var Parser = class {
904
988
  this.pos++;
905
989
  return this.parseTemplate(t.raw ?? "");
906
990
  }
991
+ if (t.type === "regex") {
992
+ this.pos++;
993
+ return { type: "RegexLiteral", pattern: t.value, flags: t.flags ?? "" };
994
+ }
907
995
  if (t.type === "ident") {
908
996
  if (t.value === "true" || t.value === "false") {
909
997
  this.pos++;
@@ -985,6 +1073,11 @@ var Parser = class {
985
1073
  return { type: "ObjectExpression", properties };
986
1074
  }
987
1075
  parseObjectProperty() {
1076
+ if (this.isKeyword("async") && this.peek(1).type === "ident") {
1077
+ throw new ParseError(
1078
+ "async methods are not supported in Summit expressions. Use a regular method that returns a Promise and chain .then()."
1079
+ );
1080
+ }
988
1081
  if ((this.isKeyword("get") || this.isKeyword("set")) && !this.isPunc(":", 1) && !this.isPunc(",", 1) && !this.isPunc("(", 1) && !this.isPunc("}", 1)) {
989
1082
  const kind = this.next().value;
990
1083
  const { key: key2, computed: computed3 } = this.parsePropertyKey();
@@ -1395,6 +1488,8 @@ var Interpreter = class {
1395
1488
  return null;
1396
1489
  case "UndefinedLiteral":
1397
1490
  return void 0;
1491
+ case "RegexLiteral":
1492
+ return new RegExp(node.pattern, node.flags);
1398
1493
  case "ThisExpression":
1399
1494
  return thisVal;
1400
1495
  case "Identifier":
@@ -1464,6 +1559,14 @@ var Interpreter = class {
1464
1559
  }
1465
1560
  case "CallExpression":
1466
1561
  return this.evalCall(node, scope, thisVal);
1562
+ case "NewExpression": {
1563
+ const ctor = this.evalExpr(node.callee, scope, thisVal);
1564
+ const args = this.evalArguments(node.arguments, scope, thisVal);
1565
+ if (typeof ctor !== "function") {
1566
+ throw new TypeError("Expression value is not a constructor");
1567
+ }
1568
+ return Reflect.construct(ctor, args);
1569
+ }
1467
1570
  case "ArrowFunction":
1468
1571
  return this.makeFunction(
1469
1572
  node,
@@ -2043,15 +2146,26 @@ function initData(el, dmeta, parentScopes) {
2043
2146
  const parentEnv = makeEnv(el);
2044
2147
  const expr = dmeta.expression.trim();
2045
2148
  let raw;
2046
- const match = expr.match(DATA_PROVIDER_RE);
2047
- if (match && getData(match[1])) {
2048
- const provider = getData(match[1]);
2049
- const args = match[2] ? parentEnv.evaluate("[" + (match[3] ?? "") + "]") : [];
2050
- raw = provider(...args);
2051
- } else if (expr === "") {
2149
+ try {
2150
+ const match = expr.match(DATA_PROVIDER_RE);
2151
+ if (match && getData(match[1])) {
2152
+ const provider = getData(match[1]);
2153
+ const args = match[2] ? parentEnv.evaluate("[" + (match[3] ?? "") + "]") : [];
2154
+ raw = provider(...args);
2155
+ } else if (expr === "") {
2156
+ raw = {};
2157
+ } else {
2158
+ raw = parentEnv.evaluate("(" + expr + ")");
2159
+ }
2160
+ } catch (err) {
2161
+ fail("E104", "s-data could not be evaluated; the component starts with empty state.", {
2162
+ el,
2163
+ expression: expr,
2164
+ doc: "s-data",
2165
+ hint: "Check the object for unsupported syntax, e.g. an async method.",
2166
+ cause: err
2167
+ });
2052
2168
  raw = {};
2053
- } else {
2054
- raw = parentEnv.evaluate("(" + expr + ")");
2055
2169
  }
2056
2170
  if (raw === null || typeof raw !== "object") raw = {};
2057
2171
  const scope = createScope(raw);
@@ -2808,6 +2922,7 @@ var sFor = (el, meta2, utils) => {
2808
2922
  blocks.delete(key);
2809
2923
  } else {
2810
2924
  const node = blueprint.cloneNode(true);
2925
+ parent.insertBefore(node, anchor2);
2811
2926
  utils.initTree(node, [...parentScopes, scope]);
2812
2927
  next.set(key, { node, scope });
2813
2928
  }