@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.
@@ -462,6 +462,49 @@ var SCOPE_NODE_TYPES = /* @__PURE__ */ new Set([
462
462
  "FunctionExpression",
463
463
  "ArrowFunctionExpression"
464
464
  ]);
465
+ var CONST_NODES = /* @__PURE__ */ Symbol("hf.constNodes");
466
+ function constNodesOf(scope) {
467
+ return scope[CONST_NODES];
468
+ }
469
+ var MATH_FNS = /* @__PURE__ */ new Set(["min", "max", "round", "floor", "ceil", "abs", "sqrt", "sign", "trunc"]);
470
+ var MATH_CONSTS = { PI: Math.PI, E: Math.E, SQRT2: Math.SQRT2 };
471
+ function resolveMemberNode(node, scope) {
472
+ if (node.object?.type === "Identifier" && node.object.name === "Math") {
473
+ const key = node.property?.name;
474
+ return typeof key === "string" ? MATH_CONSTS[key] : void 0;
475
+ }
476
+ const objNode = resolveConstNode(node.object, scope);
477
+ if (!objNode) return void 0;
478
+ let valueNode;
479
+ if (node.computed) {
480
+ const idx = resolveNode(node.property, scope);
481
+ if (objNode.type === "ArrayExpression" && typeof idx === "number") {
482
+ valueNode = objNode.elements?.[idx];
483
+ } else if (objNode.type === "ObjectExpression" && (typeof idx === "string" || typeof idx === "number")) {
484
+ valueNode = findPropertyNode(objNode, String(idx));
485
+ }
486
+ } else if (objNode.type === "ObjectExpression") {
487
+ valueNode = findPropertyNode(objNode, node.property?.name ?? node.property?.value);
488
+ }
489
+ return valueNode ? resolveNode(valueNode, scope) : void 0;
490
+ }
491
+ function resolveConstMember(objNode, node, scope) {
492
+ if (!node.computed) {
493
+ return objNode.type === "ObjectExpression" ? findPropertyNode(objNode, node.property?.name ?? node.property?.value) : void 0;
494
+ }
495
+ const idx = resolveNode(node.property, scope);
496
+ if (objNode.type === "ArrayExpression" && typeof idx === "number") return objNode.elements?.[idx];
497
+ if (objNode.type === "ObjectExpression") return findPropertyNode(objNode, String(idx));
498
+ return void 0;
499
+ }
500
+ function resolveConstNode(node, scope) {
501
+ if (!node) return void 0;
502
+ if (node.type === "ArrayExpression" || node.type === "ObjectExpression") return node;
503
+ if (node.type === "Identifier") return constNodesOf(scope)?.get(node.name);
504
+ if (node.type !== "MemberExpression") return void 0;
505
+ const objNode = resolveConstNode(node.object, scope);
506
+ return objNode ? resolveConstMember(objNode, node, scope) : void 0;
507
+ }
465
508
  function resolveNode(node, scope) {
466
509
  if (!node) return void 0;
467
510
  if (node.type === "NumericLiteral" || node.type === "Literal" && typeof node.value === "number")
@@ -498,6 +541,15 @@ function resolveNode(node, scope) {
498
541
  if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
499
542
  return node.quasis?.[0]?.value?.cooked ?? void 0;
500
543
  }
544
+ if (node.type === "MemberExpression") {
545
+ return resolveMemberNode(node, scope);
546
+ }
547
+ 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)) {
548
+ const args = (node.arguments ?? []).map((a) => resolveNode(a, scope));
549
+ if (args.every((a) => typeof a === "number")) {
550
+ return Math[node.callee.property.name](...args);
551
+ }
552
+ }
501
553
  return void 0;
502
554
  }
503
555
  function extractLiteralValue(node, scope) {
@@ -555,14 +607,19 @@ function resolveCollectionSelector(node, ancestors, scope, bindings) {
555
607
  }
556
608
  function collectScopeBindings(ast) {
557
609
  const bindings = /* @__PURE__ */ new Map();
610
+ const constNodes = /* @__PURE__ */ new Map();
611
+ Object.defineProperty(bindings, CONST_NODES, { value: constNodes, enumerable: false });
558
612
  acornWalk.simple(ast, {
559
613
  VariableDeclarator(node) {
560
614
  const name = node.id?.name;
561
615
  const init = node.init;
562
- if (name && init) {
563
- const val = resolveNode(init, bindings);
564
- if (val !== void 0) bindings.set(name, val);
616
+ if (!name || !init) return;
617
+ if (init.type === "ArrayExpression" || init.type === "ObjectExpression") {
618
+ constNodes.set(name, init);
619
+ return;
565
620
  }
621
+ const val = resolveNode(init, bindings);
622
+ if (val !== void 0) bindings.set(name, val);
566
623
  }
567
624
  });
568
625
  return bindings;
@@ -604,6 +661,25 @@ function collectTargetBindings(ast, scope) {
604
661
  }
605
662
  }
606
663
  });
664
+ const COLLECTION_ALIAS_METHODS = /* @__PURE__ */ new Set(["slice", "filter", "concat", "reverse"]);
665
+ acornWalk.ancestor(ast, {
666
+ // fallow-ignore-next-line complexity
667
+ VariableDeclarator(node, _, ancestors) {
668
+ const name = node.id?.name;
669
+ const init = node.init;
670
+ if (!name || !init) return;
671
+ let sourceVar;
672
+ if (init.type === "MemberExpression" && init.object?.type === "Identifier") {
673
+ sourceVar = init.object.name;
674
+ } 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)) {
675
+ sourceVar = init.callee.object.name;
676
+ }
677
+ if (!sourceVar) return;
678
+ const selector = lookupBindingFromAncestors(sourceVar, ancestors, bindings);
679
+ if (selector)
680
+ addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), name, selector);
681
+ }
682
+ });
607
683
  return bindings;
608
684
  }
609
685
  function resolveTargetSelector(node, ancestors, scope, bindings) {
@@ -626,6 +702,32 @@ function resolveTargetSelector(node, ancestors, scope, bindings) {
626
702
  }
627
703
  return null;
628
704
  }
705
+ function describeProxyTarget(targetNode, varsNode, scope) {
706
+ const objNode = targetNode?.type === "ObjectExpression" ? targetNode : targetNode?.type === "Identifier" ? resolveConstNode(targetNode, scope) : void 0;
707
+ if (objNode?.type !== "ObjectExpression") return null;
708
+ const onUpdate = findPropertyNode(varsNode, "onUpdate");
709
+ const driven = onUpdate ? drivenDomChannel(onUpdate) : void 0;
710
+ if (driven) return `proxy \u2192 ${driven}`;
711
+ return "dwell/hold";
712
+ }
713
+ function isStyleAssignmentTarget(left) {
714
+ return left?.type === "MemberExpression" && left.object?.type === "MemberExpression" && left.object.property?.name === "style" && !!left.property?.name;
715
+ }
716
+ function drivenDomChannel(fnNode) {
717
+ let found;
718
+ acornWalk.simple(fnNode, {
719
+ CallExpression(node) {
720
+ if (node.callee?.type === "MemberExpression" && node.callee.property?.name === "setAttribute" && typeof node.arguments?.[0]?.value === "string") {
721
+ found ??= node.arguments[0].value;
722
+ }
723
+ },
724
+ AssignmentExpression(node) {
725
+ const left = node.left;
726
+ if (isStyleAssignmentTarget(left)) found ??= `style.${left.property.name}`;
727
+ }
728
+ });
729
+ return found;
730
+ }
629
731
  function isObjectProperty(prop) {
630
732
  return prop?.type === "ObjectProperty" || prop?.type === "Property";
631
733
  }
@@ -1094,8 +1196,13 @@ function tweenCallToAnimation(call, scope, source) {
1094
1196
  if (duration === void 0 && keyframesData) {
1095
1197
  duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
1096
1198
  }
1199
+ let selector = call.selector;
1200
+ if (selector === "__unresolved__") {
1201
+ const proxyLabel = describeProxyTarget(call.node.arguments?.[0], call.varsArg, scope);
1202
+ if (proxyLabel) selector = proxyLabel;
1203
+ }
1097
1204
  const anim = {
1098
- targetSelector: call.selector,
1205
+ targetSelector: selector,
1099
1206
  method: call.method,
1100
1207
  position,
1101
1208
  properties,
@@ -1118,11 +1225,52 @@ function tweenCallToAnimation(call, scope, source) {
1118
1225
  if (keyframesData) anim.keyframes = keyframesData;
1119
1226
  if (motionPathResult) anim.arcPath = motionPathResult.arcPath;
1120
1227
  if (hasUnresolvedKeyframes) anim.hasUnresolvedKeyframes = true;
1121
- if (call.selector === "__unresolved__") anim.hasUnresolvedSelector = true;
1228
+ if (selector === "__unresolved__") anim.hasUnresolvedSelector = true;
1122
1229
  const provenance = readProvenance(call.node);
1123
1230
  if (provenance) anim.provenance = provenance;
1124
1231
  return anim;
1125
1232
  }
1233
+ function staggerAmount(raw) {
1234
+ if (typeof raw === "number") return raw;
1235
+ if (typeof raw !== "string") return void 0;
1236
+ const src = raw.startsWith("__raw:") ? raw.slice(6) : raw;
1237
+ const m = /(?:each\s*:\s*)?(-?\d+(?:\.\d+)?)/.exec(src);
1238
+ if (!m) return void 0;
1239
+ const n = Number.parseFloat(m[1]);
1240
+ return Number.isFinite(n) ? n : void 0;
1241
+ }
1242
+ function restValue(prop) {
1243
+ return prop === "opacity" || prop.startsWith("scale") ? 1 : 0;
1244
+ }
1245
+ function staggeredKeyframes(anim, each) {
1246
+ const vars = { ...anim.properties };
1247
+ let from;
1248
+ let to;
1249
+ if (anim.method === "fromTo") {
1250
+ from = { ...anim.fromProperties ?? {} };
1251
+ to = vars;
1252
+ } else if (anim.method === "from") {
1253
+ from = vars;
1254
+ to = {};
1255
+ for (const k of Object.keys(vars)) to[k] = restValue(k);
1256
+ } else {
1257
+ from = { ...anim.fromProperties ?? {} };
1258
+ for (const k of Object.keys(vars)) if (from[k] === void 0) from[k] = restValue(k);
1259
+ to = vars;
1260
+ }
1261
+ return [
1262
+ { percentage: 0, properties: { ...from, stagger: each } },
1263
+ { percentage: 100, properties: { ...to, stagger: each } }
1264
+ ];
1265
+ }
1266
+ function annotateStaggeredCollections(anims) {
1267
+ for (const anim of anims) {
1268
+ if (anim.keyframes || anim.arcPath) continue;
1269
+ const each = staggerAmount(anim.extras?.stagger);
1270
+ if (each === void 0) continue;
1271
+ anim.keyframes = { format: "percentage", keyframes: staggeredKeyframes(anim, each) };
1272
+ }
1273
+ }
1126
1274
  var GSAP_DEFAULT_DURATION = 0.5;
1127
1275
  function resolvePositionString(pos, cursor, prevStart) {
1128
1276
  const trimmed = pos.trim();
@@ -1148,6 +1296,55 @@ function resolvePositionString(pos, cursor, prevStart) {
1148
1296
  const n = Number.parseFloat(trimmed);
1149
1297
  return Number.isFinite(n) ? n : null;
1150
1298
  }
1299
+ function collectGsapSetStates(ast, scope, bindings, source) {
1300
+ const states = /* @__PURE__ */ new Map();
1301
+ acornWalk.ancestor(ast, {
1302
+ // fallow-ignore-next-line complexity
1303
+ CallExpression(node, _, ancestors) {
1304
+ const callee = node.callee;
1305
+ if (callee?.type !== "MemberExpression" || callee.object?.name !== "gsap" || callee.property?.name !== "set")
1306
+ return;
1307
+ const selector = resolveTargetSelector(node.arguments?.[0], ancestors, scope, bindings);
1308
+ if (!selector) return;
1309
+ const rec = objectExpressionToRecord(node.arguments?.[1], scope, source);
1310
+ const props = states.get(selector) ?? {};
1311
+ for (const [k, v] of Object.entries(rec)) {
1312
+ if (typeof v === "number" || typeof v === "string") props[k] = v;
1313
+ }
1314
+ states.set(selector, props);
1315
+ }
1316
+ });
1317
+ return states;
1318
+ }
1319
+ function mergeProps(target, props) {
1320
+ for (const [k, v] of Object.entries(props)) target[k] = v;
1321
+ return target;
1322
+ }
1323
+ function seedFromPreState(anim, cur) {
1324
+ const from = { ...anim.fromProperties ?? {} };
1325
+ let seeded = false;
1326
+ for (const prop of Object.keys(anim.properties)) {
1327
+ if (from[prop] === void 0 && cur[prop] !== void 0) {
1328
+ from[prop] = cur[prop];
1329
+ seeded = true;
1330
+ }
1331
+ }
1332
+ if (seeded) anim.fromProperties = from;
1333
+ }
1334
+ function seedSetStates(anims, initial) {
1335
+ const state = /* @__PURE__ */ new Map();
1336
+ for (const [sel, props] of initial) state.set(sel, { ...props });
1337
+ for (const anim of anims) {
1338
+ const sel = anim.targetSelector;
1339
+ if (anim.method === "set") {
1340
+ state.set(sel, mergeProps(state.get(sel) ?? {}, anim.properties));
1341
+ continue;
1342
+ }
1343
+ const cur = state.get(sel);
1344
+ if (anim.method === "to" && cur) seedFromPreState(anim, cur);
1345
+ state.set(sel, mergeProps(state.get(sel) ?? {}, anim.properties));
1346
+ }
1347
+ }
1151
1348
  function applyTimelineDefaults(anims, defaults) {
1152
1349
  if (!defaults) return;
1153
1350
  for (const anim of anims) {
@@ -1160,31 +1357,89 @@ function applyTimelineDefaults(anims, defaults) {
1160
1357
  }
1161
1358
  }
1162
1359
  }
1163
- function resolveTimelinePositions(anims) {
1360
+ function resolveLabelPosition(pos, labels, cursor) {
1361
+ const m = /^([A-Za-z_$][\w$]*)\s*(?:([+-])=\s*([\d.]+))?$/.exec(pos.trim());
1362
+ if (!m) return null;
1363
+ const name = m[1];
1364
+ let base = labels.get(name);
1365
+ if (base === void 0) {
1366
+ base = cursor;
1367
+ labels.set(name, base);
1368
+ }
1369
+ if (m[2] && m[3]) {
1370
+ const n = Number.parseFloat(m[3]);
1371
+ if (Number.isFinite(n)) return m[2] === "+" ? base + n : base - n;
1372
+ }
1373
+ return base;
1374
+ }
1375
+ function resolveAnimStart(anim, cursor, prevStart, labels) {
1376
+ if (anim.implicitPosition) return cursor;
1377
+ if (typeof anim.position === "number") return anim.position;
1378
+ if (typeof anim.position === "string") {
1379
+ return resolveLabelPosition(anim.position, labels, cursor) ?? resolvePositionString(anim.position, cursor, prevStart);
1380
+ }
1381
+ return cursor;
1382
+ }
1383
+ function resolveTimelinePositions(anims, labelDefs = []) {
1164
1384
  let cursor = 0;
1165
1385
  let prevStart = 0;
1166
- for (const anim of anims) {
1386
+ const labels = /* @__PURE__ */ new Map();
1387
+ let labelIdx = 0;
1388
+ const sortedLabels = [...labelDefs].sort((a, b) => a.order - b.order);
1389
+ const defineLabel = (def) => {
1390
+ let value;
1391
+ if (typeof def.position === "number") value = def.position;
1392
+ else if (typeof def.position === "string") {
1393
+ value = resolveLabelPosition(def.position, labels, cursor) ?? cursor;
1394
+ } else value = cursor;
1395
+ labels.set(def.name, Math.max(0, value));
1396
+ };
1397
+ anims.forEach((anim, i) => {
1398
+ while (labelIdx < sortedLabels.length && sortedLabels[labelIdx].order <= i) {
1399
+ defineLabel(sortedLabels[labelIdx]);
1400
+ labelIdx++;
1401
+ }
1167
1402
  if (anim.method === "set" && anim.global) {
1168
1403
  anim.resolvedStart = 0;
1169
- continue;
1404
+ return;
1170
1405
  }
1171
1406
  const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION;
1172
- let start;
1173
- if (anim.implicitPosition) {
1174
- start = cursor;
1175
- } else if (typeof anim.position === "number") {
1176
- start = anim.position;
1177
- } else if (typeof anim.position === "string") {
1178
- start = resolvePositionString(anim.position, cursor, prevStart);
1179
- } else {
1180
- start = cursor;
1181
- }
1407
+ const start = resolveAnimStart(anim, cursor, prevStart, labels);
1182
1408
  if (start != null) {
1183
1409
  anim.resolvedStart = Math.max(0, start);
1184
1410
  prevStart = anim.resolvedStart;
1185
1411
  cursor = Math.max(cursor, anim.resolvedStart + duration);
1186
1412
  }
1187
- }
1413
+ });
1414
+ while (labelIdx < sortedLabels.length) defineLabel(sortedLabels[labelIdx++]);
1415
+ }
1416
+ function collectAddLabelDefs(ast, ref, scope, sortedCalls) {
1417
+ const callLocs = sortedCalls.map((c) => c.node.callee?.property?.loc?.start);
1418
+ const defs = [];
1419
+ acornWalk.simple(ast, {
1420
+ // fallow-ignore-next-line complexity
1421
+ CallExpression(node) {
1422
+ const callee = node.callee;
1423
+ const objMatches = ref.kind === "identifier" ? callee.object?.type === "Identifier" && callee.object.name === ref.name : sameMemberAccess(callee.object, ref.node);
1424
+ if (callee?.type !== "MemberExpression" || !objMatches || callee.property?.name !== "addLabel")
1425
+ return;
1426
+ const nameNode = node.arguments?.[0];
1427
+ const name = typeof nameNode?.value === "string" ? nameNode.value : void 0;
1428
+ if (!name) return;
1429
+ const posVal = resolveNode(node.arguments?.[1], scope);
1430
+ const position = typeof posVal === "number" || typeof posVal === "string" ? posVal : void 0;
1431
+ const labelLoc = callee.property?.loc?.start;
1432
+ let order = sortedCalls.length;
1433
+ if (labelLoc) {
1434
+ order = callLocs.findIndex(
1435
+ (l) => l && (l.line > labelLoc.line || l.line === labelLoc.line && l.column > labelLoc.column)
1436
+ );
1437
+ if (order === -1) order = sortedCalls.length;
1438
+ }
1439
+ defs.push({ name, position, order });
1440
+ }
1441
+ });
1442
+ return defs;
1188
1443
  }
1189
1444
  function compareByLoc(a, b) {
1190
1445
  const aLoc = a.node.callee?.property?.loc?.start;
@@ -1265,7 +1520,10 @@ function parseGsapScriptAcorn(script) {
1265
1520
  sortBySourcePosition(calls);
1266
1521
  const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
1267
1522
  applyTimelineDefaults(rawAnims, detection.defaults);
1268
- resolveTimelinePositions(rawAnims);
1523
+ seedSetStates(rawAnims, collectGsapSetStates(ast, scope, targetBindings, script));
1524
+ const labelDefs = collectAddLabelDefs(ast, ref, scope, calls);
1525
+ resolveTimelinePositions(rawAnims, labelDefs);
1526
+ annotateStaggeredCollections(rawAnims);
1269
1527
  const animations = assignStableIds(rawAnims);
1270
1528
  const declPattern = ref.kind === "identifier" ? `(?:const|let|var)\\s+${timelineVar}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?` : `${escapeRegExp(timelineVar)}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?`;
1271
1529
  const timelineMatch = script.match(new RegExp(`^[\\s\\S]*?${declPattern}`));