@pacem/pacem-core 1.0.6-ricci → 2.0.0-abel

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.
Files changed (40) hide show
  1. package/dist/browser/pacem-core.js +289 -161
  2. package/dist/browser/pacem-core.js.map +1 -1
  3. package/dist/browser/pacem-core.min.js +2 -2
  4. package/dist/bundle/pacem-core.min.mjs +6 -6
  5. package/dist/bundle/pacem-core.mjs +276 -104
  6. package/dist/bundle/pacem-core.mjs.map +4 -4
  7. package/dist/docs/pacem-core.json +1 -1
  8. package/dist/esm/adapter.js +1 -1
  9. package/dist/esm/binding-parser.js +259 -0
  10. package/dist/esm/command.js +1 -1
  11. package/dist/esm/comparers.js +1 -1
  12. package/dist/esm/converters.js +1 -1
  13. package/dist/esm/decorators-next.js +1 -1
  14. package/dist/esm/decorators.js +1 -1
  15. package/dist/esm/defaults.js +1 -1
  16. package/dist/esm/events.js +1 -1
  17. package/dist/esm/eventtarget.js +1 -1
  18. package/dist/esm/expression.js +36 -161
  19. package/dist/esm/index-animations.js +1 -1
  20. package/dist/esm/index-behaviors.js +1 -1
  21. package/dist/esm/index-components.js +1 -1
  22. package/dist/esm/index-iife.js +1 -1
  23. package/dist/esm/index-logging.js +1 -1
  24. package/dist/esm/index-net.js +1 -1
  25. package/dist/esm/index-root.js +1 -1
  26. package/dist/esm/index-ui.js +1 -1
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/logger.js +1 -1
  29. package/dist/esm/prefix.js +1 -1
  30. package/dist/esm/promise.js +1 -1
  31. package/dist/esm/regex.js +1 -1
  32. package/dist/esm/storage.js +1 -1
  33. package/dist/esm/template.js +1 -1
  34. package/dist/esm/transforms.js +1 -1
  35. package/dist/esm/types.js +1 -1
  36. package/dist/esm/utils-customelement.js +1 -1
  37. package/dist/esm/utils-customevent.js +1 -1
  38. package/dist/esm/utils.js +1 -1
  39. package/package.json +1 -1
  40. package/typings/index.d.ts +6 -2
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1355,11 +1355,249 @@ var root;
1355
1355
  var P = (root = window["Pacem"])?.Configuration?.prefix ?? "pacem";
1356
1356
  var PCSS = root?.Configuration?.css ?? P;
1357
1357
 
1358
+ // packages/core/dist/esm/binding-parser.js
1359
+ import { Compile } from "@pacem/pacem-foundation";
1360
+ var BINDING_CONTEXT_PREFIX = "__context_pacem";
1361
+ var HOST_REFERENCE = "__host_pacem";
1362
+ var BINDING_SYNTAX = "pacem-binding";
1363
+ var MAX_CACHED_ANALYSES = 4096;
1364
+ var INSIGNIFICANT = /* @__PURE__ */ new Set(["space", "newline", "comment", "doc-comment"]);
1365
+ var MEMBER_ACCESS = /* @__PURE__ */ new Set([".", "?."]);
1366
+ var UNSAFE_OWNERS = /* @__PURE__ */ new Set(["window", "document"]);
1367
+ function isReference(token) {
1368
+ return token.type.startsWith("binding-");
1369
+ }
1370
+ function isMemberName(token) {
1371
+ return token?.type === "identifier" || token?.type === "keyword";
1372
+ }
1373
+ function isInvocation(raw, next) {
1374
+ if (raw === "(") {
1375
+ return true;
1376
+ }
1377
+ return raw === "?." && next === "(";
1378
+ }
1379
+ var BindingParser = class _BindingParser {
1380
+ static {
1381
+ this._scanner = new Compile.Scanner();
1382
+ }
1383
+ static {
1384
+ this._cache = /* @__PURE__ */ new Map();
1385
+ }
1386
+ /** Tokenizes `expression` (cached) and detects its references and unsafe constructs. */
1387
+ static analyze(expression) {
1388
+ const text = expression ?? "";
1389
+ const cache = _BindingParser._cache;
1390
+ const cached = cache.get(text);
1391
+ if (cached != null) {
1392
+ return cached;
1393
+ }
1394
+ if (cache.size >= MAX_CACHED_ANALYSES) {
1395
+ cache.clear();
1396
+ }
1397
+ const tokens = _BindingParser._scanner.tokenize(text, BINDING_SYNTAX);
1398
+ const significant = [];
1399
+ tokens.forEach((t, i) => {
1400
+ if (!INSIGNIFICANT.has(t.type)) {
1401
+ significant.push(i);
1402
+ }
1403
+ });
1404
+ const hasReferences = tokens.some(isReference);
1405
+ const unsafe = _BindingParser._findUnsafe(tokens, significant);
1406
+ const analysis = { tokens, significant, hasReferences, unsafe };
1407
+ cache.set(text, analysis);
1408
+ return analysis;
1409
+ }
1410
+ /**
1411
+ * Finds the first unsafe construct: `new`, members of `window`/`document`, `eval(…)`. Member names (`x.window`),
1412
+ * object keys (`{ new: 1 }`) and string contents never count.
1413
+ */
1414
+ static _findUnsafe(tokens, significant) {
1415
+ for (let k = 0; k < significant.length; k++) {
1416
+ const token = tokens[significant[k]];
1417
+ const previous = tokens[significant[k - 1]];
1418
+ const next = tokens[significant[k + 1]];
1419
+ if (MEMBER_ACCESS.has(previous?.raw)) {
1420
+ continue;
1421
+ }
1422
+ const instantiation = token.type === "keyword" && token.raw === "new" && next?.raw !== ":";
1423
+ if (instantiation) {
1424
+ return "new";
1425
+ }
1426
+ if (token.type !== "identifier") {
1427
+ continue;
1428
+ }
1429
+ const accessed = next?.raw === "[" || MEMBER_ACCESS.has(next?.raw);
1430
+ if (UNSAFE_OWNERS.has(token.raw) && accessed) {
1431
+ return token.raw + next.raw;
1432
+ }
1433
+ if (token.raw === "eval" && next?.raw === "(") {
1434
+ return "eval(";
1435
+ }
1436
+ }
1437
+ return null;
1438
+ }
1439
+ /**
1440
+ * Parses a binding expression against a scope.
1441
+ * @param expression The bare expression text (no `{{ }}` delimiters, no `, twoway`/`, once` modifier).
1442
+ * @param scope What the references resolve to.
1443
+ * @throws When the expression is unsafe (see {@link analyze}).
1444
+ */
1445
+ static parse(expression, scope) {
1446
+ const analysis = _BindingParser.analyze(expression);
1447
+ if (analysis.unsafe != null) {
1448
+ throw `Unsafe expression detected: "${analysis.unsafe}" in "${expression}".`;
1449
+ }
1450
+ if (!analysis.hasReferences) {
1451
+ return { kind: "constant", functionBody: `return ${expression};`, voidBody: expression, dependencies: [] };
1452
+ }
1453
+ if (!scope.ready) {
1454
+ return { kind: "pending", dependencies: [] };
1455
+ }
1456
+ const rewriter = new BindingRewriter(analysis, scope);
1457
+ return rewriter.run();
1458
+ }
1459
+ };
1460
+ var BindingRewriter = class {
1461
+ constructor(_analysis, _scope) {
1462
+ this._analysis = _analysis;
1463
+ this._scope = _scope;
1464
+ this._dependencies = [];
1465
+ this._args = { "$this": _scope.self };
1466
+ }
1467
+ run() {
1468
+ const tokens = this._analysis.tokens;
1469
+ let expression = "";
1470
+ for (let i = 0; i < tokens.length; i++) {
1471
+ const token = tokens[i];
1472
+ expression += isReference(token) ? this._rewrite(token, i) : token.raw;
1473
+ }
1474
+ for (let key2 in this._args) {
1475
+ if (this._args[key2] == null) {
1476
+ return { kind: "pending", dependencies: [] };
1477
+ }
1478
+ }
1479
+ const dependencies = this._dependencies;
1480
+ const trimmed = expression.trim();
1481
+ if (dependencies.length === 1 && !/[^\w\.\$]+/.test(trimmed)) {
1482
+ dependencies[0].twowayAllowed = true;
1483
+ }
1484
+ return {
1485
+ kind: "resolved",
1486
+ functionBody: `try{ var ___$$$r = ${expression}; return ___$$$r; }catch(e){ }`,
1487
+ voidBody: `try{ ${expression} }catch(e){ }`,
1488
+ dependencies,
1489
+ args: this._args
1490
+ };
1491
+ }
1492
+ /**
1493
+ * The member values read directly after the token at `index` (`.a.b`, `?.a`), e.g. `['a', 'b']`.
1494
+ * As in javascript, `?.` is a member access like `.`; a member that gets invoked (`.map(`, `?.map(`, `.fn?.(`)
1495
+ * is a method, not a value, so the chain stops before it. Spaces and comments in between are skipped, as in javascript.
1496
+ */
1497
+ _memberChain(index) {
1498
+ const tokens = this._analysis.tokens;
1499
+ const names = [];
1500
+ let k = this._significantAfter(index);
1501
+ while (MEMBER_ACCESS.has(this._significantRaw(k)) && isMemberName(tokens[this._significant(k + 1)])) {
1502
+ const following = this._significantRaw(k + 2);
1503
+ const afterFollowing = this._significantRaw(k + 3);
1504
+ if (isInvocation(following, afterFollowing)) {
1505
+ return names;
1506
+ }
1507
+ const name = tokens[this._significant(k + 1)].raw;
1508
+ names.push(name);
1509
+ k += 2;
1510
+ }
1511
+ return names;
1512
+ }
1513
+ /** The position, in the significant tokens, of the first one after the token at `index`. */
1514
+ _significantAfter(index) {
1515
+ const significant = this._analysis.significant;
1516
+ let k = 0;
1517
+ while (k < significant.length && significant[k] <= index) {
1518
+ k++;
1519
+ }
1520
+ return k;
1521
+ }
1522
+ /** The token index of the `k`-th significant token, if any. */
1523
+ _significant(k) {
1524
+ return this._analysis.significant[k] ?? -1;
1525
+ }
1526
+ _significantRaw(k) {
1527
+ const index = this._significant(k);
1528
+ return this._analysis.tokens[index]?.raw;
1529
+ }
1530
+ _ensureArg(key2, factory) {
1531
+ const args = this._args;
1532
+ if (args[key2] == null) {
1533
+ args[key2] = factory();
1534
+ }
1535
+ return args[key2];
1536
+ }
1537
+ _host() {
1538
+ return this._ensureArg(HOST_REFERENCE, () => this._scope.host());
1539
+ }
1540
+ _rewrite(token, index) {
1541
+ const chain = this._memberChain(index);
1542
+ switch (token.type) {
1543
+ case "binding-host": {
1544
+ const host = this._host();
1545
+ this._addDependency(host, chain);
1546
+ return `${BINDING_CONTEXT_PREFIX}.${HOST_REFERENCE}`;
1547
+ }
1548
+ case "binding-host-property": {
1549
+ const name = token.raw.slice(2);
1550
+ const host = this._host();
1551
+ const element = host == null ? null : host[name];
1552
+ this._addDependency(element, chain);
1553
+ return `${BINDING_CONTEXT_PREFIX}.${HOST_REFERENCE}.${name}`;
1554
+ }
1555
+ case "binding-item":
1556
+ case "binding-index": {
1557
+ const upLevels = token.raw.lastIndexOf("^");
1558
+ const member = token.type === "binding-item" ? "item" : "index";
1559
+ const reference = `__item_${upLevels}_up_pacem`;
1560
+ const placeholder = this._ensureArg(reference, () => this._scope.item(upLevels));
1561
+ const memberChain = member === "item" ? [member].concat(chain) : [member];
1562
+ this._addDependency(placeholder, memberChain);
1563
+ return `${BINDING_CONTEXT_PREFIX}.${reference}.${member}`;
1564
+ }
1565
+ case "binding-element": {
1566
+ const id = token.raw.slice(1);
1567
+ const element = this._ensureArg(id, () => this._scope.element(id));
1568
+ this._addDependency(element, chain);
1569
+ return `${BINDING_CONTEXT_PREFIX}.${id}`;
1570
+ }
1571
+ default: {
1572
+ this._addDependency(this._scope.self, chain);
1573
+ return `${BINDING_CONTEXT_PREFIX}.$this`;
1574
+ }
1575
+ }
1576
+ }
1577
+ /**
1578
+ * Registers a dependency on the first member of `chain` of `element`, unless it's a method, it doesn't exist,
1579
+ * or it's already registered.
1580
+ */
1581
+ _addDependency(element, chain) {
1582
+ const property = chain[0];
1583
+ if (property == null || element == null || typeof element !== "object" && typeof element !== "function") {
1584
+ return;
1585
+ }
1586
+ if (!(property in element) || typeof element[property] === "function") {
1587
+ return;
1588
+ }
1589
+ const dependencies = this._dependencies;
1590
+ const duplicate = dependencies.some((d) => d.element === element && d.property === property);
1591
+ if (duplicate) {
1592
+ return;
1593
+ }
1594
+ const path = chain.join(".");
1595
+ dependencies.push({ element, property, path, twowayAllowed: false });
1596
+ }
1597
+ };
1598
+
1358
1599
  // packages/core/dist/esm/expression.js
1359
- var CONTEXT_PREFIX = "__context_pacem";
1360
- var stringPattern = /'(\\'|[^'])*[^\\]?'|"(\\"|[^"])*[^\\]?"/g;
1361
- var safetyPattern = /[^\w]new\s|[^\w]window\s*[\.\[]|[^\w]document\s*\.|[^\w]eval\s*\(/;
1362
- var variablePattern = /(^|[\s\(\[\+\-\*\/!])(:host\.|\^+item[\s\.,;\)\]]|\^+item$|\^+index[^\w\.]|\^+index$|\$?this[\.\s$]?|#[a-zA-Z_][\w]*|::[a-zA-Z_][\w]*)/g;
1600
+ var CONTEXT_PREFIX = BINDING_CONTEXT_PREFIX;
1363
1601
  var Expression = class _Expression {
1364
1602
  constructor() {
1365
1603
  this._pending = false;
@@ -1441,111 +1679,45 @@ var Expression = class _Expression {
1441
1679
  * Parses a binding expression's source text (the bare content of a `{{ ... }}` attribute, without the
1442
1680
  * delimiters - see {@link CustomElementUtils.extractBindingAttributeExpression}) into an {@link Expression},
1443
1681
  * resolving its special tokens against `element`'s scope (see {@link CustomElementUtils.findScopeContext}).
1444
- * Throws if the expression matches the unsafe-code guard (`new`, `window.`, `document.`, `eval(`).
1682
+ * Tokenization and rewriting are delegated to {@link BindingParser} (cached per expression text).
1683
+ * Throws if the expression uses an unsafe construct (`new`, members of `window`/`document`, `eval(`).
1445
1684
  * @param expression Bare expression source text.
1446
1685
  * @param element Element the expression is bound to; provides the scope special tokens (`:host.`, `#id`, `^item`, ...) resolve against.
1447
- * @returns A constant expression if it has no dependencies; a shared pending placeholder if `element` isn't attached to a scope yet; otherwise a fully resolved expression.
1686
+ * @returns A constant expression if it has no special tokens; a shared pending placeholder if `element` isn't attached to a scope yet; otherwise a fully resolved expression.
1448
1687
  */
1449
1688
  static parse(expression, element) {
1450
- var context = CustomElementUtils.findScopeContext(element);
1451
- var args = { "$this": element };
1452
- var dependencies = [];
1453
- var expr = "";
1454
- var independent = true;
1455
- var strings = [], stringMatches, index = 0, trunks = [];
1456
- while ((stringMatches = stringPattern.exec(expression)) != null) {
1457
- trunks.push(expression.substr(index, stringMatches.index - index));
1458
- let length = stringMatches[0].length;
1459
- strings.push({ start: stringMatches.index, length });
1460
- index = stringMatches.index + length;
1461
- }
1462
- if (index < expression.length)
1463
- trunks.push(expression.substr(index, expression.length - index));
1464
- for (var j = 0; j < trunks.length; j++) {
1465
- let trunk = trunks[j];
1466
- if (safetyPattern.test(trunk))
1467
- throw `Unsafe expression detected: "${trunk}".`;
1468
- if (context == null) {
1469
- if (new RegExp(variablePattern).test(trunk) === true) {
1470
- independent = false;
1471
- break;
1472
- }
1473
- } else {
1474
- let processedTrunk = trunk.replace(variablePattern, (mwhole, mstart, melem, index2, input) => {
1475
- independent = false;
1476
- const cmpRef = `__host_pacem`;
1477
- let retval = CONTEXT_PREFIX + ".$this" + (melem.endsWith(".") ? "." : ""), el = element, dotIndex = input.indexOf(".", index2), furtherParts = dotIndex > index2 ? input.substr(dotIndex + 1).trim() : "", pathRegexArray = /^[\w\.\$]+/.exec(furtherParts), path = pathRegexArray && pathRegexArray.length > 0 && pathRegexArray[0], propArray, prop = path && (propArray = path.split(".")).length > 0 && propArray[0];
1478
- if (melem === ":host.") {
1479
- el = args[cmpRef] = args[cmpRef] || CustomElementUtils.findHostContext(element);
1480
- retval = `${CONTEXT_PREFIX}.${cmpRef}.`;
1481
- } else if (melem.endsWith("^item") || melem.substr(0, melem.length - 1).endsWith("^item")) {
1482
- let lastChar = melem.endsWith("^item") ? "" : melem.substr(melem.length - 1, 1);
1483
- let upLevels = melem.length - (lastChar.length > 0 ? 6 : 5);
1484
- let itemRef = `__item_${upLevels}_up_pacem`;
1485
- let item = Repeater.findItemContext(element, upLevels);
1486
- el = args[itemRef] = args[itemRef] || item && item.placeholder;
1487
- path = "item" + (path ? "." + path : "");
1488
- prop = "item";
1489
- retval = `${CONTEXT_PREFIX}.${itemRef}.item${lastChar}`;
1490
- } else if (melem.endsWith("^index") || melem.substr(0, melem.length - 1).endsWith("^index")) {
1491
- let lastChar = melem.endsWith("^index") ? "" : melem.substr(melem.length - 1, 1);
1492
- let upLevels = melem.length - (lastChar.length > 0 ? 7 : 6);
1493
- let itemRef = `__item_${upLevels}_up_pacem`;
1494
- let item = Repeater.findItemContext(element, upLevels);
1495
- el = args[itemRef] = args[itemRef] || item && item.placeholder;
1496
- path = "index";
1497
- prop = "index";
1498
- retval = `${CONTEXT_PREFIX}.${itemRef}.index${lastChar}`;
1499
- } else if (melem.startsWith("::")) {
1500
- let melem0 = melem.substr(2, melem.length - 2);
1501
- let host = args[cmpRef] = args[cmpRef] || CustomElementUtils.findHostContext(element);
1502
- if (!Utils.isNull(host)) {
1503
- el = host[melem0];
1504
- } else {
1505
- el = null;
1506
- }
1507
- retval = `${CONTEXT_PREFIX}.${cmpRef}.${melem0}`;
1508
- } else if (melem.startsWith("#")) {
1509
- let melem0 = melem.substr(1, melem.length - 1);
1510
- el = args[melem0] = args[melem0] || context.querySelector("#" + melem0);
1511
- retval = `${CONTEXT_PREFIX}.${melem0}`;
1512
- }
1513
- if (prop && el && prop in el && typeof el[prop] !== "function" && dependencies.find((d) => d.element === el && d.property == prop) == null) {
1514
- dependencies.push({ element: el, property: prop, path, twowayAllowed: false });
1515
- }
1516
- return mstart + retval;
1517
- });
1518
- expr += processedTrunk;
1519
- if (j < strings.length) {
1520
- const s = strings[j];
1521
- expr += expression.substr(s.start, s.length);
1522
- }
1523
- }
1524
- }
1525
- if (independent) {
1526
- var constexpr = new _Expression();
1527
- constexpr._independent = true;
1528
- constexpr._fnBody = `return ${expression};`;
1529
- constexpr._voidBody = expression;
1530
- constexpr._dependencies = [];
1531
- return constexpr;
1532
- }
1533
- if (context == null) {
1534
- return this._getPendingExpressionSingleton();
1535
- }
1536
- for (var elem in args) {
1537
- if (args[elem] == null)
1538
- return _Expression._getPendingExpressionSingleton();
1689
+ const scope = _Expression._scopeOf(element);
1690
+ const parsed = BindingParser.parse(expression, scope);
1691
+ return _Expression._fromParsed(parsed);
1692
+ }
1693
+ /** The DOM surroundings of `element` the special tokens resolve against (lazily, except for the scope context). */
1694
+ static _scopeOf(element) {
1695
+ const context = CustomElementUtils.findScopeContext(element);
1696
+ return {
1697
+ ready: context != null,
1698
+ self: element,
1699
+ host: () => CustomElementUtils.findHostContext(element),
1700
+ item: (upLevels) => {
1701
+ const item = Repeater.findItemContext(element, upLevels);
1702
+ return item?.placeholder;
1703
+ },
1704
+ element: (id) => context.querySelector("#" + id)
1705
+ };
1706
+ }
1707
+ static _fromParsed(parsed) {
1708
+ if (parsed.kind === "pending") {
1709
+ return _Expression._getPendingExpressionSingleton();
1539
1710
  }
1540
- if (dependencies.length == 1 && /[^\w\.\$]+/.test(expr.trim()) !== true) {
1541
- dependencies[0].twowayAllowed = true;
1711
+ const retval = new _Expression();
1712
+ retval._fnBody = parsed.functionBody;
1713
+ retval._voidBody = parsed.voidBody;
1714
+ retval._dependencies = parsed.dependencies;
1715
+ if (parsed.kind === "constant") {
1716
+ retval._independent = true;
1717
+ return retval;
1542
1718
  }
1543
- var retexpr = new _Expression();
1544
- retexpr._args = args;
1545
- retexpr._fnBody = `try{ var ___$$$r = ${expr}; return ___$$$r; }catch(e){ }`;
1546
- retexpr._voidBody = `try{ ${expr} }catch(e){ }`;
1547
- retexpr._dependencies = dependencies;
1548
- return retexpr;
1719
+ retval._args = parsed.args;
1720
+ return retval;
1549
1721
  }
1550
1722
  };
1551
1723