@hyperframes/parsers 0.7.15 → 0.7.16

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.
@@ -572,6 +572,35 @@ function objectExpressionToRecord(node, scope) {
572
572
  function isGsapTimelineCall(node) {
573
573
  return node?.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.object?.name === "gsap" && node.callee.property?.name === "timeline";
574
574
  }
575
+ function staticMemberKey(node) {
576
+ if (!node || node.type !== "MemberExpression") return null;
577
+ if (node.computed) {
578
+ const p = node.property;
579
+ if (p?.type === "StringLiteral") return p.value;
580
+ if (p?.type === "Literal" && typeof p.value === "string") return p.value;
581
+ return null;
582
+ }
583
+ return node.property?.type === "Identifier" ? node.property.name : null;
584
+ }
585
+ function isStaticMemberRef(node) {
586
+ return node?.type === "MemberExpression" && staticMemberKey(node) !== null;
587
+ }
588
+ function sameMemberAccess(a, b) {
589
+ if (a?.type !== "MemberExpression" || b?.type !== "MemberExpression") return false;
590
+ if (staticMemberKey(a) !== staticMemberKey(b) || staticMemberKey(a) === null) return false;
591
+ const ao = a.object;
592
+ const bo = b.object;
593
+ if (ao?.type === "Identifier" && bo?.type === "Identifier") return ao.name === bo.name;
594
+ if (ao?.type === "MemberExpression" && bo?.type === "MemberExpression")
595
+ return sameMemberAccess(ao, bo);
596
+ return false;
597
+ }
598
+ function timelineRootSource(ref) {
599
+ return ref.kind === "identifier" ? ref.name : recast.print(ref.node).code;
600
+ }
601
+ function escapeRegExp(s) {
602
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
603
+ }
575
604
  function extractTimelineDefaults(callNode, scope) {
576
605
  const arg = callNode.arguments?.[0];
577
606
  if (!arg || arg.type !== "ObjectExpression") return void 0;
@@ -587,6 +616,7 @@ function extractTimelineDefaults(callNode, scope) {
587
616
  }
588
617
  function findTimelineVar(ast, scope) {
589
618
  let timelineVar = null;
619
+ let ref = null;
590
620
  let timelineCount = 0;
591
621
  let defaults;
592
622
  const emptyScope = scope ?? /* @__PURE__ */ new Map();
@@ -594,8 +624,9 @@ function findTimelineVar(ast, scope) {
594
624
  visitVariableDeclarator(path) {
595
625
  if (isGsapTimelineCall(path.node.init)) {
596
626
  timelineCount += 1;
597
- if (!timelineVar) {
598
- timelineVar = path.node.id?.name ?? null;
627
+ if (!ref && path.node.id?.type === "Identifier") {
628
+ timelineVar = path.node.id.name;
629
+ ref = { kind: "identifier", name: path.node.id.name };
599
630
  defaults = extractTimelineDefaults(path.node.init, emptyScope);
600
631
  }
601
632
  }
@@ -604,25 +635,32 @@ function findTimelineVar(ast, scope) {
604
635
  visitAssignmentExpression(path) {
605
636
  if (isGsapTimelineCall(path.node.right)) {
606
637
  timelineCount += 1;
607
- if (!timelineVar) {
638
+ if (!ref) {
608
639
  const left = path.node.left;
609
- if (left?.type === "Identifier") timelineVar = left.name;
610
- defaults = extractTimelineDefaults(path.node.right, emptyScope);
640
+ if (left?.type === "Identifier") {
641
+ timelineVar = left.name;
642
+ ref = { kind: "identifier", name: left.name };
643
+ defaults = extractTimelineDefaults(path.node.right, emptyScope);
644
+ } else if (isStaticMemberRef(left)) {
645
+ ref = { kind: "member", node: left };
646
+ defaults = extractTimelineDefaults(path.node.right, emptyScope);
647
+ }
611
648
  }
612
649
  }
613
650
  this.traverse(path);
614
651
  }
615
652
  });
616
- return { timelineVar, timelineCount, defaults };
653
+ return { timelineVar, ref, timelineCount, defaults };
617
654
  }
618
- function isTimelineRootedCall(callNode, timelineVar) {
655
+ function isTimelineRootedCall(callNode, ref) {
619
656
  let obj = callNode.callee?.object;
620
657
  while (obj?.type === "CallExpression") {
621
658
  obj = obj.callee?.object;
622
659
  }
623
- return obj?.type === "Identifier" && obj.name === timelineVar;
660
+ if (ref.kind === "identifier") return obj?.type === "Identifier" && obj.name === ref.name;
661
+ return sameMemberAccess(obj, ref.node);
624
662
  }
625
- function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
663
+ function findAllTweenCalls(ast, ref, scope, targetBindings) {
626
664
  const results = [];
627
665
  recast.types.visit(ast, {
628
666
  visitCallExpression(path) {
@@ -630,7 +668,7 @@ function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
630
668
  const callee = node.callee;
631
669
  const gsapSetArg = node.arguments?.[0];
632
670
  const isGlobalSet = callee?.type === "MemberExpression" && callee.object?.type === "Identifier" && callee.object.name === "gsap" && callee.property?.type === "Identifier" && callee.property.name === "set" && (gsapSetArg?.type === "StringLiteral" || gsapSetArg?.type === "Literal" && typeof gsapSetArg.value === "string");
633
- if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && (isTimelineRootedCall(node, timelineVar) || isGlobalSet)) {
671
+ if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && (isTimelineRootedCall(node, ref) || isGlobalSet)) {
634
672
  const method = callee.property.name;
635
673
  if (!GSAP_METHODS.has(method)) {
636
674
  this.traverse(path);
@@ -1108,8 +1146,9 @@ function parseGsapAst(script) {
1108
1146
  const scope = collectScopeBindings(ast);
1109
1147
  const targetBindings = collectTargetBindings(ast, scope);
1110
1148
  const detection = findTimelineVar(ast, scope);
1111
- const timelineVar = detection.timelineVar ?? "tl";
1112
- const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
1149
+ const ref = detection.ref ?? { kind: "identifier", name: "tl" };
1150
+ const timelineVar = timelineRootSource(ref);
1151
+ const calls = findAllTweenCalls(ast, ref, scope, targetBindings);
1113
1152
  sortBySourcePosition(calls);
1114
1153
  const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope));
1115
1154
  applyTimelineDefaults(rawAnims, detection.defaults);
@@ -1125,13 +1164,12 @@ function parseGsapAst(script) {
1125
1164
  function parseGsapScript(script) {
1126
1165
  try {
1127
1166
  const { detection, timelineVar, located } = parseGsapAst(script);
1167
+ const ref = detection.ref ?? { kind: "identifier", name: "tl" };
1128
1168
  const animations = located.map((l) => l.animation);
1129
- const timelineMatch = script.match(
1130
- new RegExp(
1131
- `^[\\s\\S]*?(?:const|let|var)\\s+${timelineVar}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?`
1132
- )
1133
- );
1134
- const preamble = timelineMatch?.[0] ?? `const ${timelineVar} = gsap.timeline({ paused: true });`;
1169
+ const declPattern = ref.kind === "identifier" ? `(?:const|let|var)\\s+${timelineVar}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?` : `${escapeRegExp(timelineVar)}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?`;
1170
+ const timelineMatch = script.match(new RegExp(`^[\\s\\S]*?${declPattern}`));
1171
+ const fallbackPreamble = ref.kind === "identifier" ? `const ${timelineVar} = gsap.timeline({ paused: true });` : `${timelineVar} = gsap.timeline({ paused: true });`;
1172
+ const preamble = timelineMatch?.[0] ?? fallbackPreamble;
1135
1173
  const lastCallIdx = script.lastIndexOf(`${timelineVar}.`);
1136
1174
  let postamble = "";
1137
1175
  if (lastCallIdx !== -1) {
@@ -1143,7 +1181,7 @@ function parseGsapScript(script) {
1143
1181
  }
1144
1182
  const result = { animations, timelineVar, preamble, postamble };
1145
1183
  if (detection.timelineCount > 1) result.multipleTimelines = true;
1146
- if (detection.timelineCount > 0 && detection.timelineVar === null)
1184
+ if (detection.timelineCount > 0 && detection.ref === null)
1147
1185
  result.unsupportedTimelinePattern = true;
1148
1186
  return result;
1149
1187
  } catch {
@@ -1355,7 +1393,7 @@ function addAnimationToScript(script, animation) {
1355
1393
  console.warn("[gsap-parser] addAnimationToScript parse failed:", e);
1356
1394
  return { script, id: "" };
1357
1395
  }
1358
- if (parsed.located.length === 0 && parsed.detection.timelineVar === null) {
1396
+ if (parsed.located.length === 0 && parsed.detection.ref === null) {
1359
1397
  return { script, id: "" };
1360
1398
  }
1361
1399
  const id = `anim-${Date.now()}`;
@@ -1372,7 +1410,7 @@ function addAnimationWithKeyframesToScript(script, targetSelector, position, dur
1372
1410
  console.warn("[gsap-parser] addAnimationWithKeyframesToScript parse failed:", e);
1373
1411
  return { script, id: "" };
1374
1412
  }
1375
- if (parsed.located.length === 0 && parsed.detection.timelineVar === null) {
1413
+ if (parsed.located.length === 0 && parsed.detection.ref === null) {
1376
1414
  return { script, id: "" };
1377
1415
  }
1378
1416
  const selector = JSON.stringify(targetSelector);
@@ -1889,9 +1927,9 @@ function updateKeyframeInScript(script, animationId, percentage, properties, eas
1889
1927
  const match = findKeyframePropByPct(kfNode, percentage);
1890
1928
  if (!match) return script;
1891
1929
  if (Object.keys(properties).length === 0 && ease) {
1892
- const existing = match.prop.value;
1893
- if (existing?.type === "ObjectExpression") {
1894
- const props = existing.properties ?? [];
1930
+ const existing2 = match.prop.value;
1931
+ if (existing2?.type === "ObjectExpression") {
1932
+ const props = existing2.properties ?? [];
1895
1933
  const easeIdx = props.findIndex(
1896
1934
  (p) => isObjectProperty(p) && propKeyName(p) === "ease"
1897
1935
  );
@@ -1905,6 +1943,19 @@ function updateKeyframeInScript(script, animationId, percentage, properties, eas
1905
1943
  }
1906
1944
  return script;
1907
1945
  }
1946
+ const existing = match.prop.value;
1947
+ if (existing?.type === "ObjectExpression") {
1948
+ const props = existing.properties ?? [];
1949
+ const upsert = (key, valueCode) => {
1950
+ const idx = props.findIndex((p) => isObjectProperty(p) && propKeyName(p) === key);
1951
+ const node = parseExpr(`({ ${safeJsKey(key)}: ${valueCode} })`).properties[0];
1952
+ if (idx >= 0) props[idx] = node;
1953
+ else props.push(node);
1954
+ };
1955
+ for (const [k, v] of Object.entries(properties)) upsert(k, serializeValue(v));
1956
+ if (ease !== void 0) upsert("ease", JSON.stringify(ease));
1957
+ return recast.print(loc.parsed.ast).code;
1958
+ }
1908
1959
  match.prop.value = buildKeyframeValueNode(properties, ease);
1909
1960
  return recast.print(loc.parsed.ast).code;
1910
1961
  }
@@ -2210,7 +2261,7 @@ function addMotionPathToScript(script, targetSelector, position, duration, point
2210
2261
  console.warn("[gsap-parser] addMotionPathToScript parse failed:", e);
2211
2262
  return { script, id: null };
2212
2263
  }
2213
- if (parsed.located.length === 0 && parsed.detection.timelineVar === null) {
2264
+ if (parsed.located.length === 0 && parsed.detection.ref === null) {
2214
2265
  return { script, id: null };
2215
2266
  }
2216
2267
  const motionPathCode = buildMotionPathObjectCode({