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.cjs CHANGED
@@ -343,6 +343,35 @@ function createScope(raw) {
343
343
  }
344
344
 
345
345
  // src/evaluator/lexer.ts
346
+ var REGEX_PRECEDING_KEYWORDS = /* @__PURE__ */ new Set([
347
+ "return",
348
+ "typeof",
349
+ "void",
350
+ "in",
351
+ "instanceof",
352
+ "new",
353
+ "of",
354
+ "delete",
355
+ "do",
356
+ "else"
357
+ ]);
358
+ function regexAllowed(prev) {
359
+ if (!prev) return true;
360
+ switch (prev.type) {
361
+ case "num":
362
+ case "str":
363
+ case "tmpl":
364
+ case "regex":
365
+ return false;
366
+ // a value precedes -> division
367
+ case "ident":
368
+ return REGEX_PRECEDING_KEYWORDS.has(prev.value);
369
+ case "punc":
370
+ return prev.value !== ")" && prev.value !== "]";
371
+ default:
372
+ return true;
373
+ }
374
+ }
346
375
  var PUNCTUATORS = [
347
376
  "...",
348
377
  "===",
@@ -489,6 +518,35 @@ function tokenize(input) {
489
518
  tokens2.push({ type: "ident", value: input.slice(start2, i), start: start2, end: i });
490
519
  continue;
491
520
  }
521
+ if (ch === "/" && regexAllowed(tokens2[tokens2.length - 1])) {
522
+ const start2 = i;
523
+ i++;
524
+ let body = "";
525
+ let inClass = false;
526
+ while (i < n) {
527
+ const c = input[i];
528
+ if (c === "\\") {
529
+ body += c + (input[i + 1] ?? "");
530
+ i += 2;
531
+ continue;
532
+ }
533
+ if (c === "\n") throw new LexError(`Unterminated regex in expression: ${input}`);
534
+ if (c === "[") inClass = true;
535
+ else if (c === "]") inClass = false;
536
+ else if (c === "/" && !inClass) break;
537
+ body += c;
538
+ i++;
539
+ }
540
+ if (input[i] !== "/") throw new LexError(`Unterminated regex in expression: ${input}`);
541
+ i++;
542
+ let flags = "";
543
+ while (i < n && /[a-z]/i.test(input[i])) {
544
+ flags += input[i];
545
+ i++;
546
+ }
547
+ tokens2.push({ type: "regex", value: body, flags, start: start2, end: i });
548
+ continue;
549
+ }
492
550
  let matched = false;
493
551
  for (const p of PUNCTUATORS) {
494
552
  if (input.startsWith(p, i)) {
@@ -832,8 +890,34 @@ var Parser = class {
832
890
  }
833
891
  return node;
834
892
  }
893
+ parseNewExpr() {
894
+ this.pos++;
895
+ let callee = this.parsePrimary();
896
+ for (; ; ) {
897
+ if (this.isPunc(".")) {
898
+ this.pos++;
899
+ const name = this.next();
900
+ callee = {
901
+ type: "MemberExpression",
902
+ object: callee,
903
+ property: { type: "Identifier", name: name.value },
904
+ computed: false,
905
+ optional: false
906
+ };
907
+ } else if (this.isPunc("[")) {
908
+ this.pos++;
909
+ const property = this.parseExpression();
910
+ this.eatPunc("]");
911
+ callee = { type: "MemberExpression", object: callee, property, computed: true, optional: false };
912
+ } else {
913
+ break;
914
+ }
915
+ }
916
+ const args = this.isPunc("(") ? this.parseArguments() : [];
917
+ return { type: "NewExpression", callee, arguments: args };
918
+ }
835
919
  parseCallMember() {
836
- let node = this.parsePrimary();
920
+ let node = this.isKeyword("new") ? this.parseNewExpr() : this.parsePrimary();
837
921
  for (; ; ) {
838
922
  if (this.isPunc(".")) {
839
923
  this.pos++;
@@ -908,6 +992,10 @@ var Parser = class {
908
992
  this.pos++;
909
993
  return this.parseTemplate(t.raw ?? "");
910
994
  }
995
+ if (t.type === "regex") {
996
+ this.pos++;
997
+ return { type: "RegexLiteral", pattern: t.value, flags: t.flags ?? "" };
998
+ }
911
999
  if (t.type === "ident") {
912
1000
  if (t.value === "true" || t.value === "false") {
913
1001
  this.pos++;
@@ -989,6 +1077,11 @@ var Parser = class {
989
1077
  return { type: "ObjectExpression", properties };
990
1078
  }
991
1079
  parseObjectProperty() {
1080
+ if (this.isKeyword("async") && this.peek(1).type === "ident") {
1081
+ throw new ParseError(
1082
+ "async methods are not supported in Summit expressions. Use a regular method that returns a Promise and chain .then()."
1083
+ );
1084
+ }
992
1085
  if ((this.isKeyword("get") || this.isKeyword("set")) && !this.isPunc(":", 1) && !this.isPunc(",", 1) && !this.isPunc("(", 1) && !this.isPunc("}", 1)) {
993
1086
  const kind = this.next().value;
994
1087
  const { key: key2, computed: computed3 } = this.parsePropertyKey();
@@ -1399,6 +1492,8 @@ var Interpreter = class {
1399
1492
  return null;
1400
1493
  case "UndefinedLiteral":
1401
1494
  return void 0;
1495
+ case "RegexLiteral":
1496
+ return new RegExp(node.pattern, node.flags);
1402
1497
  case "ThisExpression":
1403
1498
  return thisVal;
1404
1499
  case "Identifier":
@@ -1468,6 +1563,14 @@ var Interpreter = class {
1468
1563
  }
1469
1564
  case "CallExpression":
1470
1565
  return this.evalCall(node, scope, thisVal);
1566
+ case "NewExpression": {
1567
+ const ctor = this.evalExpr(node.callee, scope, thisVal);
1568
+ const args = this.evalArguments(node.arguments, scope, thisVal);
1569
+ if (typeof ctor !== "function") {
1570
+ throw new TypeError("Expression value is not a constructor");
1571
+ }
1572
+ return Reflect.construct(ctor, args);
1573
+ }
1471
1574
  case "ArrowFunction":
1472
1575
  return this.makeFunction(
1473
1576
  node,
@@ -2047,15 +2150,26 @@ function initData(el, dmeta, parentScopes) {
2047
2150
  const parentEnv = makeEnv(el);
2048
2151
  const expr = dmeta.expression.trim();
2049
2152
  let raw;
2050
- const match = expr.match(DATA_PROVIDER_RE);
2051
- if (match && getData(match[1])) {
2052
- const provider = getData(match[1]);
2053
- const args = match[2] ? parentEnv.evaluate("[" + (match[3] ?? "") + "]") : [];
2054
- raw = provider(...args);
2055
- } else if (expr === "") {
2153
+ try {
2154
+ const match = expr.match(DATA_PROVIDER_RE);
2155
+ if (match && getData(match[1])) {
2156
+ const provider = getData(match[1]);
2157
+ const args = match[2] ? parentEnv.evaluate("[" + (match[3] ?? "") + "]") : [];
2158
+ raw = provider(...args);
2159
+ } else if (expr === "") {
2160
+ raw = {};
2161
+ } else {
2162
+ raw = parentEnv.evaluate("(" + expr + ")");
2163
+ }
2164
+ } catch (err) {
2165
+ fail("E104", "s-data could not be evaluated; the component starts with empty state.", {
2166
+ el,
2167
+ expression: expr,
2168
+ doc: "s-data",
2169
+ hint: "Check the object for unsupported syntax, e.g. an async method.",
2170
+ cause: err
2171
+ });
2056
2172
  raw = {};
2057
- } else {
2058
- raw = parentEnv.evaluate("(" + expr + ")");
2059
2173
  }
2060
2174
  if (raw === null || typeof raw !== "object") raw = {};
2061
2175
  const scope = createScope(raw);
@@ -2812,6 +2926,7 @@ var sFor = (el, meta2, utils) => {
2812
2926
  blocks.delete(key);
2813
2927
  } else {
2814
2928
  const node = blueprint.cloneNode(true);
2929
+ parent.insertBefore(node, anchor2);
2815
2930
  utils.initTree(node, [...parentScopes, scope]);
2816
2931
  next.set(key, { node, scope });
2817
2932
  }