@hyperframes/core 0.7.13 → 0.7.15

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 (238) hide show
  1. package/dist/compiler/staticGuard.js +1 -1
  2. package/dist/compiler/staticGuard.js.map +1 -1
  3. package/dist/core.types.d.ts +2 -369
  4. package/dist/core.types.d.ts.map +1 -1
  5. package/dist/core.types.js +1 -84
  6. package/dist/core.types.js.map +1 -1
  7. package/dist/generators/hyperframes.d.ts +1 -1
  8. package/dist/generators/hyperframes.d.ts.map +1 -1
  9. package/dist/generators/hyperframes.js +1 -1
  10. package/dist/generators/hyperframes.js.map +1 -1
  11. package/dist/index.d.ts +5 -7
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +7 -4
  14. package/dist/index.js.map +1 -1
  15. package/dist/lint/index.d.ts +2 -2
  16. package/dist/lint/index.d.ts.map +1 -1
  17. package/dist/lint/index.js +2 -1
  18. package/dist/lint/index.js.map +1 -1
  19. package/dist/parsers/gsapConstants.d.ts +2 -12
  20. package/dist/parsers/gsapConstants.d.ts.map +1 -1
  21. package/dist/parsers/gsapConstants.js +2 -110
  22. package/dist/parsers/gsapConstants.js.map +1 -1
  23. package/dist/parsers/gsapParserAcorn.d.ts +2 -48
  24. package/dist/parsers/gsapParserAcorn.d.ts.map +1 -1
  25. package/dist/parsers/gsapParserAcorn.js +2 -1057
  26. package/dist/parsers/gsapParserAcorn.js.map +1 -1
  27. package/dist/parsers/gsapParserExports.d.ts +2 -20
  28. package/dist/parsers/gsapParserExports.d.ts.map +1 -1
  29. package/dist/parsers/gsapParserExports.js +2 -8
  30. package/dist/parsers/gsapParserExports.js.map +1 -1
  31. package/dist/parsers/gsapWriterAcorn.d.ts +2 -90
  32. package/dist/parsers/gsapWriterAcorn.d.ts.map +1 -1
  33. package/dist/parsers/gsapWriterAcorn.js +2 -2016
  34. package/dist/parsers/gsapWriterAcorn.js.map +1 -1
  35. package/dist/parsers/hfIds.d.ts +2 -16
  36. package/dist/parsers/hfIds.d.ts.map +1 -1
  37. package/dist/parsers/hfIds.js +2 -127
  38. package/dist/parsers/hfIds.js.map +1 -1
  39. package/dist/parsers/springEase.d.ts +2 -28
  40. package/dist/parsers/springEase.d.ts.map +1 -1
  41. package/dist/parsers/springEase.js +2 -69
  42. package/dist/parsers/springEase.js.map +1 -1
  43. package/dist/slideshow/index.d.ts +1 -0
  44. package/dist/slideshow/index.d.ts.map +1 -1
  45. package/dist/slideshow/index.js +1 -0
  46. package/dist/slideshow/index.js.map +1 -1
  47. package/dist/studio-api/helpers/draftMarkers.d.ts +2 -10
  48. package/dist/studio-api/helpers/draftMarkers.d.ts.map +1 -1
  49. package/dist/studio-api/helpers/draftMarkers.js +2 -10
  50. package/dist/studio-api/helpers/draftMarkers.js.map +1 -1
  51. package/dist/studio-api/helpers/finiteMutation.d.ts +2 -10
  52. package/dist/studio-api/helpers/finiteMutation.d.ts.map +1 -1
  53. package/dist/studio-api/helpers/finiteMutation.js +2 -20
  54. package/dist/studio-api/helpers/finiteMutation.js.map +1 -1
  55. package/dist/studio-api/helpers/manualEditsRenderScript.d.ts +2 -12
  56. package/dist/studio-api/helpers/manualEditsRenderScript.d.ts.map +1 -1
  57. package/dist/studio-api/helpers/manualEditsRenderScript.js +2 -632
  58. package/dist/studio-api/helpers/manualEditsRenderScript.js.map +1 -1
  59. package/dist/studio-api/helpers/screenshotClip.d.ts +2 -7
  60. package/dist/studio-api/helpers/screenshotClip.d.ts.map +1 -1
  61. package/dist/studio-api/helpers/screenshotClip.js +2 -21
  62. package/dist/studio-api/helpers/screenshotClip.js.map +1 -1
  63. package/dist/studio-api/helpers/studioMotionRenderScript.d.ts +2 -8
  64. package/dist/studio-api/helpers/studioMotionRenderScript.d.ts.map +1 -1
  65. package/dist/studio-api/helpers/studioMotionRenderScript.js +2 -207
  66. package/dist/studio-api/helpers/studioMotionRenderScript.js.map +1 -1
  67. package/dist/studio-api/index.d.ts +2 -9
  68. package/dist/studio-api/index.d.ts.map +1 -1
  69. package/dist/studio-api/index.js +2 -8
  70. package/dist/studio-api/index.js.map +1 -1
  71. package/package.json +16 -18
  72. package/dist/generated/runtime-inline.js +0 -11
  73. package/dist/hyperframe.manifest.json +0 -22
  74. package/dist/hyperframe.runtime.mjs +0 -325
  75. package/dist/lint/context.d.ts +0 -17
  76. package/dist/lint/context.d.ts.map +0 -1
  77. package/dist/lint/context.js +0 -37
  78. package/dist/lint/context.js.map +0 -1
  79. package/dist/lint/hyperframeLinter.d.ts +0 -14
  80. package/dist/lint/hyperframeLinter.d.ts.map +0 -1
  81. package/dist/lint/hyperframeLinter.js +0 -132
  82. package/dist/lint/hyperframeLinter.js.map +0 -1
  83. package/dist/lint/rules/adapters.d.ts +0 -3
  84. package/dist/lint/rules/adapters.d.ts.map +0 -1
  85. package/dist/lint/rules/adapters.js +0 -40
  86. package/dist/lint/rules/adapters.js.map +0 -1
  87. package/dist/lint/rules/captions.d.ts +0 -3
  88. package/dist/lint/rules/captions.d.ts.map +0 -1
  89. package/dist/lint/rules/captions.js +0 -242
  90. package/dist/lint/rules/captions.js.map +0 -1
  91. package/dist/lint/rules/composition.d.ts +0 -5
  92. package/dist/lint/rules/composition.d.ts.map +0 -1
  93. package/dist/lint/rules/composition.js +0 -694
  94. package/dist/lint/rules/composition.js.map +0 -1
  95. package/dist/lint/rules/core.d.ts +0 -3
  96. package/dist/lint/rules/core.d.ts.map +0 -1
  97. package/dist/lint/rules/core.js +0 -432
  98. package/dist/lint/rules/core.js.map +0 -1
  99. package/dist/lint/rules/fonts.d.ts +0 -3
  100. package/dist/lint/rules/fonts.d.ts.map +0 -1
  101. package/dist/lint/rules/fonts.js +0 -205
  102. package/dist/lint/rules/fonts.js.map +0 -1
  103. package/dist/lint/rules/gsap.d.ts +0 -4
  104. package/dist/lint/rules/gsap.d.ts.map +0 -1
  105. package/dist/lint/rules/gsap.js +0 -1039
  106. package/dist/lint/rules/gsap.js.map +0 -1
  107. package/dist/lint/rules/media.d.ts +0 -3
  108. package/dist/lint/rules/media.d.ts.map +0 -1
  109. package/dist/lint/rules/media.js +0 -491
  110. package/dist/lint/rules/media.js.map +0 -1
  111. package/dist/lint/rules/slideshow.d.ts +0 -4
  112. package/dist/lint/rules/slideshow.d.ts.map +0 -1
  113. package/dist/lint/rules/slideshow.js +0 -74
  114. package/dist/lint/rules/slideshow.js.map +0 -1
  115. package/dist/lint/rules/textures.d.ts +0 -3
  116. package/dist/lint/rules/textures.d.ts.map +0 -1
  117. package/dist/lint/rules/textures.js +0 -192
  118. package/dist/lint/rules/textures.js.map +0 -1
  119. package/dist/lint/types.d.ts +0 -36
  120. package/dist/lint/types.d.ts.map +0 -1
  121. package/dist/lint/types.js +0 -2
  122. package/dist/lint/types.js.map +0 -1
  123. package/dist/lint/utils.d.ts +0 -55
  124. package/dist/lint/utils.d.ts.map +0 -1
  125. package/dist/lint/utils.js +0 -264
  126. package/dist/lint/utils.js.map +0 -1
  127. package/dist/parsers/gsapInline.d.ts +0 -47
  128. package/dist/parsers/gsapInline.d.ts.map +0 -1
  129. package/dist/parsers/gsapInline.js +0 -513
  130. package/dist/parsers/gsapInline.js.map +0 -1
  131. package/dist/parsers/gsapParser.d.ts +0 -158
  132. package/dist/parsers/gsapParser.d.ts.map +0 -1
  133. package/dist/parsers/gsapParser.js +0 -2634
  134. package/dist/parsers/gsapParser.js.map +0 -1
  135. package/dist/parsers/gsapParser.test-helpers.d.ts +0 -49
  136. package/dist/parsers/gsapParser.test-helpers.d.ts.map +0 -1
  137. package/dist/parsers/gsapParser.test-helpers.js +0 -97
  138. package/dist/parsers/gsapParser.test-helpers.js.map +0 -1
  139. package/dist/parsers/gsapSerialize.d.ts +0 -191
  140. package/dist/parsers/gsapSerialize.d.ts.map +0 -1
  141. package/dist/parsers/gsapSerialize.js +0 -410
  142. package/dist/parsers/gsapSerialize.js.map +0 -1
  143. package/dist/parsers/gsapUnroll.d.ts +0 -7
  144. package/dist/parsers/gsapUnroll.d.ts.map +0 -1
  145. package/dist/parsers/gsapUnroll.js +0 -137
  146. package/dist/parsers/gsapUnroll.js.map +0 -1
  147. package/dist/parsers/htmlParser.d.ts +0 -27
  148. package/dist/parsers/htmlParser.d.ts.map +0 -1
  149. package/dist/parsers/htmlParser.js +0 -731
  150. package/dist/parsers/htmlParser.js.map +0 -1
  151. package/dist/studio-api/createStudioApi.d.ts +0 -10
  152. package/dist/studio-api/createStudioApi.d.ts.map +0 -1
  153. package/dist/studio-api/createStudioApi.js +0 -32
  154. package/dist/studio-api/createStudioApi.js.map +0 -1
  155. package/dist/studio-api/helpers/backupJournal.d.ts +0 -9
  156. package/dist/studio-api/helpers/backupJournal.d.ts.map +0 -1
  157. package/dist/studio-api/helpers/backupJournal.js +0 -81
  158. package/dist/studio-api/helpers/backupJournal.js.map +0 -1
  159. package/dist/studio-api/helpers/hfIdPersist.d.ts +0 -11
  160. package/dist/studio-api/helpers/hfIdPersist.d.ts.map +0 -1
  161. package/dist/studio-api/helpers/hfIdPersist.js +0 -39
  162. package/dist/studio-api/helpers/hfIdPersist.js.map +0 -1
  163. package/dist/studio-api/helpers/mediaValidation.d.ts +0 -20
  164. package/dist/studio-api/helpers/mediaValidation.d.ts.map +0 -1
  165. package/dist/studio-api/helpers/mediaValidation.js +0 -56
  166. package/dist/studio-api/helpers/mediaValidation.js.map +0 -1
  167. package/dist/studio-api/helpers/mime.d.ts +0 -4
  168. package/dist/studio-api/helpers/mime.d.ts.map +0 -1
  169. package/dist/studio-api/helpers/mime.js +0 -39
  170. package/dist/studio-api/helpers/mime.js.map +0 -1
  171. package/dist/studio-api/helpers/previewAdapter.d.ts +0 -43
  172. package/dist/studio-api/helpers/previewAdapter.d.ts.map +0 -1
  173. package/dist/studio-api/helpers/previewAdapter.js +0 -118
  174. package/dist/studio-api/helpers/previewAdapter.js.map +0 -1
  175. package/dist/studio-api/helpers/projectSignature.d.ts +0 -5
  176. package/dist/studio-api/helpers/projectSignature.d.ts.map +0 -1
  177. package/dist/studio-api/helpers/projectSignature.js +0 -154
  178. package/dist/studio-api/helpers/projectSignature.js.map +0 -1
  179. package/dist/studio-api/helpers/safePath.d.ts +0 -12
  180. package/dist/studio-api/helpers/safePath.d.ts.map +0 -1
  181. package/dist/studio-api/helpers/safePath.js +0 -38
  182. package/dist/studio-api/helpers/safePath.js.map +0 -1
  183. package/dist/studio-api/helpers/sourceMutation.d.ts +0 -28
  184. package/dist/studio-api/helpers/sourceMutation.d.ts.map +0 -1
  185. package/dist/studio-api/helpers/sourceMutation.js +0 -339
  186. package/dist/studio-api/helpers/sourceMutation.js.map +0 -1
  187. package/dist/studio-api/helpers/subComposition.d.ts +0 -20
  188. package/dist/studio-api/helpers/subComposition.d.ts.map +0 -1
  189. package/dist/studio-api/helpers/subComposition.js +0 -289
  190. package/dist/studio-api/helpers/subComposition.js.map +0 -1
  191. package/dist/studio-api/helpers/waveform.d.ts +0 -4
  192. package/dist/studio-api/helpers/waveform.d.ts.map +0 -1
  193. package/dist/studio-api/helpers/waveform.js +0 -80
  194. package/dist/studio-api/helpers/waveform.js.map +0 -1
  195. package/dist/studio-api/routes/files.d.ts +0 -4
  196. package/dist/studio-api/routes/files.d.ts.map +0 -1
  197. package/dist/studio-api/routes/files.js +0 -1200
  198. package/dist/studio-api/routes/files.js.map +0 -1
  199. package/dist/studio-api/routes/fonts.d.ts +0 -3
  200. package/dist/studio-api/routes/fonts.d.ts.map +0 -1
  201. package/dist/studio-api/routes/fonts.js +0 -157
  202. package/dist/studio-api/routes/fonts.js.map +0 -1
  203. package/dist/studio-api/routes/lint.d.ts +0 -4
  204. package/dist/studio-api/routes/lint.d.ts.map +0 -1
  205. package/dist/studio-api/routes/lint.js +0 -29
  206. package/dist/studio-api/routes/lint.js.map +0 -1
  207. package/dist/studio-api/routes/preview.d.ts +0 -4
  208. package/dist/studio-api/routes/preview.d.ts.map +0 -1
  209. package/dist/studio-api/routes/preview.js +0 -331
  210. package/dist/studio-api/routes/preview.js.map +0 -1
  211. package/dist/studio-api/routes/projects.d.ts +0 -4
  212. package/dist/studio-api/routes/projects.d.ts.map +0 -1
  213. package/dist/studio-api/routes/projects.js +0 -45
  214. package/dist/studio-api/routes/projects.js.map +0 -1
  215. package/dist/studio-api/routes/registry.d.ts +0 -4
  216. package/dist/studio-api/routes/registry.d.ts.map +0 -1
  217. package/dist/studio-api/routes/registry.js +0 -31
  218. package/dist/studio-api/routes/registry.js.map +0 -1
  219. package/dist/studio-api/routes/render.d.ts +0 -4
  220. package/dist/studio-api/routes/render.d.ts.map +0 -1
  221. package/dist/studio-api/routes/render.js +0 -269
  222. package/dist/studio-api/routes/render.js.map +0 -1
  223. package/dist/studio-api/routes/storyboard.d.ts +0 -4
  224. package/dist/studio-api/routes/storyboard.d.ts.map +0 -1
  225. package/dist/studio-api/routes/storyboard.js +0 -65
  226. package/dist/studio-api/routes/storyboard.js.map +0 -1
  227. package/dist/studio-api/routes/thumbnail.d.ts +0 -4
  228. package/dist/studio-api/routes/thumbnail.d.ts.map +0 -1
  229. package/dist/studio-api/routes/thumbnail.js +0 -107
  230. package/dist/studio-api/routes/thumbnail.js.map +0 -1
  231. package/dist/studio-api/routes/waveform.d.ts +0 -4
  232. package/dist/studio-api/routes/waveform.d.ts.map +0 -1
  233. package/dist/studio-api/routes/waveform.js +0 -41
  234. package/dist/studio-api/routes/waveform.js.map +0 -1
  235. package/dist/studio-api/types.d.ts +0 -120
  236. package/dist/studio-api/types.d.ts.map +0 -1
  237. package/dist/studio-api/types.js +0 -2
  238. package/dist/studio-api/types.js.map +0 -1
@@ -1,1058 +1,3 @@
1
- // fallow-ignore-file code-duplication
2
- /**
3
- * Browser-safe GSAP read path — acorn + acorn-walk.
4
- *
5
- * T6b oracle: produces identical ParsedGsap output to gsapParser.ts (recast).
6
- * Replaces recast as the shared implementation once T6d passes.
7
- *
8
- * Write path (T6c) will add magic-string splice once read parity is confirmed.
9
- * No Node globals, no fs, no require — safe to bundle for browser use.
10
- */
11
- import * as acorn from "acorn";
12
- import * as acornWalk from "acorn-walk";
13
- import { classifyTweenPropertyGroup } from "./gsapConstants.js";
14
- import { buildArcPath } from "./gsapSerialize.js";
15
- import { inlineComputedTimelines, readProvenance } from "./gsapInline.js";
16
- // Browser-safe re-exports so studio code can build arc config without importing
17
- // the recast parser (this acorn module is the browser-safe gsap subpath).
18
- export { buildArcPath, editabilityForProvenance } from "./gsapSerialize.js";
19
- const GSAP_METHODS = new Set(["set", "to", "from", "fromTo"]);
20
- const QUERY_METHODS = new Set(["querySelector", "querySelectorAll"]);
21
- const ITERATION_METHODS = new Set(["forEach", "map"]);
22
- const SCOPE_NODE_TYPES = new Set([
23
- "Program",
24
- "FunctionDeclaration",
25
- "FunctionExpression",
26
- "ArrowFunctionExpression",
27
- ]);
28
- // ── Value resolution ─────────────────────────────────────────────────────────
29
- // fallow-ignore-next-line complexity
30
- function resolveNode(node, scope) {
31
- if (!node)
32
- return undefined;
33
- if (node.type === "NumericLiteral" || (node.type === "Literal" && typeof node.value === "number"))
34
- return node.value;
35
- if (node.type === "StringLiteral" || (node.type === "Literal" && typeof node.value === "string"))
36
- return node.value;
37
- if (node.type === "BooleanLiteral" ||
38
- (node.type === "Literal" && typeof node.value === "boolean"))
39
- return node.value;
40
- if (node.type === "UnaryExpression" && node.operator === "-" && node.argument) {
41
- const val = resolveNode(node.argument, scope);
42
- return typeof val === "number" ? -val : undefined;
43
- }
44
- if (node.type === "BinaryExpression") {
45
- const left = resolveNode(node.left, scope);
46
- const right = resolveNode(node.right, scope);
47
- if (typeof left === "number" && typeof right === "number") {
48
- switch (node.operator) {
49
- case "+":
50
- return left + right;
51
- case "-":
52
- return left - right;
53
- case "*":
54
- return left * right;
55
- case "/":
56
- return right !== 0 ? left / right : undefined;
57
- }
58
- }
59
- if (typeof left === "string" && node.operator === "+")
60
- return left + String(right ?? "");
61
- if (typeof right === "string" && node.operator === "+")
62
- return String(left ?? "") + right;
63
- }
64
- if (node.type === "Identifier" && scope.has(node.name)) {
65
- return scope.get(node.name);
66
- }
67
- if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
68
- return node.quasis?.[0]?.value?.cooked ?? undefined;
69
- }
70
- return undefined;
71
- }
72
- function extractLiteralValue(node, scope) {
73
- return resolveNode(node, scope);
74
- }
75
- // ── DOM selector resolution ───────────────────────────────────────────────────
76
- // fallow-ignore-next-line complexity
77
- function selectorFromQueryCall(node, scope) {
78
- if (node?.type !== "CallExpression")
79
- return null;
80
- const callee = node.callee;
81
- if (callee?.type !== "MemberExpression" || callee.property?.type !== "Identifier")
82
- return null;
83
- const method = callee.property.name;
84
- const argValue = resolveNode(node.arguments?.[0], scope);
85
- if (typeof argValue !== "string" || argValue.length === 0)
86
- return null;
87
- if (QUERY_METHODS.has(method) || method === "toArray")
88
- return argValue;
89
- if (method === "getElementById")
90
- return `#${argValue}`;
91
- return null;
92
- }
93
- // ── Ancestor-based scope helpers (replaces NodePath walking) ──────────────────
94
- /**
95
- * Return the nearest ancestor node whose type is in SCOPE_NODE_TYPES.
96
- * `ancestors` is the acorn-walk ancestor array (root→current, current is last).
97
- */
98
- function enclosingScopeNodeFromAncestors(ancestors) {
99
- for (let i = ancestors.length - 2; i >= 0; i--) {
100
- const node = ancestors[i];
101
- if (node && SCOPE_NODE_TYPES.has(node.type))
102
- return node;
103
- }
104
- return null;
105
- }
106
- /** Scope chain innermost-first, derived from the acorn-walk ancestors array. */
107
- function scopeChainFromAncestors(ancestors) {
108
- const chain = [];
109
- for (let i = ancestors.length - 1; i >= 0; i--) {
110
- const node = ancestors[i];
111
- if (node && SCOPE_NODE_TYPES.has(node.type))
112
- chain.push(node);
113
- }
114
- return chain;
115
- }
116
- // ── Target bindings ───────────────────────────────────────────────────────────
117
- function addBinding(bindings, scopeNode, name, selector) {
118
- let scoped = bindings.get(scopeNode);
119
- if (!scoped) {
120
- scoped = new Map();
121
- bindings.set(scopeNode, scoped);
122
- }
123
- if (!scoped.has(name))
124
- scoped.set(name, selector);
125
- }
126
- function lookupBindingFromAncestors(name, ancestors, bindings) {
127
- for (const scopeNode of scopeChainFromAncestors(ancestors)) {
128
- const selector = bindings.get(scopeNode)?.get(name);
129
- if (selector !== undefined)
130
- return selector;
131
- }
132
- // Program-scope bindings are stored under null (enclosingScopeNodeFromAncestors
133
- // returns null when no function wrapper exists — the common case in HF scripts).
134
- return bindings.get(null)?.get(name) ?? null;
135
- }
136
- function isFunctionNode(node) {
137
- return (node?.type === "ArrowFunctionExpression" ||
138
- node?.type === "FunctionExpression" ||
139
- node?.type === "FunctionDeclaration");
140
- }
141
- function resolveCollectionSelector(node, ancestors, scope, bindings) {
142
- if (node?.type === "Identifier")
143
- return lookupBindingFromAncestors(node.name, ancestors, bindings);
144
- if (node?.type === "CallExpression")
145
- return selectorFromQueryCall(node, scope);
146
- return null;
147
- }
148
- function collectScopeBindings(ast) {
149
- const bindings = new Map();
150
- acornWalk.simple(ast, {
151
- VariableDeclarator(node) {
152
- const name = node.id?.name;
153
- const init = node.init;
154
- if (name && init) {
155
- const val = resolveNode(init, bindings);
156
- if (val !== undefined)
157
- bindings.set(name, val);
158
- }
159
- },
160
- });
161
- return bindings;
162
- }
163
- /**
164
- * Build a lexically-scoped index of element variables → selector.
165
- * Pass 1: direct DOM-lookup assignments.
166
- * Pass 2: forEach/map callback params whose collection's selector is known.
167
- */
168
- function collectTargetBindings(ast, scope) {
169
- const bindings = new Map();
170
- acornWalk.ancestor(ast, {
171
- VariableDeclarator(node, _, ancestors) {
172
- const name = node.id?.name;
173
- const selector = selectorFromQueryCall(node.init, scope);
174
- if (name && selector !== null) {
175
- addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), name, selector);
176
- }
177
- },
178
- AssignmentExpression(node, _, ancestors) {
179
- const left = node.left;
180
- const selector = selectorFromQueryCall(node.right, scope);
181
- if (left?.type === "Identifier" && selector !== null) {
182
- addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), left.name, selector);
183
- }
184
- },
185
- });
186
- // Pass 2: forEach/map callback params take the collection's selector.
187
- acornWalk.ancestor(ast, {
188
- // fallow-ignore-next-line complexity
189
- CallExpression(node, _, ancestors) {
190
- const callee = node.callee;
191
- if (callee?.type === "MemberExpression" &&
192
- callee.property?.type === "Identifier" &&
193
- ITERATION_METHODS.has(callee.property.name)) {
194
- const collectionSelector = resolveCollectionSelector(callee.object, ancestors, scope, bindings);
195
- const fn = node.arguments?.[0];
196
- const param = fn?.params?.[0];
197
- if (collectionSelector && param?.type === "Identifier" && isFunctionNode(fn)) {
198
- addBinding(bindings, fn, param.name, collectionSelector);
199
- }
200
- }
201
- },
202
- });
203
- return bindings;
204
- }
205
- // fallow-ignore-next-line complexity
206
- function resolveTargetSelector(node, ancestors, scope, bindings) {
207
- if (!node)
208
- return null;
209
- if (node.type === "StringLiteral" || node.type === "Literal") {
210
- return typeof node.value === "string" ? node.value : null;
211
- }
212
- if (node.type === "Identifier") {
213
- return lookupBindingFromAncestors(node.name, ancestors, bindings);
214
- }
215
- if (node.type === "CallExpression") {
216
- return selectorFromQueryCall(node, scope);
217
- }
218
- if (node.type === "ArrayExpression") {
219
- const parts = node.elements
220
- .map((el) => resolveTargetSelector(el, ancestors, scope, bindings))
221
- .filter((s) => typeof s === "string" && s.length > 0);
222
- return parts.length > 0 ? parts.join(", ") : null;
223
- }
224
- if (node.type === "MemberExpression" && node.object?.type === "Identifier") {
225
- return lookupBindingFromAncestors(node.object.name, ancestors, bindings);
226
- }
227
- return null;
228
- }
229
- // ── ObjectExpression utilities ────────────────────────────────────────────────
230
- function isObjectProperty(prop) {
231
- return prop?.type === "ObjectProperty" || prop?.type === "Property";
232
- }
233
- function propKeyName(prop) {
234
- return prop?.key?.name ?? prop?.key?.value;
235
- }
236
- function findPropertyNode(varsArgNode, key) {
237
- if (varsArgNode?.type !== "ObjectExpression")
238
- return undefined;
239
- for (const prop of varsArgNode.properties ?? []) {
240
- if (!isObjectProperty(prop))
241
- continue;
242
- if (propKeyName(prop) === key)
243
- return prop.value;
244
- }
245
- return undefined;
246
- }
247
- /**
248
- * Extract raw source text for a property value — the offset-splice primitive.
249
- * Equivalent to `recast.print(node).code` for unmodified nodes.
250
- */
251
- function extractRawPropertySource(varsArgNode, key, source) {
252
- const node = findPropertyNode(varsArgNode, key);
253
- return node ? source.slice(node.start, node.end) : undefined;
254
- }
255
- // fallow-ignore-next-line complexity
256
- function objectExpressionToRecord(node, scope, source) {
257
- const result = {};
258
- if (node?.type !== "ObjectExpression")
259
- return result;
260
- for (const prop of node.properties ?? []) {
261
- if (!isObjectProperty(prop))
262
- continue;
263
- const key = prop.key?.name ?? prop.key?.value;
264
- if (!key)
265
- continue;
266
- const resolved = resolveNode(prop.value, scope);
267
- if (resolved !== undefined) {
268
- result[key] = resolved;
269
- }
270
- else {
271
- result[key] = `__raw:${source.slice(prop.value.start, prop.value.end)}`;
272
- }
273
- }
274
- return result;
275
- }
276
- // ── Timeline detection ────────────────────────────────────────────────────────
277
- function isGsapTimelineCall(node) {
278
- return (node?.type === "CallExpression" &&
279
- node.callee?.type === "MemberExpression" &&
280
- node.callee.object?.name === "gsap" &&
281
- node.callee.property?.name === "timeline");
282
- }
283
- // fallow-ignore-next-line complexity
284
- function extractTimelineDefaults(callNode, scope) {
285
- const arg = callNode.arguments?.[0];
286
- if (!arg || arg.type !== "ObjectExpression")
287
- return undefined;
288
- const defaultsProp = arg.properties?.find((p) => isObjectProperty(p) && propKeyName(p) === "defaults");
289
- if (!defaultsProp?.value || defaultsProp.value.type !== "ObjectExpression")
290
- return undefined;
291
- const result = {};
292
- for (const prop of defaultsProp.value.properties ?? []) {
293
- if (!isObjectProperty(prop))
294
- continue;
295
- const key = propKeyName(prop);
296
- const val = resolveNode(prop.value, scope);
297
- if (key === "ease" && typeof val === "string")
298
- result.ease = val;
299
- if (key === "duration" && typeof val === "number")
300
- result.duration = val;
301
- }
302
- return Object.keys(result).length > 0 ? result : undefined;
303
- }
304
- function findTimelineVar(ast, scope) {
305
- let timelineVar = null;
306
- let timelineCount = 0;
307
- let defaults;
308
- const emptyScope = scope ?? new Map();
309
- acornWalk.simple(ast, {
310
- VariableDeclarator(node) {
311
- if (isGsapTimelineCall(node.init)) {
312
- timelineCount += 1;
313
- if (!timelineVar) {
314
- timelineVar = node.id?.name ?? null;
315
- defaults = extractTimelineDefaults(node.init, emptyScope);
316
- }
317
- }
318
- },
319
- AssignmentExpression(node) {
320
- if (isGsapTimelineCall(node.right)) {
321
- timelineCount += 1;
322
- if (!timelineVar) {
323
- const left = node.left;
324
- if (left?.type === "Identifier")
325
- timelineVar = left.name;
326
- defaults = extractTimelineDefaults(node.right, emptyScope);
327
- }
328
- }
329
- },
330
- });
331
- return { timelineVar, timelineCount, defaults };
332
- }
333
- // ── Tween call collection ─────────────────────────────────────────────────────
334
- /** Keys stored on dedicated GsapAnimation fields (not in properties/extras). */
335
- const BUILTIN_VAR_KEYS = new Set(["duration", "ease", "delay"]);
336
- /** Keys never preserved (callbacks / advanced patterns). */
337
- const DROPPED_VAR_KEYS = new Set(["onComplete", "onStart", "onUpdate", "onRepeat"]);
338
- /** Keys that go in `extras` — non-editable GSAP config that must survive round-trips. */
339
- const EXTRAS_KEYS = new Set([
340
- "stagger",
341
- "yoyo",
342
- "repeat",
343
- "repeatDelay",
344
- "snap",
345
- "overwrite",
346
- "immediateRender",
347
- ]);
348
- /** True when callee chain is rooted at the timeline variable. */
349
- function isTimelineRootedCall(callNode, timelineVar) {
350
- let obj = callNode.callee?.object;
351
- while (obj?.type === "CallExpression") {
352
- obj = obj.callee?.object;
353
- }
354
- return obj?.type === "Identifier" && obj.name === timelineVar;
355
- }
356
- /**
357
- * Pre-order recursive walk for tween collection.
358
- *
359
- * acorn-walk is POST-order (visitor fires after children), which reverses
360
- * chained calls vs recast.types.visit (PRE-order). We need pre-order to
361
- * match the golden ordering where the outermost chained call appears first.
362
- */
363
- function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
364
- const results = [];
365
- // fallow-ignore-next-line complexity
366
- function visit(node, ancestors) {
367
- if (!node || typeof node !== "object")
368
- return;
369
- const nodeAncestors = [...ancestors, node];
370
- // Fire BEFORE children (pre-order) so chained outer calls come first.
371
- if (node.type === "CallExpression") {
372
- const callee = node.callee;
373
- // A base `gsap.set("#sel", props)` is an off-timeline static hold — parse it as
374
- // an editable global `set` so a static value round-trips and re-edits in place.
375
- // STRING-LITERAL selectors only: variable-target holds stay surrounding source.
376
- const gsapSetArg = node.arguments?.[0];
377
- const isGlobalSet = callee?.type === "MemberExpression" &&
378
- callee.object?.type === "Identifier" &&
379
- callee.object.name === "gsap" &&
380
- callee.property?.type === "Identifier" &&
381
- callee.property.name === "set" &&
382
- (gsapSetArg?.type === "StringLiteral" ||
383
- (gsapSetArg?.type === "Literal" && typeof gsapSetArg.value === "string"));
384
- if (callee?.type === "MemberExpression" &&
385
- callee.property?.type === "Identifier" &&
386
- (isTimelineRootedCall(node, timelineVar) || isGlobalSet) &&
387
- GSAP_METHODS.has(callee.property.name)) {
388
- const method = callee.property.name;
389
- const args = node.arguments;
390
- const selectorValue = args.length >= 1
391
- ? (resolveTargetSelector(args[0], nodeAncestors, scope, targetBindings) ??
392
- "__unresolved__")
393
- : "__unresolved__";
394
- if (method === "fromTo" && args.length >= 3) {
395
- results.push({
396
- node,
397
- ancestors: nodeAncestors,
398
- method: "fromTo",
399
- selector: selectorValue,
400
- fromArg: args[1],
401
- varsArg: args[2],
402
- positionArg: args[3],
403
- });
404
- }
405
- else if (method !== "fromTo" && args.length >= 2) {
406
- results.push({
407
- node,
408
- ancestors: nodeAncestors,
409
- method: method,
410
- selector: selectorValue,
411
- varsArg: args[1],
412
- positionArg: args[2],
413
- ...(isGlobalSet ? { global: true } : {}),
414
- });
415
- }
416
- }
417
- }
418
- // Traverse children. Object.keys preserves insertion order, so callee
419
- // comes before arguments in acorn's CallExpression nodes.
420
- for (const key of Object.keys(node)) {
421
- if (key === "type" || key === "start" || key === "end" || key === "loc")
422
- continue;
423
- const child = node[key];
424
- if (Array.isArray(child)) {
425
- for (const item of child) {
426
- if (item && typeof item === "object" && item.type)
427
- visit(item, nodeAncestors);
428
- }
429
- }
430
- else if (child && typeof child === "object" && child.type) {
431
- visit(child, nodeAncestors);
432
- }
433
- }
434
- }
435
- visit(ast, []);
436
- return results;
437
- }
438
- // ── Keyframes parsing ─────────────────────────────────────────────────────────
439
- const PERCENTAGE_KEY_RE = /^(\d+(?:\.\d+)?)%$/;
440
- function tryResolveStringProp(propValue, scope) {
441
- const val = resolveNode(propValue, scope);
442
- return typeof val === "string" ? val : undefined;
443
- }
444
- // fallow-ignore-next-line complexity
445
- function parsePercentageKeyframes(node, scope, source) {
446
- const keyframes = [];
447
- let ease;
448
- let easeEach;
449
- for (const prop of node.properties ?? []) {
450
- if (prop.type !== "ObjectProperty" && prop.type !== "Property")
451
- continue;
452
- const key = prop.key?.value ?? prop.key?.name;
453
- if (typeof key !== "string")
454
- continue;
455
- const pctMatch = PERCENTAGE_KEY_RE.exec(key);
456
- if (pctMatch) {
457
- const percentage = Number.parseFloat(pctMatch[1] ?? "0");
458
- const record = objectExpressionToRecord(prop.value, scope, source);
459
- const properties = {};
460
- let kfEase;
461
- for (const [k, v] of Object.entries(record)) {
462
- if (k === "ease" && typeof v === "string") {
463
- kfEase = v;
464
- }
465
- else if (typeof v === "number" || typeof v === "string") {
466
- properties[k] = v;
467
- }
468
- }
469
- keyframes.push({ percentage, properties, ...(kfEase ? { ease: kfEase } : {}) });
470
- }
471
- else if (key === "ease") {
472
- ease = tryResolveStringProp(prop.value, scope) ?? ease;
473
- }
474
- else if (key === "easeEach") {
475
- easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
476
- }
477
- }
478
- keyframes.sort((a, b) => a.percentage - b.percentage);
479
- return {
480
- format: "percentage",
481
- keyframes,
482
- ...(ease ? { ease } : {}),
483
- ...(easeEach ? { easeEach } : {}),
484
- };
485
- }
486
- // fallow-ignore-next-line complexity
487
- function computeKeyframesTotalDuration(varsNode, scope, source) {
488
- const kfNode = (varsNode.properties ?? []).find((p) => (p.key?.name ?? p.key?.value) === "keyframes")?.value;
489
- if (!kfNode || kfNode.type !== "ArrayExpression")
490
- return undefined;
491
- let total = 0;
492
- for (const el of kfNode.elements ?? []) {
493
- if (!el || el.type !== "ObjectExpression")
494
- continue;
495
- const r = objectExpressionToRecord(el, scope, source);
496
- if (typeof r.duration === "number")
497
- total += r.duration;
498
- }
499
- return total > 0 ? total : undefined;
500
- }
501
- // fallow-ignore-next-line complexity
502
- function parseObjectArrayKeyframes(node, scope, source) {
503
- const elements = node.elements ?? [];
504
- const raw = [];
505
- for (const el of elements) {
506
- if (!el || el.type !== "ObjectExpression")
507
- continue;
508
- const record = objectExpressionToRecord(el, scope, source);
509
- const properties = {};
510
- let duration;
511
- let ease;
512
- for (const [k, v] of Object.entries(record)) {
513
- if (k === "duration" && typeof v === "number") {
514
- duration = v;
515
- }
516
- else if (k === "ease" && typeof v === "string") {
517
- ease = v;
518
- }
519
- else if (typeof v === "number" || typeof v === "string") {
520
- properties[k] = v;
521
- }
522
- }
523
- raw.push({ properties, duration, ease });
524
- }
525
- const totalDuration = raw.reduce((sum, r) => sum + (r.duration ?? 0), 0);
526
- const keyframes = [];
527
- if (totalDuration > 0) {
528
- let cumulative = 0;
529
- for (const entry of raw) {
530
- cumulative += entry.duration ?? 0;
531
- const percentage = Math.round((cumulative / totalDuration) * 100);
532
- keyframes.push({
533
- percentage,
534
- properties: entry.properties,
535
- ...(entry.ease ? { ease: entry.ease } : {}),
536
- });
537
- }
538
- }
539
- else {
540
- for (let i = 0; i < raw.length; i++) {
541
- const entry = raw[i];
542
- if (!entry)
543
- continue;
544
- const percentage = raw.length > 1 ? Math.round((i / (raw.length - 1)) * 100) : 0;
545
- keyframes.push({
546
- percentage,
547
- properties: entry.properties,
548
- ...(entry.ease ? { ease: entry.ease } : {}),
549
- });
550
- }
551
- }
552
- return { format: "object-array", keyframes };
553
- }
554
- // fallow-ignore-next-line complexity
555
- function parseSimpleArrayKeyframes(node, scope) {
556
- const arrayProps = new Map();
557
- let ease;
558
- let easeEach;
559
- for (const prop of node.properties ?? []) {
560
- if (prop.type !== "ObjectProperty" && prop.type !== "Property")
561
- continue;
562
- const key = prop.key?.name ?? prop.key?.value;
563
- if (typeof key !== "string")
564
- continue;
565
- if (prop.value?.type === "ArrayExpression") {
566
- const values = [];
567
- for (const el of prop.value.elements ?? []) {
568
- const val = resolveNode(el, scope);
569
- if (typeof val === "number" || typeof val === "string") {
570
- values.push(val);
571
- }
572
- }
573
- if (values.length > 0)
574
- arrayProps.set(key, values);
575
- }
576
- else if (key === "ease") {
577
- ease = tryResolveStringProp(prop.value, scope) ?? ease;
578
- }
579
- else if (key === "easeEach") {
580
- easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
581
- }
582
- }
583
- const maxLen = Math.max(...[...arrayProps.values()].map((a) => a.length), 0);
584
- const keyframes = [];
585
- for (let i = 0; i < maxLen; i++) {
586
- const percentage = maxLen > 1 ? Math.round((i / (maxLen - 1)) * 100) : 0;
587
- const properties = {};
588
- for (const [key, values] of arrayProps) {
589
- if (i < values.length)
590
- properties[key] = values[i];
591
- }
592
- keyframes.push({ percentage, properties });
593
- }
594
- return {
595
- format: "simple-array",
596
- keyframes,
597
- ...(ease ? { ease } : {}),
598
- ...(easeEach ? { easeEach } : {}),
599
- };
600
- }
601
- // fallow-ignore-next-line complexity
602
- function parseKeyframesNode(node, scope, source) {
603
- if (!node)
604
- return undefined;
605
- if (node.type === "ArrayExpression") {
606
- return parseObjectArrayKeyframes(node, scope, source);
607
- }
608
- if (node.type !== "ObjectExpression")
609
- return undefined;
610
- const props = node.properties ?? [];
611
- let hasPercentageKey = false;
612
- let hasArrayValue = false;
613
- for (const prop of props) {
614
- if (prop.type !== "ObjectProperty" && prop.type !== "Property")
615
- continue;
616
- const key = prop.key?.value ?? prop.key?.name;
617
- if (typeof key === "string" && PERCENTAGE_KEY_RE.test(key)) {
618
- hasPercentageKey = true;
619
- break;
620
- }
621
- if (prop.value?.type === "ArrayExpression") {
622
- hasArrayValue = true;
623
- }
624
- }
625
- if (hasPercentageKey)
626
- return parsePercentageKeyframes(node, scope, source);
627
- if (hasArrayValue)
628
- return parseSimpleArrayKeyframes(node, scope);
629
- return undefined;
630
- }
631
- // fallow-ignore-next-line complexity
632
- function parseMotionPathNode(node, scope, source) {
633
- if (!node)
634
- return undefined;
635
- let pathNode;
636
- let autoRotate = false;
637
- let curviness = 1;
638
- let isCubic = false;
639
- if (node.type === "ObjectExpression") {
640
- for (const prop of node.properties ?? []) {
641
- if (!isObjectProperty(prop))
642
- continue;
643
- const key = propKeyName(prop);
644
- if (key === "path")
645
- pathNode = prop.value;
646
- else if (key === "autoRotate") {
647
- const val = resolveNode(prop.value, scope);
648
- autoRotate = typeof val === "number" ? val : val === true;
649
- }
650
- else if (key === "curviness") {
651
- const val = resolveNode(prop.value, scope);
652
- if (typeof val === "number")
653
- curviness = val;
654
- }
655
- else if (key === "type") {
656
- const val = resolveNode(prop.value, scope);
657
- if (val === "cubic")
658
- isCubic = true;
659
- }
660
- }
661
- }
662
- else if (node.type === "ArrayExpression") {
663
- pathNode = node;
664
- }
665
- if (!pathNode || pathNode.type !== "ArrayExpression")
666
- return undefined;
667
- const elements = pathNode.elements ?? [];
668
- const coords = [];
669
- for (const elem of elements) {
670
- if (!elem || elem.type !== "ObjectExpression")
671
- continue;
672
- const rec = objectExpressionToRecord(elem, scope, source);
673
- const x = typeof rec.x === "number" ? rec.x : undefined;
674
- const y = typeof rec.y === "number" ? rec.y : undefined;
675
- if (x !== undefined && y !== undefined)
676
- coords.push({ x, y });
677
- }
678
- return buildArcPath(coords, curviness, autoRotate, isCubic);
679
- }
680
- // ── Animation assembly ────────────────────────────────────────────────────────
681
- // fallow-ignore-next-line complexity
682
- function tweenCallToAnimation(call, scope, source) {
683
- const vars = objectExpressionToRecord(call.varsArg, scope, source);
684
- const properties = {};
685
- const extras = {};
686
- let keyframesData;
687
- let hasUnresolvedKeyframes = false;
688
- let motionPathResult;
689
- for (const [key, val] of Object.entries(vars)) {
690
- if (BUILTIN_VAR_KEYS.has(key))
691
- continue;
692
- if (DROPPED_VAR_KEYS.has(key))
693
- continue;
694
- if (key === "keyframes") {
695
- const kfNode = findPropertyNode(call.varsArg, "keyframes");
696
- keyframesData = parseKeyframesNode(kfNode, scope, source);
697
- if (!keyframesData && kfNode)
698
- hasUnresolvedKeyframes = true;
699
- continue;
700
- }
701
- if (key === "motionPath") {
702
- const mpNode = findPropertyNode(call.varsArg, "motionPath");
703
- motionPathResult = parseMotionPathNode(mpNode, scope, source);
704
- continue;
705
- }
706
- if (key === "easeEach")
707
- continue;
708
- if (EXTRAS_KEYS.has(key)) {
709
- const rawSource = extractRawPropertySource(call.varsArg, key, source);
710
- if (rawSource !== undefined) {
711
- extras[key] = `__raw:${rawSource}`;
712
- }
713
- else if (val !== undefined) {
714
- extras[key] = val;
715
- }
716
- continue;
717
- }
718
- if (typeof val === "number" || typeof val === "string") {
719
- properties[key] = val;
720
- }
721
- }
722
- if (keyframesData && typeof vars.easeEach === "string") {
723
- keyframesData.easeEach = vars.easeEach;
724
- }
725
- if (motionPathResult) {
726
- const { waypoints } = motionPathResult;
727
- if (!keyframesData) {
728
- const kf = waypoints.map((wp, i) => ({
729
- percentage: waypoints.length > 1 ? Math.round((i / (waypoints.length - 1)) * 100) : 0,
730
- properties: { x: wp.x, y: wp.y },
731
- }));
732
- keyframesData = { format: "percentage", keyframes: kf };
733
- }
734
- else {
735
- const kfs = keyframesData.keyframes;
736
- if (kfs.length === waypoints.length) {
737
- for (let i = 0; i < kfs.length; i++) {
738
- const kf = kfs[i];
739
- const wp = waypoints[i];
740
- if (kf && wp) {
741
- kf.properties.x = wp.x;
742
- kf.properties.y = wp.y;
743
- }
744
- }
745
- }
746
- }
747
- }
748
- let fromProperties;
749
- if (call.method === "fromTo" && call.fromArg) {
750
- fromProperties = {};
751
- const fromVars = objectExpressionToRecord(call.fromArg, scope, source);
752
- for (const [key, val] of Object.entries(fromVars)) {
753
- if (typeof val === "number" || typeof val === "string") {
754
- fromProperties[key] = val;
755
- }
756
- }
757
- }
758
- const hasPositionArg = !!call.positionArg;
759
- const posVal = hasPositionArg ? extractLiteralValue(call.positionArg, scope) : 0;
760
- const position = typeof posVal === "number" ? posVal : typeof posVal === "string" ? posVal : 0;
761
- let duration = typeof vars.duration === "number" ? vars.duration : undefined;
762
- const ease = typeof vars.ease === "string" ? vars.ease : undefined;
763
- if (duration === undefined && keyframesData) {
764
- duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
765
- }
766
- const anim = {
767
- targetSelector: call.selector,
768
- method: call.method,
769
- position,
770
- properties,
771
- fromProperties,
772
- duration,
773
- ease,
774
- };
775
- if (!hasPositionArg)
776
- anim.implicitPosition = true;
777
- let group = classifyTweenPropertyGroup(properties);
778
- if (!group && keyframesData) {
779
- const kfProps = {};
780
- for (const kf of keyframesData.keyframes) {
781
- for (const k of Object.keys(kf.properties))
782
- kfProps[k] = true;
783
- }
784
- group = classifyTweenPropertyGroup(kfProps);
785
- }
786
- if (group)
787
- anim.propertyGroup = group;
788
- if (call.global)
789
- anim.global = true;
790
- if (Object.keys(extras).length > 0)
791
- anim.extras = extras;
792
- if (keyframesData)
793
- anim.keyframes = keyframesData;
794
- if (motionPathResult)
795
- anim.arcPath = motionPathResult.arcPath;
796
- if (hasUnresolvedKeyframes)
797
- anim.hasUnresolvedKeyframes = true;
798
- if (call.selector === "__unresolved__")
799
- anim.hasUnresolvedSelector = true;
800
- const provenance = readProvenance(call.node);
801
- if (provenance)
802
- anim.provenance = provenance;
803
- return anim;
804
- }
805
- // ── Timeline position resolution ─────────────────────────────────────────────
806
- const GSAP_DEFAULT_DURATION = 0.5;
807
- // fallow-ignore-next-line complexity
808
- function resolvePositionString(pos, cursor, prevStart) {
809
- const trimmed = pos.trim();
810
- if (trimmed === "")
811
- return cursor;
812
- if (trimmed.startsWith("+=")) {
813
- const n = Number.parseFloat(trimmed.slice(2));
814
- return Number.isFinite(n) ? cursor + n : null;
815
- }
816
- if (trimmed.startsWith("-=")) {
817
- const n = Number.parseFloat(trimmed.slice(2));
818
- return Number.isFinite(n) ? cursor - n : null;
819
- }
820
- if (trimmed === "<")
821
- return prevStart;
822
- if (trimmed === ">")
823
- return cursor;
824
- if (trimmed.startsWith("<")) {
825
- const n = Number.parseFloat(trimmed.slice(1));
826
- return Number.isFinite(n) ? prevStart + n : null;
827
- }
828
- if (trimmed.startsWith(">")) {
829
- const n = Number.parseFloat(trimmed.slice(1));
830
- return Number.isFinite(n) ? cursor + n : null;
831
- }
832
- const n = Number.parseFloat(trimmed);
833
- return Number.isFinite(n) ? n : null;
834
- }
835
- function applyTimelineDefaults(anims, defaults) {
836
- if (!defaults)
837
- return;
838
- for (const anim of anims) {
839
- if (anim.method === "set")
840
- continue;
841
- if (anim.duration === undefined && defaults.duration !== undefined) {
842
- anim.duration = defaults.duration;
843
- }
844
- if (anim.ease === undefined && defaults.ease !== undefined) {
845
- anim.ease = defaults.ease;
846
- }
847
- }
848
- }
849
- // fallow-ignore-next-line complexity
850
- function resolveTimelinePositions(anims) {
851
- let cursor = 0;
852
- let prevStart = 0;
853
- for (const anim of anims) {
854
- // A global `gsap.set(...)` is off-timeline — applied once at load, not
855
- // sequenced on the master timeline. It carries no position arg, so the
856
- // cursor fallback would otherwise hand it the comp-end time. Pin it to 0
857
- // (its load-time start) and don't advance the cursor/prevStart.
858
- if (anim.method === "set" && anim.global) {
859
- anim.resolvedStart = 0;
860
- continue;
861
- }
862
- const duration = anim.method === "set" ? 0 : (anim.duration ?? GSAP_DEFAULT_DURATION);
863
- let start;
864
- if (anim.implicitPosition) {
865
- start = cursor;
866
- }
867
- else if (typeof anim.position === "number") {
868
- start = anim.position;
869
- }
870
- else if (typeof anim.position === "string") {
871
- start = resolvePositionString(anim.position, cursor, prevStart);
872
- }
873
- else {
874
- start = cursor;
875
- }
876
- if (start != null) {
877
- anim.resolvedStart = Math.max(0, start);
878
- prevStart = anim.resolvedStart;
879
- cursor = Math.max(cursor, anim.resolvedStart + duration);
880
- }
881
- }
882
- }
883
- function compareByLoc(a, b) {
884
- const aLoc = a.node.callee?.property?.loc?.start;
885
- const bLoc = b.node.callee?.property?.loc?.start;
886
- if (!aLoc || !bLoc)
887
- return 0;
888
- return aLoc.line - bLoc.line || aLoc.column - bLoc.column;
889
- }
890
- // Inlined tweens carry a monotonic __hfOrder (clones share source loc, so loc
891
- // can't order them); they sort by that, after all literal (loc-ordered) tweens.
892
- function compareCallOrder(a, b) {
893
- const ao = a.node.__hfOrder;
894
- const bo = b.node.__hfOrder;
895
- if (ao === undefined && bo === undefined)
896
- return compareByLoc(a, b);
897
- if (ao === undefined)
898
- return -1;
899
- if (bo === undefined)
900
- return 1;
901
- return ao - bo;
902
- }
903
- function sortBySourcePosition(calls) {
904
- calls.sort(compareCallOrder);
905
- }
906
- // ── Stable ID generation ──────────────────────────────────────────────────────
907
- function assignStableIds(anims) {
908
- const counts = new Map();
909
- return anims.map((anim) => {
910
- const posKey = typeof anim.position === "number"
911
- ? String(Math.round(anim.position * 1000))
912
- : String(anim.position);
913
- const groupSuffix = anim.propertyGroup ? `-${anim.propertyGroup}` : "";
914
- const base = `${anim.targetSelector}-${anim.method}-${posKey}${groupSuffix}`;
915
- const count = (counts.get(base) ?? 0) + 1;
916
- counts.set(base, count);
917
- const id = count === 1 ? base : `${base}-${count}`;
918
- return { ...anim, id };
919
- });
920
- }
921
- /**
922
- * Parse a GSAP script and return internal AST + call nodes for the write path.
923
- * Consumed by gsapWriterAcorn.ts (magic-string offset-splice).
924
- */
925
- export function parseGsapScriptAcornForWrite(script) {
926
- try {
927
- const ast = acorn.parse(script, {
928
- ecmaVersion: "latest",
929
- sourceType: "script",
930
- locations: true,
931
- });
932
- const scope = collectScopeBindings(ast);
933
- const targetBindings = collectTargetBindings(ast, scope);
934
- const detection = findTimelineVar(ast, scope);
935
- const timelineVar = detection.timelineVar ?? "tl";
936
- const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
937
- sortBySourcePosition(calls);
938
- const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
939
- applyTimelineDefaults(rawAnims, detection.defaults);
940
- resolveTimelinePositions(rawAnims);
941
- const animations = assignStableIds(rawAnims);
942
- const located = calls.map((call, i) => ({
943
- id: animations[i].id,
944
- call,
945
- animation: animations[i],
946
- }));
947
- return { ast, timelineVar, hasTimeline: detection.timelineVar !== null, located };
948
- }
949
- catch {
950
- return null;
951
- }
952
- }
953
- // ── Public API ────────────────────────────────────────────────────────────────
954
- /**
955
- * Browser-safe equivalent of `parseGsapScript` (gsapParser.ts).
956
- * Uses acorn + acorn-walk instead of recast + @babel/parser.
957
- */
958
- export function parseGsapScriptAcorn(script) {
959
- try {
960
- const ast = acorn.parse(script, {
961
- ecmaVersion: "latest",
962
- sourceType: "script",
963
- locations: true,
964
- });
965
- const scope = collectScopeBindings(ast);
966
- const detection = findTimelineVar(ast, scope);
967
- const timelineVar = detection.timelineVar ?? "tl";
968
- // Expand helper-built / bounded-loop timelines before analysis so their
969
- // tweens resolve at true positions (read path only — the write path keeps
970
- // original source nodes). Degrades to the un-inlined AST on any failure.
971
- try {
972
- inlineComputedTimelines(ast, timelineVar, (node) => resolveNode(node, scope));
973
- }
974
- catch {
975
- /* fall back to current behavior */
976
- }
977
- const targetBindings = collectTargetBindings(ast, scope);
978
- const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
979
- sortBySourcePosition(calls);
980
- const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
981
- applyTimelineDefaults(rawAnims, detection.defaults);
982
- resolveTimelinePositions(rawAnims);
983
- const animations = assignStableIds(rawAnims);
984
- const timelineMatch = script.match(new RegExp(`^[\\s\\S]*?(?:const|let|var)\\s+${timelineVar}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?`));
985
- const preamble = timelineMatch?.[0] ?? `const ${timelineVar} = gsap.timeline({ paused: true });`;
986
- const lastCallIdx = script.lastIndexOf(`${timelineVar}.`);
987
- let postamble = "";
988
- if (lastCallIdx !== -1) {
989
- const afterLast = script.slice(lastCallIdx);
990
- const endOfCall = afterLast.indexOf(";");
991
- if (endOfCall !== -1) {
992
- postamble = script.slice(lastCallIdx + endOfCall + 1).trim();
993
- }
994
- }
995
- const result = { animations, timelineVar, preamble, postamble };
996
- if (detection.timelineCount > 1)
997
- result.multipleTimelines = true;
998
- if (detection.timelineCount > 0 && detection.timelineVar === null)
999
- result.unsupportedTimelinePattern = true;
1000
- return result;
1001
- }
1002
- catch {
1003
- return { animations: [], timelineVar: "tl", preamble: "", postamble: "" };
1004
- }
1005
- }
1006
- /**
1007
- * Extract all `tl.addLabel("name", position)` calls from a GSAP script.
1008
- *
1009
- * Returns labels in source order. Position must be a numeric literal; labels
1010
- * with non-numeric positions (e.g. label-relative offsets) are skipped.
1011
- *
1012
- * Pure — no side effects, no DOM, no Date.now.
1013
- */
1014
- export function extractGsapLabels(script) {
1015
- try {
1016
- const ast = acorn.parse(script, {
1017
- ecmaVersion: "latest",
1018
- sourceType: "script",
1019
- locations: true,
1020
- });
1021
- const scope = collectScopeBindings(ast);
1022
- const detection = findTimelineVar(ast, scope);
1023
- const timelineVar = detection.timelineVar ?? "tl";
1024
- const labels = [];
1025
- acornWalk.simple(ast, {
1026
- // fallow-ignore-next-line complexity
1027
- ExpressionStatement(node) {
1028
- const expr = node.expression;
1029
- if (!expr || expr.type !== "CallExpression")
1030
- return;
1031
- const callee = expr.callee;
1032
- // Match tl.addLabel(...)
1033
- if (callee?.type !== "MemberExpression" ||
1034
- callee.object?.name !== timelineVar ||
1035
- callee.property?.name !== "addLabel")
1036
- return;
1037
- const args = expr.arguments ?? [];
1038
- const nameNode = args[0];
1039
- const posNode = args[1];
1040
- if (nameNode?.type !== "Literal" || typeof nameNode.value !== "string")
1041
- return;
1042
- if (!posNode)
1043
- return;
1044
- const pos = resolveNode(posNode, scope);
1045
- if (typeof pos !== "number" || !Number.isFinite(pos))
1046
- return;
1047
- labels.push({ name: nameNode.value, position: pos });
1048
- },
1049
- });
1050
- return labels;
1051
- }
1052
- catch {
1053
- // Labels are best-effort/supplementary, not load-bearing — a malformed or
1054
- // unparseable script yields no labels rather than failing the caller.
1055
- return [];
1056
- }
1057
- }
1
+ /** @deprecated Import from @hyperframes/parsers/gsap-parser-acorn */
2
+ export * from "@hyperframes/parsers/gsap-parser-acorn";
1058
3
  //# sourceMappingURL=gsapParserAcorn.js.map