@hyperframes/parsers 0.7.15 → 0.7.17

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.
@@ -0,0 +1,166 @@
1
+ import { K as Keyframe, V as ValidationResult } from './types-Cg0ZTXEf.js';
2
+ import { PropertyGroupName } from './gsapConstants.js';
3
+
4
+ /**
5
+ * Recast-free GSAP helpers: serialization, keyframe<->animation conversion,
6
+ * validation, and shared types.
7
+ *
8
+ * This module MUST NOT import recast / @babel/parser. It is part of the
9
+ * isomorphic core layer that the barrel and browser code depend on. AST
10
+ * parsing of GSAP source lives in the Node-only `./gsapParser` module.
11
+ */
12
+
13
+ type GsapMethod = "set" | "to" | "from" | "fromTo";
14
+ /** How a tween was constructed in source — drives display classification and editability. */
15
+ type GsapProvenanceKind = "literal" | "helper" | "loop" | "runtime-dynamic";
16
+ /**
17
+ * Origin of a parsed tween. `literal` tweens map 1:1 to a source call and edit
18
+ * directly; `helper`/`loop` tweens are expanded from a reused construct (unroll
19
+ * to edit); `runtime-dynamic` tweens come from live introspection (override to
20
+ * edit). Absent provenance is treated as `literal`.
21
+ */
22
+ interface GsapProvenance {
23
+ kind: GsapProvenanceKind;
24
+ /** Helper function name (kind === "helper"). */
25
+ fn?: string;
26
+ /** 1-based ordinal of the originating call site / loop construct in source order. */
27
+ callSite?: number;
28
+ /** 0-based iteration index (kind === "loop"). */
29
+ iteration?: number;
30
+ /** Source offset [start, end] of the originating call/loop, when known. */
31
+ sourceRange?: [number, number];
32
+ }
33
+ /** How a tween's keyframes can be edited, derived from its provenance. */
34
+ type KeyframeEditability = "direct" | "unroll" | "source";
35
+ /**
36
+ * Map provenance to an editing strategy:
37
+ * - `direct` — literal tween, maps 1:1 to source; edit in place.
38
+ * - `unroll` — helper/loop expansion; unroll to literal tweens, then edit.
39
+ * - `source` — runtime-dynamic value; not statically editable, edit the code.
40
+ */
41
+ declare function editabilityForProvenance(provenance?: GsapProvenance): KeyframeEditability;
42
+ interface GsapAnimation {
43
+ id: string;
44
+ targetSelector: string;
45
+ method: GsapMethod;
46
+ position: number | string;
47
+ properties: Record<string, number | string>;
48
+ fromProperties?: Record<string, number | string>;
49
+ duration?: number;
50
+ ease?: string;
51
+ /** Non-editable GSAP config (stagger, yoyo, repeat, etc.) preserved for round-trips. */
52
+ extras?: Record<string, unknown>;
53
+ /** Native GSAP keyframes data — present when the tween uses keyframes: { ... }. */
54
+ keyframes?: GsapKeyframesData;
55
+ /** Arc motion path config — present when the tween uses motionPath for curved position interpolation. */
56
+ arcPath?: ArcPathConfig;
57
+ /** True when the tween has a `keyframes` property that couldn't be statically resolved (dynamic). */
58
+ hasUnresolvedKeyframes?: boolean;
59
+ /** True when the tween's target selector couldn't be statically resolved (dynamic). */
60
+ hasUnresolvedSelector?: boolean;
61
+ /** Absolute start time computed by walking the timeline chain (handles +=, -=, <, >, labels). */
62
+ resolvedStart?: number;
63
+ /** True when no position arg was authored — the tween is sequentially placed by GSAP. */
64
+ implicitPosition?: boolean;
65
+ /** Which property group this tween belongs to (position, scale, size, rotation, visual, other).
66
+ * Undefined for legacy mixed tweens that bundle multiple groups. */
67
+ propertyGroup?: PropertyGroupName;
68
+ /** True for a base `gsap.set(...)` (a static hold that runs immediately, OFF the
69
+ * timeline) rather than `tl.set(...)`. Carries no timeline position and shows no
70
+ * keyframe marker — used to persist a static value (e.g. a 3D transform) without
71
+ * introducing a 0% keyframe. */
72
+ global?: boolean;
73
+ /** How this tween was constructed in source. Absent ⇒ literal. */
74
+ provenance?: GsapProvenance;
75
+ }
76
+ interface GsapPercentageKeyframe {
77
+ percentage: number;
78
+ properties: Record<string, number | string>;
79
+ ease?: string;
80
+ }
81
+ type GsapKeyframeFormat = "percentage" | "object-array" | "simple-array";
82
+ interface GsapKeyframesData {
83
+ format: GsapKeyframeFormat;
84
+ keyframes: GsapPercentageKeyframe[];
85
+ ease?: string;
86
+ easeEach?: string;
87
+ }
88
+ interface ArcPathSegment {
89
+ curviness: number;
90
+ cp1?: {
91
+ x: number;
92
+ y: number;
93
+ };
94
+ cp2?: {
95
+ x: number;
96
+ y: number;
97
+ };
98
+ }
99
+ interface ArcPathConfig {
100
+ enabled: boolean;
101
+ autoRotate: boolean | number;
102
+ segments: ArcPathSegment[];
103
+ }
104
+ interface MotionPathShape {
105
+ arcPath: ArcPathConfig;
106
+ waypoints: Array<{
107
+ x: number;
108
+ y: number;
109
+ }>;
110
+ }
111
+ /**
112
+ * Build arcPath segments + waypoints from resolved path coordinates. Shared by
113
+ * the AST parser (coords from literal nodes) and the runtime scanner (coords
114
+ * from a live `vars.motionPath`), so both produce identical arc config.
115
+ */
116
+ declare function buildArcPath(coords: Array<{
117
+ x: number;
118
+ y: number;
119
+ }>, curviness: number, autoRotate: boolean | number, isCubic: boolean): MotionPathShape | undefined;
120
+ interface ParsedGsap {
121
+ animations: GsapAnimation[];
122
+ timelineVar: string;
123
+ preamble: string;
124
+ postamble: string;
125
+ multipleTimelines?: boolean;
126
+ unsupportedTimelinePattern?: boolean;
127
+ }
128
+
129
+ interface SplitAnimationsOptions {
130
+ originalId: string;
131
+ newId: string;
132
+ splitTime: number;
133
+ elementStart: number;
134
+ elementDuration: number;
135
+ }
136
+ interface SplitAnimationsResult {
137
+ script: string;
138
+ /** Non-ID-selector animations that the engine cannot safely retarget. */
139
+ skippedSelectors: string[];
140
+ }
141
+ declare function serializeGsapAnimations(animations: GsapAnimation[], timelineVar?: string, options?: {
142
+ includeMediaSync?: boolean;
143
+ preamble?: string;
144
+ postamble?: string;
145
+ }): string;
146
+ /**
147
+ * Filter animations to those targeting `#<elementId>` (id-only match). For the
148
+ * studio panel's id-OR-selector matching, see `getAnimationsForElement` in
149
+ * `useGsapTweenCache.ts` — distinct on purpose, hence the distinct name.
150
+ */
151
+ declare function getAnimationsForElementId(animations: GsapAnimation[], elementId: string): GsapAnimation[];
152
+ declare function validateCompositionGsap(script: string): ValidationResult;
153
+ declare function keyframesToGsapAnimations(elementId: string, keyframes: Keyframe[], elementStartTime: number, base?: {
154
+ x?: number;
155
+ y?: number;
156
+ scale?: number;
157
+ }): GsapAnimation[];
158
+ declare function gsapAnimationsToKeyframes(animations: GsapAnimation[], elementStartTime: number, options?: {
159
+ baseX?: number;
160
+ baseY?: number;
161
+ baseScale?: number;
162
+ clampTimeToZero?: boolean;
163
+ skipBaseSet?: boolean;
164
+ }): Keyframe[];
165
+
166
+ export { type ArcPathConfig as A, type GsapAnimation as G, type KeyframeEditability as K, type MotionPathShape as M, type ParsedGsap as P, type SplitAnimationsOptions as S, type ArcPathSegment as a, type GsapKeyframesData as b, type GsapMethod as c, type GsapPercentageKeyframe as d, type GsapProvenance as e, type GsapProvenanceKind as f, type SplitAnimationsResult as g, editabilityForProvenance as h, getAnimationsForElementId as i, gsapAnimationsToKeyframes as j, keyframesToGsapAnimations as k, buildArcPath as l, type GsapKeyframeFormat as m, serializeGsapAnimations as s, validateCompositionGsap as v };
@@ -1,4 +1,5 @@
1
- import { G as GsapAnimation, A as ArcPathConfig, S as SplitAnimationsOptions, g as SplitAnimationsResult, a as ArcPathSegment } from './gsapSerialize-B_JRTCeV.js';
1
+ import { G as GsapAnimation, A as ArcPathConfig, S as SplitAnimationsOptions, g as SplitAnimationsResult, a as ArcPathSegment } from './gsapSerialize-Bei7m7M7.js';
2
+ import './types-Cg0ZTXEf.js';
2
3
  import './gsapConstants.js';
3
4
 
4
5
  declare function updateAnimationInScript(script: string, animationId: string, updates: Partial<GsapAnimation> & {
@@ -370,6 +370,31 @@ function objectExpressionToRecord(node, scope, source) {
370
370
  function isGsapTimelineCall(node) {
371
371
  return node?.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.object?.name === "gsap" && node.callee.property?.name === "timeline";
372
372
  }
373
+ function staticMemberKey(node) {
374
+ if (!node || node.type !== "MemberExpression") return null;
375
+ if (node.computed) {
376
+ const p = node.property;
377
+ if (p?.type === "Literal" && typeof p.value === "string") return p.value;
378
+ return null;
379
+ }
380
+ return node.property?.type === "Identifier" ? node.property.name : null;
381
+ }
382
+ function isStaticMemberRef(node) {
383
+ return node?.type === "MemberExpression" && staticMemberKey(node) !== null;
384
+ }
385
+ function sameMemberAccess(a, b) {
386
+ if (a?.type !== "MemberExpression" || b?.type !== "MemberExpression") return false;
387
+ if (staticMemberKey(a) !== staticMemberKey(b) || staticMemberKey(a) === null) return false;
388
+ const ao = a.object;
389
+ const bo = b.object;
390
+ if (ao?.type === "Identifier" && bo?.type === "Identifier") return ao.name === bo.name;
391
+ if (ao?.type === "MemberExpression" && bo?.type === "MemberExpression")
392
+ return sameMemberAccess(ao, bo);
393
+ return false;
394
+ }
395
+ function timelineRootSource(ref, script) {
396
+ return ref.kind === "identifier" ? ref.name : script.slice(ref.node.start, ref.node.end);
397
+ }
373
398
  function extractTimelineDefaults(callNode, scope) {
374
399
  const arg = callNode.arguments?.[0];
375
400
  if (!arg || arg.type !== "ObjectExpression") return void 0;
@@ -389,6 +414,7 @@ function extractTimelineDefaults(callNode, scope) {
389
414
  }
390
415
  function findTimelineVar(ast, scope) {
391
416
  let timelineVar = null;
417
+ let ref = null;
392
418
  let timelineCount = 0;
393
419
  let defaults;
394
420
  const emptyScope = scope ?? /* @__PURE__ */ new Map();
@@ -396,8 +422,9 @@ function findTimelineVar(ast, scope) {
396
422
  VariableDeclarator(node) {
397
423
  if (isGsapTimelineCall(node.init)) {
398
424
  timelineCount += 1;
399
- if (!timelineVar) {
400
- timelineVar = node.id?.name ?? null;
425
+ if (!ref && node.id?.type === "Identifier") {
426
+ timelineVar = node.id.name;
427
+ ref = { kind: "identifier", name: node.id.name };
401
428
  defaults = extractTimelineDefaults(node.init, emptyScope);
402
429
  }
403
430
  }
@@ -405,15 +432,21 @@ function findTimelineVar(ast, scope) {
405
432
  AssignmentExpression(node) {
406
433
  if (isGsapTimelineCall(node.right)) {
407
434
  timelineCount += 1;
408
- if (!timelineVar) {
435
+ if (!ref) {
409
436
  const left = node.left;
410
- if (left?.type === "Identifier") timelineVar = left.name;
411
- defaults = extractTimelineDefaults(node.right, emptyScope);
437
+ if (left?.type === "Identifier") {
438
+ timelineVar = left.name;
439
+ ref = { kind: "identifier", name: left.name };
440
+ defaults = extractTimelineDefaults(node.right, emptyScope);
441
+ } else if (isStaticMemberRef(left)) {
442
+ ref = { kind: "member", node: left };
443
+ defaults = extractTimelineDefaults(node.right, emptyScope);
444
+ }
412
445
  }
413
446
  }
414
447
  }
415
448
  });
416
- return { timelineVar, timelineCount, defaults };
449
+ return { timelineVar, ref, timelineCount, defaults };
417
450
  }
418
451
  var BUILTIN_VAR_KEYS = /* @__PURE__ */ new Set(["duration", "ease", "delay"]);
419
452
  var DROPPED_VAR_KEYS = /* @__PURE__ */ new Set(["onComplete", "onStart", "onUpdate", "onRepeat"]);
@@ -426,14 +459,15 @@ var EXTRAS_KEYS = /* @__PURE__ */ new Set([
426
459
  "overwrite",
427
460
  "immediateRender"
428
461
  ]);
429
- function isTimelineRootedCall(callNode, timelineVar) {
462
+ function isTimelineRootedCall(callNode, ref) {
430
463
  let obj = callNode.callee?.object;
431
464
  while (obj?.type === "CallExpression") {
432
465
  obj = obj.callee?.object;
433
466
  }
434
- return obj?.type === "Identifier" && obj.name === timelineVar;
467
+ if (ref.kind === "identifier") return obj?.type === "Identifier" && obj.name === ref.name;
468
+ return sameMemberAccess(obj, ref.node);
435
469
  }
436
- function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
470
+ function findAllTweenCalls(ast, ref, scope, targetBindings) {
437
471
  const results = [];
438
472
  function visit(node, ancestors) {
439
473
  if (!node || typeof node !== "object") return;
@@ -442,7 +476,7 @@ function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
442
476
  const callee = node.callee;
443
477
  const gsapSetArg = node.arguments?.[0];
444
478
  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");
445
- if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && (isTimelineRootedCall(node, timelineVar) || isGlobalSet) && GSAP_METHODS.has(callee.property.name)) {
479
+ if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && (isTimelineRootedCall(node, ref) || isGlobalSet) && GSAP_METHODS.has(callee.property.name)) {
446
480
  const method = callee.property.name;
447
481
  const args = node.arguments;
448
482
  const selectorValue = args.length >= 1 ? resolveTargetSelector(args[0], nodeAncestors, scope, targetBindings) ?? "__unresolved__" : "__unresolved__";
@@ -895,8 +929,9 @@ function parseGsapScriptAcornForWrite(script) {
895
929
  const scope = collectScopeBindings(ast);
896
930
  const targetBindings = collectTargetBindings(ast, scope);
897
931
  const detection = findTimelineVar(ast, scope);
898
- const timelineVar = detection.timelineVar ?? "tl";
899
- const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
932
+ const ref = detection.ref ?? { kind: "identifier", name: "tl" };
933
+ const timelineVar = timelineRootSource(ref, script);
934
+ const calls = findAllTweenCalls(ast, ref, scope, targetBindings);
900
935
  sortBySourcePosition(calls);
901
936
  const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
902
937
  applyTimelineDefaults(rawAnims, detection.defaults);
@@ -907,7 +942,7 @@ function parseGsapScriptAcornForWrite(script) {
907
942
  call,
908
943
  animation: animations[i]
909
944
  }));
910
- return { ast, timelineVar, hasTimeline: detection.timelineVar !== null, located };
945
+ return { ast, timelineVar, hasTimeline: detection.ref !== null, located };
911
946
  } catch {
912
947
  return null;
913
948
  }
@@ -1063,9 +1098,11 @@ function reconcileEditableProps(ms, objNode, source, newProps, nonEditableOverri
1063
1098
  }
1064
1099
  ms.overwrite(objNode.start, objNode.end, `{ ${entries.join(", ")} }`);
1065
1100
  }
1066
- function isTimelineRooted(node, timelineVar) {
1101
+ function isTimelineRooted(node, timelineVar, script) {
1067
1102
  if (node?.type === "Identifier") return node.name === timelineVar;
1068
- if (node?.type === "CallExpression") return isTimelineRooted(node.callee?.object, timelineVar);
1103
+ if (node?.type === "MemberExpression") return script.slice(node.start, node.end) === timelineVar;
1104
+ if (node?.type === "CallExpression")
1105
+ return isTimelineRooted(node.callee?.object, timelineVar, script);
1069
1106
  return false;
1070
1107
  }
1071
1108
  function findInsertionPoint(parsed) {
@@ -1372,10 +1409,17 @@ function updateKeyframeInScript(script, animationId, percentage, properties, eas
1372
1409
  if (kfPropNode.value?.type !== "ObjectExpression") return script;
1373
1410
  const match = findKfPropByPct(kfPropNode.value, percentage);
1374
1411
  if (!match) return script;
1375
- const record = { ...properties };
1376
- if (ease) record.ease = ease;
1377
1412
  const ms = new MagicString(script);
1378
- ms.overwrite(match.prop.value.start, match.prop.value.end, recordToCode(record));
1413
+ if (match.prop.value?.type === "ObjectExpression") {
1414
+ for (const [k, v] of Object.entries(properties)) {
1415
+ upsertProp(ms, match.prop.value, k, v);
1416
+ }
1417
+ if (ease !== void 0) upsertProp(ms, match.prop.value, "ease", ease);
1418
+ } else {
1419
+ const record = { ...properties };
1420
+ if (ease) record.ease = ease;
1421
+ ms.overwrite(match.prop.value.start, match.prop.value.end, recordToCode(record));
1422
+ }
1379
1423
  return ms.toString();
1380
1424
  }
1381
1425
  function updateArrayKeyframeByPct(script, arrayNode, percentage, properties, ease) {
@@ -1854,18 +1898,18 @@ function splitIntoPropertyGroupsFromScript(script, animationId) {
1854
1898
  const newIds = (reParsed?.located ?? []).filter((l) => l.animation.targetSelector === animation.targetSelector).map((l) => l.id);
1855
1899
  return { script: result, ids: newIds };
1856
1900
  }
1857
- function isTimelineMethodCall(expr, timelineVar, method) {
1858
- return expr?.type === "CallExpression" && expr.callee?.type === "MemberExpression" && isTimelineRooted(expr.callee.object, timelineVar) && expr.callee.property?.name === method;
1901
+ function isTimelineMethodCall(expr, timelineVar, method, script) {
1902
+ return expr?.type === "CallExpression" && expr.callee?.type === "MemberExpression" && isTimelineRooted(expr.callee.object, timelineVar, script) && expr.callee.property?.name === method;
1859
1903
  }
1860
- function isAddLabelCall(expr, timelineVar, name) {
1904
+ function isAddLabelCall(expr, timelineVar, name, script) {
1861
1905
  const firstArg = expr?.arguments?.[0];
1862
- return isTimelineMethodCall(expr, timelineVar, "addLabel") && firstArg?.type === "Literal" && firstArg.value === name;
1906
+ return isTimelineMethodCall(expr, timelineVar, "addLabel", script) && firstArg?.type === "Literal" && firstArg.value === name;
1863
1907
  }
1864
- function findLabelStatements(parsed, name) {
1908
+ function findLabelStatements(parsed, name, script) {
1865
1909
  const targets = [];
1866
1910
  acornWalk2.simple(parsed.ast, {
1867
1911
  ExpressionStatement(node) {
1868
- if (isAddLabelCall(node.expression, parsed.timelineVar, name)) targets.push(node);
1912
+ if (isAddLabelCall(node.expression, parsed.timelineVar, name, script)) targets.push(node);
1869
1913
  }
1870
1914
  });
1871
1915
  return targets;
@@ -1873,7 +1917,7 @@ function findLabelStatements(parsed, name) {
1873
1917
  function addLabelToScript(script, name, position) {
1874
1918
  const parsed = parseGsapScriptAcornForWrite(script);
1875
1919
  if (!parsed) return script;
1876
- const existing = findLabelStatements(parsed, name)[0];
1920
+ const existing = findLabelStatements(parsed, name, script)[0];
1877
1921
  if (existing) {
1878
1922
  const ms2 = new MagicString(script);
1879
1923
  const posArg = existing.expression.arguments?.[1];
@@ -1891,7 +1935,7 @@ function addLabelToScript(script, name, position) {
1891
1935
  function removeLabelFromScript(script, name) {
1892
1936
  const parsed = parseGsapScriptAcornForWrite(script);
1893
1937
  if (!parsed) return script;
1894
- const targets = findLabelStatements(parsed, name);
1938
+ const targets = findLabelStatements(parsed, name, script);
1895
1939
  if (!targets.length) return script;
1896
1940
  const ms = new MagicString(script);
1897
1941
  for (const target of targets) {