@hyperframes/parsers 0.7.24 → 0.7.26

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.
@@ -777,6 +777,49 @@ var SCOPE_NODE_TYPES = /* @__PURE__ */ new Set([
777
777
  "FunctionExpression",
778
778
  "ArrowFunctionExpression"
779
779
  ]);
780
+ var CONST_NODES = /* @__PURE__ */ Symbol("hf.constNodes");
781
+ function constNodesOf(scope) {
782
+ return scope[CONST_NODES];
783
+ }
784
+ var MATH_FNS = /* @__PURE__ */ new Set(["min", "max", "round", "floor", "ceil", "abs", "sqrt", "sign", "trunc"]);
785
+ var MATH_CONSTS = { PI: Math.PI, E: Math.E, SQRT2: Math.SQRT2 };
786
+ function resolveMemberNode(node, scope) {
787
+ if (node.object?.type === "Identifier" && node.object.name === "Math") {
788
+ const key = node.property?.name;
789
+ return typeof key === "string" ? MATH_CONSTS[key] : void 0;
790
+ }
791
+ const objNode = resolveConstNode(node.object, scope);
792
+ if (!objNode) return void 0;
793
+ let valueNode;
794
+ if (node.computed) {
795
+ const idx = resolveNode(node.property, scope);
796
+ if (objNode.type === "ArrayExpression" && typeof idx === "number") {
797
+ valueNode = objNode.elements?.[idx];
798
+ } else if (objNode.type === "ObjectExpression" && (typeof idx === "string" || typeof idx === "number")) {
799
+ valueNode = findPropertyNode(objNode, String(idx));
800
+ }
801
+ } else if (objNode.type === "ObjectExpression") {
802
+ valueNode = findPropertyNode(objNode, node.property?.name ?? node.property?.value);
803
+ }
804
+ return valueNode ? resolveNode(valueNode, scope) : void 0;
805
+ }
806
+ function resolveConstMember(objNode, node, scope) {
807
+ if (!node.computed) {
808
+ return objNode.type === "ObjectExpression" ? findPropertyNode(objNode, node.property?.name ?? node.property?.value) : void 0;
809
+ }
810
+ const idx = resolveNode(node.property, scope);
811
+ if (objNode.type === "ArrayExpression" && typeof idx === "number") return objNode.elements?.[idx];
812
+ if (objNode.type === "ObjectExpression") return findPropertyNode(objNode, String(idx));
813
+ return void 0;
814
+ }
815
+ function resolveConstNode(node, scope) {
816
+ if (!node) return void 0;
817
+ if (node.type === "ArrayExpression" || node.type === "ObjectExpression") return node;
818
+ if (node.type === "Identifier") return constNodesOf(scope)?.get(node.name);
819
+ if (node.type !== "MemberExpression") return void 0;
820
+ const objNode = resolveConstNode(node.object, scope);
821
+ return objNode ? resolveConstMember(objNode, node, scope) : void 0;
822
+ }
780
823
  function resolveNode(node, scope) {
781
824
  if (!node) return void 0;
782
825
  if (node.type === "NumericLiteral" || node.type === "Literal" && typeof node.value === "number")
@@ -813,6 +856,15 @@ function resolveNode(node, scope) {
813
856
  if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
814
857
  return node.quasis?.[0]?.value?.cooked ?? void 0;
815
858
  }
859
+ if (node.type === "MemberExpression") {
860
+ return resolveMemberNode(node, scope);
861
+ }
862
+ if (node.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.object?.type === "Identifier" && node.callee.object.name === "Math" && MATH_FNS.has(node.callee.property?.name)) {
863
+ const args = (node.arguments ?? []).map((a) => resolveNode(a, scope));
864
+ if (args.every((a) => typeof a === "number")) {
865
+ return Math[node.callee.property.name](...args);
866
+ }
867
+ }
816
868
  return void 0;
817
869
  }
818
870
  function extractLiteralValue(node, scope) {
@@ -870,14 +922,19 @@ function resolveCollectionSelector(node, ancestors, scope, bindings) {
870
922
  }
871
923
  function collectScopeBindings(ast) {
872
924
  const bindings = /* @__PURE__ */ new Map();
925
+ const constNodes = /* @__PURE__ */ new Map();
926
+ Object.defineProperty(bindings, CONST_NODES, { value: constNodes, enumerable: false });
873
927
  acornWalk.simple(ast, {
874
928
  VariableDeclarator(node) {
875
929
  const name = node.id?.name;
876
930
  const init = node.init;
877
- if (name && init) {
878
- const val = resolveNode(init, bindings);
879
- if (val !== void 0) bindings.set(name, val);
931
+ if (!name || !init) return;
932
+ if (init.type === "ArrayExpression" || init.type === "ObjectExpression") {
933
+ constNodes.set(name, init);
934
+ return;
880
935
  }
936
+ const val = resolveNode(init, bindings);
937
+ if (val !== void 0) bindings.set(name, val);
881
938
  }
882
939
  });
883
940
  return bindings;
@@ -919,6 +976,25 @@ function collectTargetBindings(ast, scope) {
919
976
  }
920
977
  }
921
978
  });
979
+ const COLLECTION_ALIAS_METHODS = /* @__PURE__ */ new Set(["slice", "filter", "concat", "reverse"]);
980
+ acornWalk.ancestor(ast, {
981
+ // fallow-ignore-next-line complexity
982
+ VariableDeclarator(node, _, ancestors) {
983
+ const name = node.id?.name;
984
+ const init = node.init;
985
+ if (!name || !init) return;
986
+ let sourceVar;
987
+ if (init.type === "MemberExpression" && init.object?.type === "Identifier") {
988
+ sourceVar = init.object.name;
989
+ } else if (init.type === "CallExpression" && init.callee?.type === "MemberExpression" && init.callee.object?.type === "Identifier" && init.callee.property?.type === "Identifier" && COLLECTION_ALIAS_METHODS.has(init.callee.property.name)) {
990
+ sourceVar = init.callee.object.name;
991
+ }
992
+ if (!sourceVar) return;
993
+ const selector = lookupBindingFromAncestors(sourceVar, ancestors, bindings);
994
+ if (selector)
995
+ addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), name, selector);
996
+ }
997
+ });
922
998
  return bindings;
923
999
  }
924
1000
  function resolveTargetSelector(node, ancestors, scope, bindings) {
@@ -941,6 +1017,32 @@ function resolveTargetSelector(node, ancestors, scope, bindings) {
941
1017
  }
942
1018
  return null;
943
1019
  }
1020
+ function describeProxyTarget(targetNode, varsNode, scope) {
1021
+ const objNode = targetNode?.type === "ObjectExpression" ? targetNode : targetNode?.type === "Identifier" ? resolveConstNode(targetNode, scope) : void 0;
1022
+ if (objNode?.type !== "ObjectExpression") return null;
1023
+ const onUpdate = findPropertyNode(varsNode, "onUpdate");
1024
+ const driven = onUpdate ? drivenDomChannel(onUpdate) : void 0;
1025
+ if (driven) return `proxy \u2192 ${driven}`;
1026
+ return "dwell/hold";
1027
+ }
1028
+ function isStyleAssignmentTarget(left) {
1029
+ return left?.type === "MemberExpression" && left.object?.type === "MemberExpression" && left.object.property?.name === "style" && !!left.property?.name;
1030
+ }
1031
+ function drivenDomChannel(fnNode) {
1032
+ let found;
1033
+ acornWalk.simple(fnNode, {
1034
+ CallExpression(node) {
1035
+ if (node.callee?.type === "MemberExpression" && node.callee.property?.name === "setAttribute" && typeof node.arguments?.[0]?.value === "string") {
1036
+ found ??= node.arguments[0].value;
1037
+ }
1038
+ },
1039
+ AssignmentExpression(node) {
1040
+ const left = node.left;
1041
+ if (isStyleAssignmentTarget(left)) found ??= `style.${left.property.name}`;
1042
+ }
1043
+ });
1044
+ return found;
1045
+ }
944
1046
  function isObjectProperty(prop) {
945
1047
  return prop?.type === "ObjectProperty" || prop?.type === "Property";
946
1048
  }
@@ -1409,8 +1511,13 @@ function tweenCallToAnimation(call, scope, source) {
1409
1511
  if (duration === void 0 && keyframesData) {
1410
1512
  duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
1411
1513
  }
1514
+ let selector = call.selector;
1515
+ if (selector === "__unresolved__") {
1516
+ const proxyLabel = describeProxyTarget(call.node.arguments?.[0], call.varsArg, scope);
1517
+ if (proxyLabel) selector = proxyLabel;
1518
+ }
1412
1519
  const anim = {
1413
- targetSelector: call.selector,
1520
+ targetSelector: selector,
1414
1521
  method: call.method,
1415
1522
  position,
1416
1523
  properties,
@@ -1433,11 +1540,52 @@ function tweenCallToAnimation(call, scope, source) {
1433
1540
  if (keyframesData) anim.keyframes = keyframesData;
1434
1541
  if (motionPathResult) anim.arcPath = motionPathResult.arcPath;
1435
1542
  if (hasUnresolvedKeyframes) anim.hasUnresolvedKeyframes = true;
1436
- if (call.selector === "__unresolved__") anim.hasUnresolvedSelector = true;
1543
+ if (selector === "__unresolved__") anim.hasUnresolvedSelector = true;
1437
1544
  const provenance = readProvenance(call.node);
1438
1545
  if (provenance) anim.provenance = provenance;
1439
1546
  return anim;
1440
1547
  }
1548
+ function staggerAmount(raw) {
1549
+ if (typeof raw === "number") return raw;
1550
+ if (typeof raw !== "string") return void 0;
1551
+ const src = raw.startsWith("__raw:") ? raw.slice(6) : raw;
1552
+ const m = /(?:each\s*:\s*)?(-?\d+(?:\.\d+)?)/.exec(src);
1553
+ if (!m) return void 0;
1554
+ const n = Number.parseFloat(m[1]);
1555
+ return Number.isFinite(n) ? n : void 0;
1556
+ }
1557
+ function restValue(prop) {
1558
+ return prop === "opacity" || prop.startsWith("scale") ? 1 : 0;
1559
+ }
1560
+ function staggeredKeyframes(anim, each) {
1561
+ const vars = { ...anim.properties };
1562
+ let from;
1563
+ let to;
1564
+ if (anim.method === "fromTo") {
1565
+ from = { ...anim.fromProperties ?? {} };
1566
+ to = vars;
1567
+ } else if (anim.method === "from") {
1568
+ from = vars;
1569
+ to = {};
1570
+ for (const k of Object.keys(vars)) to[k] = restValue(k);
1571
+ } else {
1572
+ from = { ...anim.fromProperties ?? {} };
1573
+ for (const k of Object.keys(vars)) if (from[k] === void 0) from[k] = restValue(k);
1574
+ to = vars;
1575
+ }
1576
+ return [
1577
+ { percentage: 0, properties: { ...from, stagger: each } },
1578
+ { percentage: 100, properties: { ...to, stagger: each } }
1579
+ ];
1580
+ }
1581
+ function annotateStaggeredCollections(anims) {
1582
+ for (const anim of anims) {
1583
+ if (anim.keyframes || anim.arcPath) continue;
1584
+ const each = staggerAmount(anim.extras?.stagger);
1585
+ if (each === void 0) continue;
1586
+ anim.keyframes = { format: "percentage", keyframes: staggeredKeyframes(anim, each) };
1587
+ }
1588
+ }
1441
1589
  var GSAP_DEFAULT_DURATION = 0.5;
1442
1590
  function resolvePositionString(pos, cursor, prevStart) {
1443
1591
  const trimmed = pos.trim();
@@ -1463,6 +1611,55 @@ function resolvePositionString(pos, cursor, prevStart) {
1463
1611
  const n = Number.parseFloat(trimmed);
1464
1612
  return Number.isFinite(n) ? n : null;
1465
1613
  }
1614
+ function collectGsapSetStates(ast, scope, bindings, source) {
1615
+ const states = /* @__PURE__ */ new Map();
1616
+ acornWalk.ancestor(ast, {
1617
+ // fallow-ignore-next-line complexity
1618
+ CallExpression(node, _, ancestors) {
1619
+ const callee = node.callee;
1620
+ if (callee?.type !== "MemberExpression" || callee.object?.name !== "gsap" || callee.property?.name !== "set")
1621
+ return;
1622
+ const selector = resolveTargetSelector(node.arguments?.[0], ancestors, scope, bindings);
1623
+ if (!selector) return;
1624
+ const rec = objectExpressionToRecord(node.arguments?.[1], scope, source);
1625
+ const props = states.get(selector) ?? {};
1626
+ for (const [k, v] of Object.entries(rec)) {
1627
+ if (typeof v === "number" || typeof v === "string") props[k] = v;
1628
+ }
1629
+ states.set(selector, props);
1630
+ }
1631
+ });
1632
+ return states;
1633
+ }
1634
+ function mergeProps(target, props) {
1635
+ for (const [k, v] of Object.entries(props)) target[k] = v;
1636
+ return target;
1637
+ }
1638
+ function seedFromPreState(anim, cur) {
1639
+ const from = { ...anim.fromProperties ?? {} };
1640
+ let seeded = false;
1641
+ for (const prop of Object.keys(anim.properties)) {
1642
+ if (from[prop] === void 0 && cur[prop] !== void 0) {
1643
+ from[prop] = cur[prop];
1644
+ seeded = true;
1645
+ }
1646
+ }
1647
+ if (seeded) anim.fromProperties = from;
1648
+ }
1649
+ function seedSetStates(anims, initial) {
1650
+ const state = /* @__PURE__ */ new Map();
1651
+ for (const [sel, props] of initial) state.set(sel, { ...props });
1652
+ for (const anim of anims) {
1653
+ const sel = anim.targetSelector;
1654
+ if (anim.method === "set") {
1655
+ state.set(sel, mergeProps(state.get(sel) ?? {}, anim.properties));
1656
+ continue;
1657
+ }
1658
+ const cur = state.get(sel);
1659
+ if (anim.method === "to" && cur) seedFromPreState(anim, cur);
1660
+ state.set(sel, mergeProps(state.get(sel) ?? {}, anim.properties));
1661
+ }
1662
+ }
1466
1663
  function applyTimelineDefaults(anims, defaults) {
1467
1664
  if (!defaults) return;
1468
1665
  for (const anim of anims) {
@@ -1475,31 +1672,89 @@ function applyTimelineDefaults(anims, defaults) {
1475
1672
  }
1476
1673
  }
1477
1674
  }
1478
- function resolveTimelinePositions(anims) {
1675
+ function resolveLabelPosition(pos, labels, cursor) {
1676
+ const m = /^([A-Za-z_$][\w$]*)\s*(?:([+-])=\s*([\d.]+))?$/.exec(pos.trim());
1677
+ if (!m) return null;
1678
+ const name = m[1];
1679
+ let base = labels.get(name);
1680
+ if (base === void 0) {
1681
+ base = cursor;
1682
+ labels.set(name, base);
1683
+ }
1684
+ if (m[2] && m[3]) {
1685
+ const n = Number.parseFloat(m[3]);
1686
+ if (Number.isFinite(n)) return m[2] === "+" ? base + n : base - n;
1687
+ }
1688
+ return base;
1689
+ }
1690
+ function resolveAnimStart(anim, cursor, prevStart, labels) {
1691
+ if (anim.implicitPosition) return cursor;
1692
+ if (typeof anim.position === "number") return anim.position;
1693
+ if (typeof anim.position === "string") {
1694
+ return resolveLabelPosition(anim.position, labels, cursor) ?? resolvePositionString(anim.position, cursor, prevStart);
1695
+ }
1696
+ return cursor;
1697
+ }
1698
+ function resolveTimelinePositions(anims, labelDefs = []) {
1479
1699
  let cursor = 0;
1480
1700
  let prevStart = 0;
1481
- for (const anim of anims) {
1701
+ const labels = /* @__PURE__ */ new Map();
1702
+ let labelIdx = 0;
1703
+ const sortedLabels = [...labelDefs].sort((a, b) => a.order - b.order);
1704
+ const defineLabel = (def) => {
1705
+ let value;
1706
+ if (typeof def.position === "number") value = def.position;
1707
+ else if (typeof def.position === "string") {
1708
+ value = resolveLabelPosition(def.position, labels, cursor) ?? cursor;
1709
+ } else value = cursor;
1710
+ labels.set(def.name, Math.max(0, value));
1711
+ };
1712
+ anims.forEach((anim, i) => {
1713
+ while (labelIdx < sortedLabels.length && sortedLabels[labelIdx].order <= i) {
1714
+ defineLabel(sortedLabels[labelIdx]);
1715
+ labelIdx++;
1716
+ }
1482
1717
  if (anim.method === "set" && anim.global) {
1483
1718
  anim.resolvedStart = 0;
1484
- continue;
1719
+ return;
1485
1720
  }
1486
1721
  const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION;
1487
- let start;
1488
- if (anim.implicitPosition) {
1489
- start = cursor;
1490
- } else if (typeof anim.position === "number") {
1491
- start = anim.position;
1492
- } else if (typeof anim.position === "string") {
1493
- start = resolvePositionString(anim.position, cursor, prevStart);
1494
- } else {
1495
- start = cursor;
1496
- }
1722
+ const start = resolveAnimStart(anim, cursor, prevStart, labels);
1497
1723
  if (start != null) {
1498
1724
  anim.resolvedStart = Math.max(0, start);
1499
1725
  prevStart = anim.resolvedStart;
1500
1726
  cursor = Math.max(cursor, anim.resolvedStart + duration);
1501
1727
  }
1502
- }
1728
+ });
1729
+ while (labelIdx < sortedLabels.length) defineLabel(sortedLabels[labelIdx++]);
1730
+ }
1731
+ function collectAddLabelDefs(ast, ref, scope, sortedCalls) {
1732
+ const callLocs = sortedCalls.map((c) => c.node.callee?.property?.loc?.start);
1733
+ const defs = [];
1734
+ acornWalk.simple(ast, {
1735
+ // fallow-ignore-next-line complexity
1736
+ CallExpression(node) {
1737
+ const callee = node.callee;
1738
+ const objMatches = ref.kind === "identifier" ? callee.object?.type === "Identifier" && callee.object.name === ref.name : sameMemberAccess(callee.object, ref.node);
1739
+ if (callee?.type !== "MemberExpression" || !objMatches || callee.property?.name !== "addLabel")
1740
+ return;
1741
+ const nameNode = node.arguments?.[0];
1742
+ const name = typeof nameNode?.value === "string" ? nameNode.value : void 0;
1743
+ if (!name) return;
1744
+ const posVal = resolveNode(node.arguments?.[1], scope);
1745
+ const position = typeof posVal === "number" || typeof posVal === "string" ? posVal : void 0;
1746
+ const labelLoc = callee.property?.loc?.start;
1747
+ let order = sortedCalls.length;
1748
+ if (labelLoc) {
1749
+ order = callLocs.findIndex(
1750
+ (l) => l && (l.line > labelLoc.line || l.line === labelLoc.line && l.column > labelLoc.column)
1751
+ );
1752
+ if (order === -1) order = sortedCalls.length;
1753
+ }
1754
+ defs.push({ name, position, order });
1755
+ }
1756
+ });
1757
+ return defs;
1503
1758
  }
1504
1759
  function compareByLoc(a, b) {
1505
1760
  const aLoc = a.node.callee?.property?.loc?.start;
@@ -1552,7 +1807,10 @@ function parseGsapScriptAcorn(script) {
1552
1807
  sortBySourcePosition(calls);
1553
1808
  const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
1554
1809
  applyTimelineDefaults(rawAnims, detection.defaults);
1555
- resolveTimelinePositions(rawAnims);
1810
+ seedSetStates(rawAnims, collectGsapSetStates(ast, scope, targetBindings, script));
1811
+ const labelDefs = collectAddLabelDefs(ast, ref, scope, calls);
1812
+ resolveTimelinePositions(rawAnims, labelDefs);
1813
+ annotateStaggeredCollections(rawAnims);
1556
1814
  const animations = assignStableIds(rawAnims);
1557
1815
  const declPattern = ref.kind === "identifier" ? `(?:const|let|var)\\s+${timelineVar}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?` : `${escapeRegExp(timelineVar)}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?`;
1558
1816
  const timelineMatch = script.match(new RegExp(`^[\\s\\S]*?${declPattern}`));