@hyperframes/parsers 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.
- package/LICENSE +190 -0
- package/dist/gsapConstants.d.ts +14 -0
- package/dist/gsapConstants.js +107 -0
- package/dist/gsapConstants.js.map +1 -0
- package/dist/gsapParser.d.ts +157 -0
- package/dist/gsapParser.js +2431 -0
- package/dist/gsapParser.js.map +1 -0
- package/dist/gsapParserAcorn.d.ts +51 -0
- package/dist/gsapParserAcorn.js +1294 -0
- package/dist/gsapParserAcorn.js.map +1 -0
- package/dist/gsapParserExports.d.ts +5 -0
- package/dist/gsapParserExports.js +1558 -0
- package/dist/gsapParserExports.js.map +1 -0
- package/dist/gsapSerialize-B_JRTCeV.d.ts +535 -0
- package/dist/gsapWriterAcorn.d.ts +93 -0
- package/dist/gsapWriterAcorn.js +2369 -0
- package/dist/gsapWriterAcorn.js.map +1 -0
- package/dist/hfIds.d.ts +18 -0
- package/dist/hfIds.js +74 -0
- package/dist/hfIds.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +2500 -0
- package/dist/index.js.map +1 -0
- package/dist/springEase.d.ts +30 -0
- package/dist/springEase.js +44 -0
- package/dist/springEase.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,2369 @@
|
|
|
1
|
+
// src/gsapWriterAcorn.ts
|
|
2
|
+
import MagicString from "magic-string";
|
|
3
|
+
|
|
4
|
+
// src/gsapConstants.ts
|
|
5
|
+
var PROPERTY_GROUPS = {
|
|
6
|
+
position: /* @__PURE__ */ new Set(["x", "y", "xPercent", "yPercent"]),
|
|
7
|
+
scale: /* @__PURE__ */ new Set(["scale", "scaleX", "scaleY"]),
|
|
8
|
+
size: /* @__PURE__ */ new Set(["width", "height"]),
|
|
9
|
+
rotation: /* @__PURE__ */ new Set(["rotation", "skewX", "skewY"]),
|
|
10
|
+
visual: /* @__PURE__ */ new Set(["opacity", "autoAlpha"]),
|
|
11
|
+
other: /* @__PURE__ */ new Set()
|
|
12
|
+
};
|
|
13
|
+
var PROP_TO_GROUP = /* @__PURE__ */ new Map();
|
|
14
|
+
for (const [group, props] of Object.entries(PROPERTY_GROUPS)) {
|
|
15
|
+
for (const p of props) PROP_TO_GROUP.set(p, group);
|
|
16
|
+
}
|
|
17
|
+
function classifyPropertyGroup(prop) {
|
|
18
|
+
return PROP_TO_GROUP.get(prop) ?? "other";
|
|
19
|
+
}
|
|
20
|
+
function classifyTweenPropertyGroup(properties) {
|
|
21
|
+
const groups = /* @__PURE__ */ new Set();
|
|
22
|
+
for (const key of Object.keys(properties)) {
|
|
23
|
+
if (key === "transformOrigin" || key === "_auto" || key === "data") continue;
|
|
24
|
+
const g = classifyPropertyGroup(key);
|
|
25
|
+
groups.add(g);
|
|
26
|
+
}
|
|
27
|
+
if (groups.size === 1) return groups.values().next().value;
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/gsapSerialize.ts
|
|
32
|
+
function buildArcPath(coords, curviness, autoRotate, isCubic) {
|
|
33
|
+
const first = coords[0];
|
|
34
|
+
if (coords.length < 2 || !first) return void 0;
|
|
35
|
+
const segments = [];
|
|
36
|
+
let waypoints;
|
|
37
|
+
if (isCubic && coords.length >= 4) {
|
|
38
|
+
waypoints = [first];
|
|
39
|
+
for (let i = 1; i + 2 < coords.length; i += 3) {
|
|
40
|
+
const cp1 = coords[i];
|
|
41
|
+
const cp2 = coords[i + 1];
|
|
42
|
+
const anchor = coords[i + 2];
|
|
43
|
+
if (!cp1 || !cp2 || !anchor) continue;
|
|
44
|
+
waypoints.push(anchor);
|
|
45
|
+
segments.push({ curviness, cp1, cp2 });
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
waypoints = coords;
|
|
49
|
+
for (let i = 0; i < waypoints.length - 1; i++) segments.push({ curviness });
|
|
50
|
+
}
|
|
51
|
+
return { arcPath: { enabled: true, autoRotate, segments }, waypoints };
|
|
52
|
+
}
|
|
53
|
+
var CSS_IDENTITY = {
|
|
54
|
+
opacity: 1,
|
|
55
|
+
autoAlpha: 1,
|
|
56
|
+
scale: 1,
|
|
57
|
+
scaleX: 1,
|
|
58
|
+
scaleY: 1
|
|
59
|
+
};
|
|
60
|
+
function cssIdentityValue(prop) {
|
|
61
|
+
return CSS_IDENTITY[prop] ?? 0;
|
|
62
|
+
}
|
|
63
|
+
function buildIdentityMap(props) {
|
|
64
|
+
const identity = {};
|
|
65
|
+
for (const [key, val] of Object.entries(props)) {
|
|
66
|
+
if (val != null) identity[key] = typeof val === "number" ? cssIdentityValue(key) : val;
|
|
67
|
+
}
|
|
68
|
+
return identity;
|
|
69
|
+
}
|
|
70
|
+
function resolveConversionProps(anim, resolvedFromValues) {
|
|
71
|
+
if (anim.method === "set") {
|
|
72
|
+
return { fromProps: { ...anim.properties }, toProps: { ...anim.properties } };
|
|
73
|
+
}
|
|
74
|
+
if (anim.method === "to") {
|
|
75
|
+
const identity = buildIdentityMap(anim.properties);
|
|
76
|
+
const fromProps = resolvedFromValues ? { ...identity, ...resolvedFromValues } : identity;
|
|
77
|
+
return { fromProps, toProps: { ...anim.properties } };
|
|
78
|
+
}
|
|
79
|
+
if (anim.method === "from") {
|
|
80
|
+
const identity = buildIdentityMap(anim.properties);
|
|
81
|
+
const toProps2 = resolvedFromValues ? { ...identity, ...resolvedFromValues } : identity;
|
|
82
|
+
return { fromProps: { ...anim.properties }, toProps: toProps2 };
|
|
83
|
+
}
|
|
84
|
+
const toProps = resolvedFromValues ? { ...anim.properties, ...resolvedFromValues } : { ...anim.properties };
|
|
85
|
+
return { fromProps: { ...anim.fromProperties ?? {} }, toProps };
|
|
86
|
+
}
|
|
87
|
+
function numericXY(props) {
|
|
88
|
+
const vx = props.x;
|
|
89
|
+
const vy = props.y;
|
|
90
|
+
return typeof vx === "number" && typeof vy === "number" ? { x: vx, y: vy } : null;
|
|
91
|
+
}
|
|
92
|
+
function extractArcWaypoints(anim) {
|
|
93
|
+
const keyframeWps = (anim.keyframes?.keyframes ?? []).map((kf) => numericXY(kf.properties)).filter((pt) => pt !== null);
|
|
94
|
+
if (keyframeWps.length >= 2) return keyframeWps;
|
|
95
|
+
const propX = anim.properties.x;
|
|
96
|
+
const propY = anim.properties.y;
|
|
97
|
+
if (typeof propX !== "number" && typeof propY !== "number") return keyframeWps;
|
|
98
|
+
const destX = typeof propX === "number" ? propX : 0;
|
|
99
|
+
const destY = typeof propY === "number" ? propY : 0;
|
|
100
|
+
return [
|
|
101
|
+
{ x: 0, y: 0 },
|
|
102
|
+
{ x: destX, y: destY }
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
function autoRotateSuffix(autoRotate) {
|
|
106
|
+
if (autoRotate === true) return ", autoRotate: true";
|
|
107
|
+
if (typeof autoRotate === "number") return `, autoRotate: ${autoRotate}`;
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
function cubicControlPoints(seg, wp, nextWp) {
|
|
111
|
+
if (seg.cp1 && seg.cp2) {
|
|
112
|
+
return [`{x: ${seg.cp1.x}, y: ${seg.cp1.y}}`, `{x: ${seg.cp2.x}, y: ${seg.cp2.y}}`];
|
|
113
|
+
}
|
|
114
|
+
const dx = nextWp.x - wp.x;
|
|
115
|
+
const dy = nextWp.y - wp.y;
|
|
116
|
+
const c = seg.curviness ?? 1;
|
|
117
|
+
return [
|
|
118
|
+
`{x: ${wp.x + dx * 0.33}, y: ${wp.y + dy * 0.33 - c * Math.abs(dx) * 0.25}}`,
|
|
119
|
+
`{x: ${wp.x + dx * 0.66}, y: ${wp.y + dy * 0.66 - c * Math.abs(dx) * 0.25}}`
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
function buildCubicPathEntries(waypoints, segments) {
|
|
123
|
+
const first = waypoints[0];
|
|
124
|
+
if (!first) return [];
|
|
125
|
+
const entries = [`{x: ${first.x}, y: ${first.y}}`];
|
|
126
|
+
for (let i = 0; i < segments.length; i++) {
|
|
127
|
+
const seg = segments[i];
|
|
128
|
+
const wp = waypoints[i];
|
|
129
|
+
const nextWp = waypoints[i + 1];
|
|
130
|
+
if (!seg || !wp || !nextWp) continue;
|
|
131
|
+
entries.push(...cubicControlPoints(seg, wp, nextWp));
|
|
132
|
+
entries.push(`{x: ${nextWp.x}, y: ${nextWp.y}}`);
|
|
133
|
+
}
|
|
134
|
+
return entries;
|
|
135
|
+
}
|
|
136
|
+
function buildMotionPathObjectCode(config) {
|
|
137
|
+
const { waypoints, segments, autoRotate } = config;
|
|
138
|
+
const arSuffix = autoRotateSuffix(autoRotate);
|
|
139
|
+
const hasExplicitCp = segments.some((s) => s.cp1 && s.cp2);
|
|
140
|
+
const curvinessVaries = segments.some(
|
|
141
|
+
(s) => (s.curviness ?? 1) !== (segments[0]?.curviness ?? 1)
|
|
142
|
+
);
|
|
143
|
+
if ((hasExplicitCp || curvinessVaries) && waypoints.length >= 2) {
|
|
144
|
+
const pathStr = buildCubicPathEntries(waypoints, segments).join(", ");
|
|
145
|
+
return `{ path: [${pathStr}], type: "cubic"${arSuffix} }`;
|
|
146
|
+
}
|
|
147
|
+
const pathEntries = waypoints.map((wp) => `{x: ${wp.x}, y: ${wp.y}}`);
|
|
148
|
+
const curviness = segments[0]?.curviness ?? 1;
|
|
149
|
+
const curvPart = curviness !== 1 ? `, curviness: ${curviness}` : "";
|
|
150
|
+
return `{ path: [${pathEntries.join(", ")}]${curvPart}${arSuffix} }`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/gsapParserAcorn.ts
|
|
154
|
+
import * as acorn from "acorn";
|
|
155
|
+
import * as acornWalk from "acorn-walk";
|
|
156
|
+
|
|
157
|
+
// src/gsapInline.ts
|
|
158
|
+
function readProvenance(node) {
|
|
159
|
+
return node?.__hfProvenance;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/gsapParserAcorn.ts
|
|
163
|
+
var GSAP_METHODS = /* @__PURE__ */ new Set(["set", "to", "from", "fromTo"]);
|
|
164
|
+
var QUERY_METHODS = /* @__PURE__ */ new Set(["querySelector", "querySelectorAll"]);
|
|
165
|
+
var ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map"]);
|
|
166
|
+
var SCOPE_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
167
|
+
"Program",
|
|
168
|
+
"FunctionDeclaration",
|
|
169
|
+
"FunctionExpression",
|
|
170
|
+
"ArrowFunctionExpression"
|
|
171
|
+
]);
|
|
172
|
+
function resolveNode(node, scope) {
|
|
173
|
+
if (!node) return void 0;
|
|
174
|
+
if (node.type === "NumericLiteral" || node.type === "Literal" && typeof node.value === "number")
|
|
175
|
+
return node.value;
|
|
176
|
+
if (node.type === "StringLiteral" || node.type === "Literal" && typeof node.value === "string")
|
|
177
|
+
return node.value;
|
|
178
|
+
if (node.type === "BooleanLiteral" || node.type === "Literal" && typeof node.value === "boolean")
|
|
179
|
+
return node.value;
|
|
180
|
+
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument) {
|
|
181
|
+
const val = resolveNode(node.argument, scope);
|
|
182
|
+
return typeof val === "number" ? -val : void 0;
|
|
183
|
+
}
|
|
184
|
+
if (node.type === "BinaryExpression") {
|
|
185
|
+
const left = resolveNode(node.left, scope);
|
|
186
|
+
const right = resolveNode(node.right, scope);
|
|
187
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
188
|
+
switch (node.operator) {
|
|
189
|
+
case "+":
|
|
190
|
+
return left + right;
|
|
191
|
+
case "-":
|
|
192
|
+
return left - right;
|
|
193
|
+
case "*":
|
|
194
|
+
return left * right;
|
|
195
|
+
case "/":
|
|
196
|
+
return right !== 0 ? left / right : void 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (typeof left === "string" && node.operator === "+") return left + String(right ?? "");
|
|
200
|
+
if (typeof right === "string" && node.operator === "+") return String(left ?? "") + right;
|
|
201
|
+
}
|
|
202
|
+
if (node.type === "Identifier" && scope.has(node.name)) {
|
|
203
|
+
return scope.get(node.name);
|
|
204
|
+
}
|
|
205
|
+
if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
|
|
206
|
+
return node.quasis?.[0]?.value?.cooked ?? void 0;
|
|
207
|
+
}
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
function extractLiteralValue(node, scope) {
|
|
211
|
+
return resolveNode(node, scope);
|
|
212
|
+
}
|
|
213
|
+
function selectorFromQueryCall(node, scope) {
|
|
214
|
+
if (node?.type !== "CallExpression") return null;
|
|
215
|
+
const callee = node.callee;
|
|
216
|
+
if (callee?.type !== "MemberExpression" || callee.property?.type !== "Identifier") return null;
|
|
217
|
+
const method = callee.property.name;
|
|
218
|
+
const argValue = resolveNode(node.arguments?.[0], scope);
|
|
219
|
+
if (typeof argValue !== "string" || argValue.length === 0) return null;
|
|
220
|
+
if (QUERY_METHODS.has(method) || method === "toArray") return argValue;
|
|
221
|
+
if (method === "getElementById") return `#${argValue}`;
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
function enclosingScopeNodeFromAncestors(ancestors) {
|
|
225
|
+
for (let i = ancestors.length - 2; i >= 0; i--) {
|
|
226
|
+
const node = ancestors[i];
|
|
227
|
+
if (node && SCOPE_NODE_TYPES.has(node.type)) return node;
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
function scopeChainFromAncestors(ancestors) {
|
|
232
|
+
const chain = [];
|
|
233
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
234
|
+
const node = ancestors[i];
|
|
235
|
+
if (node && SCOPE_NODE_TYPES.has(node.type)) chain.push(node);
|
|
236
|
+
}
|
|
237
|
+
return chain;
|
|
238
|
+
}
|
|
239
|
+
function addBinding(bindings, scopeNode, name, selector) {
|
|
240
|
+
let scoped = bindings.get(scopeNode);
|
|
241
|
+
if (!scoped) {
|
|
242
|
+
scoped = /* @__PURE__ */ new Map();
|
|
243
|
+
bindings.set(scopeNode, scoped);
|
|
244
|
+
}
|
|
245
|
+
if (!scoped.has(name)) scoped.set(name, selector);
|
|
246
|
+
}
|
|
247
|
+
function lookupBindingFromAncestors(name, ancestors, bindings) {
|
|
248
|
+
for (const scopeNode of scopeChainFromAncestors(ancestors)) {
|
|
249
|
+
const selector = bindings.get(scopeNode)?.get(name);
|
|
250
|
+
if (selector !== void 0) return selector;
|
|
251
|
+
}
|
|
252
|
+
return bindings.get(null)?.get(name) ?? null;
|
|
253
|
+
}
|
|
254
|
+
function isFunctionNode(node) {
|
|
255
|
+
return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression" || node?.type === "FunctionDeclaration";
|
|
256
|
+
}
|
|
257
|
+
function resolveCollectionSelector(node, ancestors, scope, bindings) {
|
|
258
|
+
if (node?.type === "Identifier")
|
|
259
|
+
return lookupBindingFromAncestors(node.name, ancestors, bindings);
|
|
260
|
+
if (node?.type === "CallExpression") return selectorFromQueryCall(node, scope);
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
function collectScopeBindings(ast) {
|
|
264
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
265
|
+
acornWalk.simple(ast, {
|
|
266
|
+
VariableDeclarator(node) {
|
|
267
|
+
const name = node.id?.name;
|
|
268
|
+
const init = node.init;
|
|
269
|
+
if (name && init) {
|
|
270
|
+
const val = resolveNode(init, bindings);
|
|
271
|
+
if (val !== void 0) bindings.set(name, val);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return bindings;
|
|
276
|
+
}
|
|
277
|
+
function collectTargetBindings(ast, scope) {
|
|
278
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
279
|
+
acornWalk.ancestor(ast, {
|
|
280
|
+
VariableDeclarator(node, _, ancestors) {
|
|
281
|
+
const name = node.id?.name;
|
|
282
|
+
const selector = selectorFromQueryCall(node.init, scope);
|
|
283
|
+
if (name && selector !== null) {
|
|
284
|
+
addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), name, selector);
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
AssignmentExpression(node, _, ancestors) {
|
|
288
|
+
const left = node.left;
|
|
289
|
+
const selector = selectorFromQueryCall(node.right, scope);
|
|
290
|
+
if (left?.type === "Identifier" && selector !== null) {
|
|
291
|
+
addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), left.name, selector);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
acornWalk.ancestor(ast, {
|
|
296
|
+
// fallow-ignore-next-line complexity
|
|
297
|
+
CallExpression(node, _, ancestors) {
|
|
298
|
+
const callee = node.callee;
|
|
299
|
+
if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && ITERATION_METHODS.has(callee.property.name)) {
|
|
300
|
+
const collectionSelector = resolveCollectionSelector(
|
|
301
|
+
callee.object,
|
|
302
|
+
ancestors,
|
|
303
|
+
scope,
|
|
304
|
+
bindings
|
|
305
|
+
);
|
|
306
|
+
const fn = node.arguments?.[0];
|
|
307
|
+
const param = fn?.params?.[0];
|
|
308
|
+
if (collectionSelector && param?.type === "Identifier" && isFunctionNode(fn)) {
|
|
309
|
+
addBinding(bindings, fn, param.name, collectionSelector);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
return bindings;
|
|
315
|
+
}
|
|
316
|
+
function resolveTargetSelector(node, ancestors, scope, bindings) {
|
|
317
|
+
if (!node) return null;
|
|
318
|
+
if (node.type === "StringLiteral" || node.type === "Literal") {
|
|
319
|
+
return typeof node.value === "string" ? node.value : null;
|
|
320
|
+
}
|
|
321
|
+
if (node.type === "Identifier") {
|
|
322
|
+
return lookupBindingFromAncestors(node.name, ancestors, bindings);
|
|
323
|
+
}
|
|
324
|
+
if (node.type === "CallExpression") {
|
|
325
|
+
return selectorFromQueryCall(node, scope);
|
|
326
|
+
}
|
|
327
|
+
if (node.type === "ArrayExpression") {
|
|
328
|
+
const parts = node.elements.map((el) => resolveTargetSelector(el, ancestors, scope, bindings)).filter((s) => typeof s === "string" && s.length > 0);
|
|
329
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
330
|
+
}
|
|
331
|
+
if (node.type === "MemberExpression" && node.object?.type === "Identifier") {
|
|
332
|
+
return lookupBindingFromAncestors(node.object.name, ancestors, bindings);
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
function isObjectProperty(prop) {
|
|
337
|
+
return prop?.type === "ObjectProperty" || prop?.type === "Property";
|
|
338
|
+
}
|
|
339
|
+
function propKeyName(prop) {
|
|
340
|
+
return prop?.key?.name ?? prop?.key?.value;
|
|
341
|
+
}
|
|
342
|
+
function findPropertyNode(varsArgNode, key) {
|
|
343
|
+
if (varsArgNode?.type !== "ObjectExpression") return void 0;
|
|
344
|
+
for (const prop of varsArgNode.properties ?? []) {
|
|
345
|
+
if (!isObjectProperty(prop)) continue;
|
|
346
|
+
if (propKeyName(prop) === key) return prop.value;
|
|
347
|
+
}
|
|
348
|
+
return void 0;
|
|
349
|
+
}
|
|
350
|
+
function extractRawPropertySource(varsArgNode, key, source) {
|
|
351
|
+
const node = findPropertyNode(varsArgNode, key);
|
|
352
|
+
return node ? source.slice(node.start, node.end) : void 0;
|
|
353
|
+
}
|
|
354
|
+
function objectExpressionToRecord(node, scope, source) {
|
|
355
|
+
const result = {};
|
|
356
|
+
if (node?.type !== "ObjectExpression") return result;
|
|
357
|
+
for (const prop of node.properties ?? []) {
|
|
358
|
+
if (!isObjectProperty(prop)) continue;
|
|
359
|
+
const key = prop.key?.name ?? prop.key?.value;
|
|
360
|
+
if (!key) continue;
|
|
361
|
+
const resolved = resolveNode(prop.value, scope);
|
|
362
|
+
if (resolved !== void 0) {
|
|
363
|
+
result[key] = resolved;
|
|
364
|
+
} else {
|
|
365
|
+
result[key] = `__raw:${source.slice(prop.value.start, prop.value.end)}`;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return result;
|
|
369
|
+
}
|
|
370
|
+
function isGsapTimelineCall(node) {
|
|
371
|
+
return node?.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.object?.name === "gsap" && node.callee.property?.name === "timeline";
|
|
372
|
+
}
|
|
373
|
+
function extractTimelineDefaults(callNode, scope) {
|
|
374
|
+
const arg = callNode.arguments?.[0];
|
|
375
|
+
if (!arg || arg.type !== "ObjectExpression") return void 0;
|
|
376
|
+
const defaultsProp = arg.properties?.find(
|
|
377
|
+
(p) => isObjectProperty(p) && propKeyName(p) === "defaults"
|
|
378
|
+
);
|
|
379
|
+
if (!defaultsProp?.value || defaultsProp.value.type !== "ObjectExpression") return void 0;
|
|
380
|
+
const result = {};
|
|
381
|
+
for (const prop of defaultsProp.value.properties ?? []) {
|
|
382
|
+
if (!isObjectProperty(prop)) continue;
|
|
383
|
+
const key = propKeyName(prop);
|
|
384
|
+
const val = resolveNode(prop.value, scope);
|
|
385
|
+
if (key === "ease" && typeof val === "string") result.ease = val;
|
|
386
|
+
if (key === "duration" && typeof val === "number") result.duration = val;
|
|
387
|
+
}
|
|
388
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
389
|
+
}
|
|
390
|
+
function findTimelineVar(ast, scope) {
|
|
391
|
+
let timelineVar = null;
|
|
392
|
+
let timelineCount = 0;
|
|
393
|
+
let defaults;
|
|
394
|
+
const emptyScope = scope ?? /* @__PURE__ */ new Map();
|
|
395
|
+
acornWalk.simple(ast, {
|
|
396
|
+
VariableDeclarator(node) {
|
|
397
|
+
if (isGsapTimelineCall(node.init)) {
|
|
398
|
+
timelineCount += 1;
|
|
399
|
+
if (!timelineVar) {
|
|
400
|
+
timelineVar = node.id?.name ?? null;
|
|
401
|
+
defaults = extractTimelineDefaults(node.init, emptyScope);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
AssignmentExpression(node) {
|
|
406
|
+
if (isGsapTimelineCall(node.right)) {
|
|
407
|
+
timelineCount += 1;
|
|
408
|
+
if (!timelineVar) {
|
|
409
|
+
const left = node.left;
|
|
410
|
+
if (left?.type === "Identifier") timelineVar = left.name;
|
|
411
|
+
defaults = extractTimelineDefaults(node.right, emptyScope);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
return { timelineVar, timelineCount, defaults };
|
|
417
|
+
}
|
|
418
|
+
var BUILTIN_VAR_KEYS = /* @__PURE__ */ new Set(["duration", "ease", "delay"]);
|
|
419
|
+
var DROPPED_VAR_KEYS = /* @__PURE__ */ new Set(["onComplete", "onStart", "onUpdate", "onRepeat"]);
|
|
420
|
+
var EXTRAS_KEYS = /* @__PURE__ */ new Set([
|
|
421
|
+
"stagger",
|
|
422
|
+
"yoyo",
|
|
423
|
+
"repeat",
|
|
424
|
+
"repeatDelay",
|
|
425
|
+
"snap",
|
|
426
|
+
"overwrite",
|
|
427
|
+
"immediateRender"
|
|
428
|
+
]);
|
|
429
|
+
function isTimelineRootedCall(callNode, timelineVar) {
|
|
430
|
+
let obj = callNode.callee?.object;
|
|
431
|
+
while (obj?.type === "CallExpression") {
|
|
432
|
+
obj = obj.callee?.object;
|
|
433
|
+
}
|
|
434
|
+
return obj?.type === "Identifier" && obj.name === timelineVar;
|
|
435
|
+
}
|
|
436
|
+
function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
|
|
437
|
+
const results = [];
|
|
438
|
+
function visit(node, ancestors) {
|
|
439
|
+
if (!node || typeof node !== "object") return;
|
|
440
|
+
const nodeAncestors = [...ancestors, node];
|
|
441
|
+
if (node.type === "CallExpression") {
|
|
442
|
+
const callee = node.callee;
|
|
443
|
+
const gsapSetArg = node.arguments?.[0];
|
|
444
|
+
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)) {
|
|
446
|
+
const method = callee.property.name;
|
|
447
|
+
const args = node.arguments;
|
|
448
|
+
const selectorValue = args.length >= 1 ? resolveTargetSelector(args[0], nodeAncestors, scope, targetBindings) ?? "__unresolved__" : "__unresolved__";
|
|
449
|
+
if (method === "fromTo" && args.length >= 3) {
|
|
450
|
+
results.push({
|
|
451
|
+
node,
|
|
452
|
+
ancestors: nodeAncestors,
|
|
453
|
+
method: "fromTo",
|
|
454
|
+
selector: selectorValue,
|
|
455
|
+
fromArg: args[1],
|
|
456
|
+
varsArg: args[2],
|
|
457
|
+
positionArg: args[3]
|
|
458
|
+
});
|
|
459
|
+
} else if (method !== "fromTo" && args.length >= 2) {
|
|
460
|
+
results.push({
|
|
461
|
+
node,
|
|
462
|
+
ancestors: nodeAncestors,
|
|
463
|
+
method,
|
|
464
|
+
selector: selectorValue,
|
|
465
|
+
varsArg: args[1],
|
|
466
|
+
positionArg: args[2],
|
|
467
|
+
...isGlobalSet ? { global: true } : {}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
for (const key of Object.keys(node)) {
|
|
473
|
+
if (key === "type" || key === "start" || key === "end" || key === "loc") continue;
|
|
474
|
+
const child = node[key];
|
|
475
|
+
if (Array.isArray(child)) {
|
|
476
|
+
for (const item of child) {
|
|
477
|
+
if (item && typeof item === "object" && item.type) visit(item, nodeAncestors);
|
|
478
|
+
}
|
|
479
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
480
|
+
visit(child, nodeAncestors);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
visit(ast, []);
|
|
485
|
+
return results;
|
|
486
|
+
}
|
|
487
|
+
var PERCENTAGE_KEY_RE = /^(\d+(?:\.\d+)?)%$/;
|
|
488
|
+
function tryResolveStringProp(propValue, scope) {
|
|
489
|
+
const val = resolveNode(propValue, scope);
|
|
490
|
+
return typeof val === "string" ? val : void 0;
|
|
491
|
+
}
|
|
492
|
+
function parsePercentageKeyframes(node, scope, source) {
|
|
493
|
+
const keyframes = [];
|
|
494
|
+
let ease;
|
|
495
|
+
let easeEach;
|
|
496
|
+
for (const prop of node.properties ?? []) {
|
|
497
|
+
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
498
|
+
const key = prop.key?.value ?? prop.key?.name;
|
|
499
|
+
if (typeof key !== "string") continue;
|
|
500
|
+
const pctMatch = PERCENTAGE_KEY_RE.exec(key);
|
|
501
|
+
if (pctMatch) {
|
|
502
|
+
const percentage = Number.parseFloat(pctMatch[1] ?? "0");
|
|
503
|
+
const record = objectExpressionToRecord(prop.value, scope, source);
|
|
504
|
+
const properties = {};
|
|
505
|
+
let kfEase;
|
|
506
|
+
for (const [k, v] of Object.entries(record)) {
|
|
507
|
+
if (k === "ease" && typeof v === "string") {
|
|
508
|
+
kfEase = v;
|
|
509
|
+
} else if (typeof v === "number" || typeof v === "string") {
|
|
510
|
+
properties[k] = v;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
keyframes.push({ percentage, properties, ...kfEase ? { ease: kfEase } : {} });
|
|
514
|
+
} else if (key === "ease") {
|
|
515
|
+
ease = tryResolveStringProp(prop.value, scope) ?? ease;
|
|
516
|
+
} else if (key === "easeEach") {
|
|
517
|
+
easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
keyframes.sort((a, b) => a.percentage - b.percentage);
|
|
521
|
+
return {
|
|
522
|
+
format: "percentage",
|
|
523
|
+
keyframes,
|
|
524
|
+
...ease ? { ease } : {},
|
|
525
|
+
...easeEach ? { easeEach } : {}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function computeKeyframesTotalDuration(varsNode, scope, source) {
|
|
529
|
+
const kfNode = (varsNode.properties ?? []).find(
|
|
530
|
+
(p) => (p.key?.name ?? p.key?.value) === "keyframes"
|
|
531
|
+
)?.value;
|
|
532
|
+
if (!kfNode || kfNode.type !== "ArrayExpression") return void 0;
|
|
533
|
+
let total = 0;
|
|
534
|
+
for (const el of kfNode.elements ?? []) {
|
|
535
|
+
if (!el || el.type !== "ObjectExpression") continue;
|
|
536
|
+
const r = objectExpressionToRecord(el, scope, source);
|
|
537
|
+
if (typeof r.duration === "number") total += r.duration;
|
|
538
|
+
}
|
|
539
|
+
return total > 0 ? total : void 0;
|
|
540
|
+
}
|
|
541
|
+
function parseObjectArrayKeyframes(node, scope, source) {
|
|
542
|
+
const elements = node.elements ?? [];
|
|
543
|
+
const raw = [];
|
|
544
|
+
for (const el of elements) {
|
|
545
|
+
if (!el || el.type !== "ObjectExpression") continue;
|
|
546
|
+
const record = objectExpressionToRecord(el, scope, source);
|
|
547
|
+
const properties = {};
|
|
548
|
+
let duration;
|
|
549
|
+
let ease;
|
|
550
|
+
for (const [k, v] of Object.entries(record)) {
|
|
551
|
+
if (k === "duration" && typeof v === "number") {
|
|
552
|
+
duration = v;
|
|
553
|
+
} else if (k === "ease" && typeof v === "string") {
|
|
554
|
+
ease = v;
|
|
555
|
+
} else if (typeof v === "number" || typeof v === "string") {
|
|
556
|
+
properties[k] = v;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
raw.push({ properties, duration, ease });
|
|
560
|
+
}
|
|
561
|
+
const totalDuration = raw.reduce((sum, r) => sum + (r.duration ?? 0), 0);
|
|
562
|
+
const keyframes = [];
|
|
563
|
+
if (totalDuration > 0) {
|
|
564
|
+
let cumulative = 0;
|
|
565
|
+
for (const entry of raw) {
|
|
566
|
+
cumulative += entry.duration ?? 0;
|
|
567
|
+
const percentage = Math.round(cumulative / totalDuration * 100);
|
|
568
|
+
keyframes.push({
|
|
569
|
+
percentage,
|
|
570
|
+
properties: entry.properties,
|
|
571
|
+
...entry.ease ? { ease: entry.ease } : {}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
} else {
|
|
575
|
+
for (let i = 0; i < raw.length; i++) {
|
|
576
|
+
const entry = raw[i];
|
|
577
|
+
if (!entry) continue;
|
|
578
|
+
const percentage = raw.length > 1 ? Math.round(i / (raw.length - 1) * 100) : 0;
|
|
579
|
+
keyframes.push({
|
|
580
|
+
percentage,
|
|
581
|
+
properties: entry.properties,
|
|
582
|
+
...entry.ease ? { ease: entry.ease } : {}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return { format: "object-array", keyframes };
|
|
587
|
+
}
|
|
588
|
+
function parseSimpleArrayKeyframes(node, scope) {
|
|
589
|
+
const arrayProps = /* @__PURE__ */ new Map();
|
|
590
|
+
let ease;
|
|
591
|
+
let easeEach;
|
|
592
|
+
for (const prop of node.properties ?? []) {
|
|
593
|
+
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
594
|
+
const key = prop.key?.name ?? prop.key?.value;
|
|
595
|
+
if (typeof key !== "string") continue;
|
|
596
|
+
if (prop.value?.type === "ArrayExpression") {
|
|
597
|
+
const values = [];
|
|
598
|
+
for (const el of prop.value.elements ?? []) {
|
|
599
|
+
const val = resolveNode(el, scope);
|
|
600
|
+
if (typeof val === "number" || typeof val === "string") {
|
|
601
|
+
values.push(val);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (values.length > 0) arrayProps.set(key, values);
|
|
605
|
+
} else if (key === "ease") {
|
|
606
|
+
ease = tryResolveStringProp(prop.value, scope) ?? ease;
|
|
607
|
+
} else if (key === "easeEach") {
|
|
608
|
+
easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const maxLen = Math.max(...[...arrayProps.values()].map((a) => a.length), 0);
|
|
612
|
+
const keyframes = [];
|
|
613
|
+
for (let i = 0; i < maxLen; i++) {
|
|
614
|
+
const percentage = maxLen > 1 ? Math.round(i / (maxLen - 1) * 100) : 0;
|
|
615
|
+
const properties = {};
|
|
616
|
+
for (const [key, values] of arrayProps) {
|
|
617
|
+
if (i < values.length) properties[key] = values[i];
|
|
618
|
+
}
|
|
619
|
+
keyframes.push({ percentage, properties });
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
format: "simple-array",
|
|
623
|
+
keyframes,
|
|
624
|
+
...ease ? { ease } : {},
|
|
625
|
+
...easeEach ? { easeEach } : {}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function parseKeyframesNode(node, scope, source) {
|
|
629
|
+
if (!node) return void 0;
|
|
630
|
+
if (node.type === "ArrayExpression") {
|
|
631
|
+
return parseObjectArrayKeyframes(node, scope, source);
|
|
632
|
+
}
|
|
633
|
+
if (node.type !== "ObjectExpression") return void 0;
|
|
634
|
+
const props = node.properties ?? [];
|
|
635
|
+
let hasPercentageKey = false;
|
|
636
|
+
let hasArrayValue = false;
|
|
637
|
+
for (const prop of props) {
|
|
638
|
+
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
639
|
+
const key = prop.key?.value ?? prop.key?.name;
|
|
640
|
+
if (typeof key === "string" && PERCENTAGE_KEY_RE.test(key)) {
|
|
641
|
+
hasPercentageKey = true;
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
if (prop.value?.type === "ArrayExpression") {
|
|
645
|
+
hasArrayValue = true;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (hasPercentageKey) return parsePercentageKeyframes(node, scope, source);
|
|
649
|
+
if (hasArrayValue) return parseSimpleArrayKeyframes(node, scope);
|
|
650
|
+
return void 0;
|
|
651
|
+
}
|
|
652
|
+
function parseMotionPathNode(node, scope, source) {
|
|
653
|
+
if (!node) return void 0;
|
|
654
|
+
let pathNode;
|
|
655
|
+
let autoRotate = false;
|
|
656
|
+
let curviness = 1;
|
|
657
|
+
let isCubic = false;
|
|
658
|
+
if (node.type === "ObjectExpression") {
|
|
659
|
+
for (const prop of node.properties ?? []) {
|
|
660
|
+
if (!isObjectProperty(prop)) continue;
|
|
661
|
+
const key = propKeyName(prop);
|
|
662
|
+
if (key === "path") pathNode = prop.value;
|
|
663
|
+
else if (key === "autoRotate") {
|
|
664
|
+
const val = resolveNode(prop.value, scope);
|
|
665
|
+
autoRotate = typeof val === "number" ? val : val === true;
|
|
666
|
+
} else if (key === "curviness") {
|
|
667
|
+
const val = resolveNode(prop.value, scope);
|
|
668
|
+
if (typeof val === "number") curviness = val;
|
|
669
|
+
} else if (key === "type") {
|
|
670
|
+
const val = resolveNode(prop.value, scope);
|
|
671
|
+
if (val === "cubic") isCubic = true;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
} else if (node.type === "ArrayExpression") {
|
|
675
|
+
pathNode = node;
|
|
676
|
+
}
|
|
677
|
+
if (!pathNode || pathNode.type !== "ArrayExpression") return void 0;
|
|
678
|
+
const elements = pathNode.elements ?? [];
|
|
679
|
+
const coords = [];
|
|
680
|
+
for (const elem of elements) {
|
|
681
|
+
if (!elem || elem.type !== "ObjectExpression") continue;
|
|
682
|
+
const rec = objectExpressionToRecord(elem, scope, source);
|
|
683
|
+
const x = typeof rec.x === "number" ? rec.x : void 0;
|
|
684
|
+
const y = typeof rec.y === "number" ? rec.y : void 0;
|
|
685
|
+
if (x !== void 0 && y !== void 0) coords.push({ x, y });
|
|
686
|
+
}
|
|
687
|
+
return buildArcPath(coords, curviness, autoRotate, isCubic);
|
|
688
|
+
}
|
|
689
|
+
function tweenCallToAnimation(call, scope, source) {
|
|
690
|
+
const vars = objectExpressionToRecord(call.varsArg, scope, source);
|
|
691
|
+
const properties = {};
|
|
692
|
+
const extras = {};
|
|
693
|
+
let keyframesData;
|
|
694
|
+
let hasUnresolvedKeyframes = false;
|
|
695
|
+
let motionPathResult;
|
|
696
|
+
for (const [key, val] of Object.entries(vars)) {
|
|
697
|
+
if (BUILTIN_VAR_KEYS.has(key)) continue;
|
|
698
|
+
if (DROPPED_VAR_KEYS.has(key)) continue;
|
|
699
|
+
if (key === "keyframes") {
|
|
700
|
+
const kfNode = findPropertyNode(call.varsArg, "keyframes");
|
|
701
|
+
keyframesData = parseKeyframesNode(kfNode, scope, source);
|
|
702
|
+
if (!keyframesData && kfNode) hasUnresolvedKeyframes = true;
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
if (key === "motionPath") {
|
|
706
|
+
const mpNode = findPropertyNode(call.varsArg, "motionPath");
|
|
707
|
+
motionPathResult = parseMotionPathNode(mpNode, scope, source);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (key === "easeEach") continue;
|
|
711
|
+
if (EXTRAS_KEYS.has(key)) {
|
|
712
|
+
const rawSource = extractRawPropertySource(call.varsArg, key, source);
|
|
713
|
+
if (rawSource !== void 0) {
|
|
714
|
+
extras[key] = `__raw:${rawSource}`;
|
|
715
|
+
} else if (val !== void 0) {
|
|
716
|
+
extras[key] = val;
|
|
717
|
+
}
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
if (typeof val === "number" || typeof val === "string") {
|
|
721
|
+
properties[key] = val;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (keyframesData && typeof vars.easeEach === "string") {
|
|
725
|
+
keyframesData.easeEach = vars.easeEach;
|
|
726
|
+
}
|
|
727
|
+
if (motionPathResult) {
|
|
728
|
+
const { waypoints } = motionPathResult;
|
|
729
|
+
if (!keyframesData) {
|
|
730
|
+
const kf = waypoints.map((wp, i) => ({
|
|
731
|
+
percentage: waypoints.length > 1 ? Math.round(i / (waypoints.length - 1) * 100) : 0,
|
|
732
|
+
properties: { x: wp.x, y: wp.y }
|
|
733
|
+
}));
|
|
734
|
+
keyframesData = { format: "percentage", keyframes: kf };
|
|
735
|
+
} else {
|
|
736
|
+
const kfs = keyframesData.keyframes;
|
|
737
|
+
if (kfs.length === waypoints.length) {
|
|
738
|
+
for (let i = 0; i < kfs.length; i++) {
|
|
739
|
+
const kf = kfs[i];
|
|
740
|
+
const wp = waypoints[i];
|
|
741
|
+
if (kf && wp) {
|
|
742
|
+
kf.properties.x = wp.x;
|
|
743
|
+
kf.properties.y = wp.y;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
let fromProperties;
|
|
750
|
+
if (call.method === "fromTo" && call.fromArg) {
|
|
751
|
+
fromProperties = {};
|
|
752
|
+
const fromVars = objectExpressionToRecord(call.fromArg, scope, source);
|
|
753
|
+
for (const [key, val] of Object.entries(fromVars)) {
|
|
754
|
+
if (typeof val === "number" || typeof val === "string") {
|
|
755
|
+
fromProperties[key] = val;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
const hasPositionArg = !!call.positionArg;
|
|
760
|
+
const posVal = hasPositionArg ? extractLiteralValue(call.positionArg, scope) : 0;
|
|
761
|
+
const position = typeof posVal === "number" ? posVal : typeof posVal === "string" ? posVal : 0;
|
|
762
|
+
let duration = typeof vars.duration === "number" ? vars.duration : void 0;
|
|
763
|
+
const ease = typeof vars.ease === "string" ? vars.ease : void 0;
|
|
764
|
+
if (duration === void 0 && keyframesData) {
|
|
765
|
+
duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
|
|
766
|
+
}
|
|
767
|
+
const anim = {
|
|
768
|
+
targetSelector: call.selector,
|
|
769
|
+
method: call.method,
|
|
770
|
+
position,
|
|
771
|
+
properties,
|
|
772
|
+
fromProperties,
|
|
773
|
+
duration,
|
|
774
|
+
ease
|
|
775
|
+
};
|
|
776
|
+
if (!hasPositionArg) 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)) kfProps[k] = true;
|
|
782
|
+
}
|
|
783
|
+
group = classifyTweenPropertyGroup(kfProps);
|
|
784
|
+
}
|
|
785
|
+
if (group) anim.propertyGroup = group;
|
|
786
|
+
if (call.global) anim.global = true;
|
|
787
|
+
if (Object.keys(extras).length > 0) anim.extras = extras;
|
|
788
|
+
if (keyframesData) anim.keyframes = keyframesData;
|
|
789
|
+
if (motionPathResult) anim.arcPath = motionPathResult.arcPath;
|
|
790
|
+
if (hasUnresolvedKeyframes) anim.hasUnresolvedKeyframes = true;
|
|
791
|
+
if (call.selector === "__unresolved__") anim.hasUnresolvedSelector = true;
|
|
792
|
+
const provenance = readProvenance(call.node);
|
|
793
|
+
if (provenance) anim.provenance = provenance;
|
|
794
|
+
return anim;
|
|
795
|
+
}
|
|
796
|
+
var GSAP_DEFAULT_DURATION = 0.5;
|
|
797
|
+
function resolvePositionString(pos, cursor, prevStart) {
|
|
798
|
+
const trimmed = pos.trim();
|
|
799
|
+
if (trimmed === "") return cursor;
|
|
800
|
+
if (trimmed.startsWith("+=")) {
|
|
801
|
+
const n2 = Number.parseFloat(trimmed.slice(2));
|
|
802
|
+
return Number.isFinite(n2) ? cursor + n2 : null;
|
|
803
|
+
}
|
|
804
|
+
if (trimmed.startsWith("-=")) {
|
|
805
|
+
const n2 = Number.parseFloat(trimmed.slice(2));
|
|
806
|
+
return Number.isFinite(n2) ? cursor - n2 : null;
|
|
807
|
+
}
|
|
808
|
+
if (trimmed === "<") return prevStart;
|
|
809
|
+
if (trimmed === ">") return cursor;
|
|
810
|
+
if (trimmed.startsWith("<")) {
|
|
811
|
+
const n2 = Number.parseFloat(trimmed.slice(1));
|
|
812
|
+
return Number.isFinite(n2) ? prevStart + n2 : null;
|
|
813
|
+
}
|
|
814
|
+
if (trimmed.startsWith(">")) {
|
|
815
|
+
const n2 = Number.parseFloat(trimmed.slice(1));
|
|
816
|
+
return Number.isFinite(n2) ? cursor + n2 : null;
|
|
817
|
+
}
|
|
818
|
+
const n = Number.parseFloat(trimmed);
|
|
819
|
+
return Number.isFinite(n) ? n : null;
|
|
820
|
+
}
|
|
821
|
+
function applyTimelineDefaults(anims, defaults) {
|
|
822
|
+
if (!defaults) return;
|
|
823
|
+
for (const anim of anims) {
|
|
824
|
+
if (anim.method === "set") continue;
|
|
825
|
+
if (anim.duration === void 0 && defaults.duration !== void 0) {
|
|
826
|
+
anim.duration = defaults.duration;
|
|
827
|
+
}
|
|
828
|
+
if (anim.ease === void 0 && defaults.ease !== void 0) {
|
|
829
|
+
anim.ease = defaults.ease;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
function resolveTimelinePositions(anims) {
|
|
834
|
+
let cursor = 0;
|
|
835
|
+
let prevStart = 0;
|
|
836
|
+
for (const anim of anims) {
|
|
837
|
+
if (anim.method === "set" && anim.global) {
|
|
838
|
+
anim.resolvedStart = 0;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION;
|
|
842
|
+
let start;
|
|
843
|
+
if (anim.implicitPosition) {
|
|
844
|
+
start = cursor;
|
|
845
|
+
} else if (typeof anim.position === "number") {
|
|
846
|
+
start = anim.position;
|
|
847
|
+
} else if (typeof anim.position === "string") {
|
|
848
|
+
start = resolvePositionString(anim.position, cursor, prevStart);
|
|
849
|
+
} else {
|
|
850
|
+
start = cursor;
|
|
851
|
+
}
|
|
852
|
+
if (start != null) {
|
|
853
|
+
anim.resolvedStart = Math.max(0, start);
|
|
854
|
+
prevStart = anim.resolvedStart;
|
|
855
|
+
cursor = Math.max(cursor, anim.resolvedStart + duration);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
function compareByLoc(a, b) {
|
|
860
|
+
const aLoc = a.node.callee?.property?.loc?.start;
|
|
861
|
+
const bLoc = b.node.callee?.property?.loc?.start;
|
|
862
|
+
if (!aLoc || !bLoc) return 0;
|
|
863
|
+
return aLoc.line - bLoc.line || aLoc.column - bLoc.column;
|
|
864
|
+
}
|
|
865
|
+
function compareCallOrder(a, b) {
|
|
866
|
+
const ao = a.node.__hfOrder;
|
|
867
|
+
const bo = b.node.__hfOrder;
|
|
868
|
+
if (ao === void 0 && bo === void 0) return compareByLoc(a, b);
|
|
869
|
+
if (ao === void 0) return -1;
|
|
870
|
+
if (bo === void 0) return 1;
|
|
871
|
+
return ao - bo;
|
|
872
|
+
}
|
|
873
|
+
function sortBySourcePosition(calls) {
|
|
874
|
+
calls.sort(compareCallOrder);
|
|
875
|
+
}
|
|
876
|
+
function assignStableIds(anims) {
|
|
877
|
+
const counts = /* @__PURE__ */ new Map();
|
|
878
|
+
return anims.map((anim) => {
|
|
879
|
+
const posKey = typeof anim.position === "number" ? String(Math.round(anim.position * 1e3)) : String(anim.position);
|
|
880
|
+
const groupSuffix = anim.propertyGroup ? `-${anim.propertyGroup}` : "";
|
|
881
|
+
const base = `${anim.targetSelector}-${anim.method}-${posKey}${groupSuffix}`;
|
|
882
|
+
const count = (counts.get(base) ?? 0) + 1;
|
|
883
|
+
counts.set(base, count);
|
|
884
|
+
const id = count === 1 ? base : `${base}-${count}`;
|
|
885
|
+
return { ...anim, id };
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
function parseGsapScriptAcornForWrite(script) {
|
|
889
|
+
try {
|
|
890
|
+
const ast = acorn.parse(script, {
|
|
891
|
+
ecmaVersion: "latest",
|
|
892
|
+
sourceType: "script",
|
|
893
|
+
locations: true
|
|
894
|
+
});
|
|
895
|
+
const scope = collectScopeBindings(ast);
|
|
896
|
+
const targetBindings = collectTargetBindings(ast, scope);
|
|
897
|
+
const detection = findTimelineVar(ast, scope);
|
|
898
|
+
const timelineVar = detection.timelineVar ?? "tl";
|
|
899
|
+
const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
|
|
900
|
+
sortBySourcePosition(calls);
|
|
901
|
+
const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
|
|
902
|
+
applyTimelineDefaults(rawAnims, detection.defaults);
|
|
903
|
+
resolveTimelinePositions(rawAnims);
|
|
904
|
+
const animations = assignStableIds(rawAnims);
|
|
905
|
+
const located = calls.map((call, i) => ({
|
|
906
|
+
id: animations[i].id,
|
|
907
|
+
call,
|
|
908
|
+
animation: animations[i]
|
|
909
|
+
}));
|
|
910
|
+
return { ast, timelineVar, hasTimeline: detection.timelineVar !== null, located };
|
|
911
|
+
} catch {
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// src/gsapWriterAcorn.ts
|
|
917
|
+
import * as acornWalk2 from "acorn-walk";
|
|
918
|
+
function valueToCode(value) {
|
|
919
|
+
if (typeof value === "string" && value.startsWith("__raw:")) return value.slice(6);
|
|
920
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
921
|
+
if (typeof value === "number") return Number.isNaN(value) ? "0" : String(value);
|
|
922
|
+
if (typeof value === "boolean") return String(value);
|
|
923
|
+
return JSON.stringify(value);
|
|
924
|
+
}
|
|
925
|
+
function safeKey(key) {
|
|
926
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
927
|
+
}
|
|
928
|
+
function buildTweenStatementCode(timelineVar, anim) {
|
|
929
|
+
const selector = JSON.stringify(anim.targetSelector);
|
|
930
|
+
const props = { ...anim.properties };
|
|
931
|
+
if (anim.method !== "set" && anim.duration !== void 0) props.duration = anim.duration;
|
|
932
|
+
if (anim.ease) props.ease = anim.ease;
|
|
933
|
+
const entries = Object.entries(props).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`);
|
|
934
|
+
if (anim.extras) {
|
|
935
|
+
for (const [k, v] of Object.entries(anim.extras)) {
|
|
936
|
+
entries.push(`${safeKey(k)}: ${valueToCode(v)}`);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
const objCode = `{ ${entries.join(", ")} }`;
|
|
940
|
+
const posCode = valueToCode(
|
|
941
|
+
typeof anim.position === "number" ? anim.position : anim.position ?? 0
|
|
942
|
+
);
|
|
943
|
+
if (anim.method === "fromTo") {
|
|
944
|
+
const fromEntries = Object.entries(anim.fromProperties ?? {}).map(
|
|
945
|
+
([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`
|
|
946
|
+
);
|
|
947
|
+
return `${timelineVar}.fromTo(${selector}, { ${fromEntries.join(", ")} }, ${objCode}, ${posCode});`;
|
|
948
|
+
}
|
|
949
|
+
if (anim.method === "set" && anim.global) {
|
|
950
|
+
return `gsap.set(${selector}, ${objCode});`;
|
|
951
|
+
}
|
|
952
|
+
return `${timelineVar}.${anim.method}(${selector}, ${objCode}, ${posCode});`;
|
|
953
|
+
}
|
|
954
|
+
function isObjectProperty2(prop) {
|
|
955
|
+
return prop?.type === "ObjectProperty" || prop?.type === "Property";
|
|
956
|
+
}
|
|
957
|
+
function propKeyName2(prop) {
|
|
958
|
+
return prop?.key?.name ?? prop?.key?.value;
|
|
959
|
+
}
|
|
960
|
+
function findPropertyNode2(varsArgNode, key) {
|
|
961
|
+
if (varsArgNode?.type !== "ObjectExpression") return void 0;
|
|
962
|
+
for (const prop of varsArgNode.properties ?? []) {
|
|
963
|
+
if (!isObjectProperty2(prop)) continue;
|
|
964
|
+
if (propKeyName2(prop) === key) return prop;
|
|
965
|
+
}
|
|
966
|
+
return void 0;
|
|
967
|
+
}
|
|
968
|
+
function keyframesObjectNode(varsNode) {
|
|
969
|
+
const kfProp = findPropertyNode2(varsNode, "keyframes");
|
|
970
|
+
return kfProp?.value?.type === "ObjectExpression" ? kfProp.value : null;
|
|
971
|
+
}
|
|
972
|
+
function findEnclosingExpressionStatement(ancestors) {
|
|
973
|
+
for (let i = ancestors.length - 2; i >= 0; i--) {
|
|
974
|
+
if (ancestors[i]?.type === "ExpressionStatement") return ancestors[i];
|
|
975
|
+
}
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
function findTimelineDeclarationStatement(ast, timelineVar) {
|
|
979
|
+
let found = null;
|
|
980
|
+
acornWalk2.simple(ast, {
|
|
981
|
+
// fallow-ignore-next-line complexity
|
|
982
|
+
VariableDeclaration(node) {
|
|
983
|
+
if (found) return;
|
|
984
|
+
for (const decl of node.declarations ?? []) {
|
|
985
|
+
if (decl.id?.name === timelineVar && decl.init?.type === "CallExpression" && decl.init.callee?.type === "MemberExpression" && decl.init.callee.object?.name === "gsap" && decl.init.callee.property?.name === "timeline") {
|
|
986
|
+
found = node;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
return found;
|
|
992
|
+
}
|
|
993
|
+
function removeProp(ms, propNode, editableProps) {
|
|
994
|
+
const idx = editableProps.indexOf(propNode);
|
|
995
|
+
if (idx === -1) return;
|
|
996
|
+
if (editableProps.length === 1) {
|
|
997
|
+
ms.remove(propNode.start, propNode.end);
|
|
998
|
+
} else if (idx === 0) {
|
|
999
|
+
ms.remove(editableProps[0].start, editableProps[1].start);
|
|
1000
|
+
} else {
|
|
1001
|
+
ms.remove(editableProps[idx - 1].end, propNode.end);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function buildVarsObjectCode(record) {
|
|
1005
|
+
const entries = Object.entries(record).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`);
|
|
1006
|
+
return entries.length > 0 ? `{ ${entries.join(", ")} }` : "{}";
|
|
1007
|
+
}
|
|
1008
|
+
function overwriteVarsArg(ms, call, objCode) {
|
|
1009
|
+
if (!call.varsArg) return;
|
|
1010
|
+
ms.overwrite(call.varsArg.start, call.varsArg.end, objCode);
|
|
1011
|
+
}
|
|
1012
|
+
function upsertProp(ms, objNode, key, value) {
|
|
1013
|
+
if (objNode?.type !== "ObjectExpression") return;
|
|
1014
|
+
const existing = findPropertyNode2(objNode, key);
|
|
1015
|
+
if (existing) {
|
|
1016
|
+
ms.overwrite(existing.value.start, existing.value.end, valueToCode(value));
|
|
1017
|
+
} else {
|
|
1018
|
+
const sep = objNode.properties.length > 0 ? ", " : "";
|
|
1019
|
+
ms.appendLeft(objNode.end - 1, `${sep}${safeKey(key)}: ${valueToCode(value)}`);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
var NON_EDITABLE_PROP_KEYS = /* @__PURE__ */ new Set([
|
|
1023
|
+
"duration",
|
|
1024
|
+
"ease",
|
|
1025
|
+
"delay",
|
|
1026
|
+
"onComplete",
|
|
1027
|
+
"onStart",
|
|
1028
|
+
"onUpdate",
|
|
1029
|
+
"onRepeat",
|
|
1030
|
+
"stagger",
|
|
1031
|
+
"yoyo",
|
|
1032
|
+
"repeat",
|
|
1033
|
+
"repeatDelay",
|
|
1034
|
+
"snap",
|
|
1035
|
+
"overwrite",
|
|
1036
|
+
"immediateRender"
|
|
1037
|
+
]);
|
|
1038
|
+
function isEditableVarKey(key) {
|
|
1039
|
+
return !NON_EDITABLE_PROP_KEYS.has(key);
|
|
1040
|
+
}
|
|
1041
|
+
function preservedEntries(objNode, source, drop, overrides) {
|
|
1042
|
+
const entries = [];
|
|
1043
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1044
|
+
for (const prop of objNode.properties ?? []) {
|
|
1045
|
+
if (!isObjectProperty2(prop)) continue;
|
|
1046
|
+
const key = propKeyName2(prop);
|
|
1047
|
+
if (typeof key !== "string" || drop(key)) continue;
|
|
1048
|
+
keys.add(key);
|
|
1049
|
+
const code = key in overrides ? valueToCode(overrides[key]) : source.slice(prop.value.start, prop.value.end);
|
|
1050
|
+
entries.push(`${safeKey(key)}: ${code}`);
|
|
1051
|
+
}
|
|
1052
|
+
return { entries, keys };
|
|
1053
|
+
}
|
|
1054
|
+
function reconcileEditableProps(ms, objNode, source, newProps, nonEditableOverrides) {
|
|
1055
|
+
if (objNode?.type !== "ObjectExpression") return;
|
|
1056
|
+
const overrides = nonEditableOverrides ?? {};
|
|
1057
|
+
const { entries, keys } = preservedEntries(objNode, source, isEditableVarKey, overrides);
|
|
1058
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
1059
|
+
if (!keys.has(key)) entries.push(`${safeKey(key)}: ${valueToCode(value)}`);
|
|
1060
|
+
}
|
|
1061
|
+
for (const [key, value] of Object.entries(newProps)) {
|
|
1062
|
+
entries.push(`${safeKey(key)}: ${valueToCode(value)}`);
|
|
1063
|
+
}
|
|
1064
|
+
ms.overwrite(objNode.start, objNode.end, `{ ${entries.join(", ")} }`);
|
|
1065
|
+
}
|
|
1066
|
+
function isTimelineRooted(node, timelineVar) {
|
|
1067
|
+
if (node?.type === "Identifier") return node.name === timelineVar;
|
|
1068
|
+
if (node?.type === "CallExpression") return isTimelineRooted(node.callee?.object, timelineVar);
|
|
1069
|
+
return false;
|
|
1070
|
+
}
|
|
1071
|
+
function findInsertionPoint(parsed) {
|
|
1072
|
+
const lastLocated = parsed.located[parsed.located.length - 1];
|
|
1073
|
+
if (lastLocated) {
|
|
1074
|
+
const lastCall = lastLocated.call;
|
|
1075
|
+
const exprStmt = findEnclosingExpressionStatement(lastCall.ancestors);
|
|
1076
|
+
return exprStmt?.end ?? lastCall.node.end;
|
|
1077
|
+
}
|
|
1078
|
+
if (!parsed.hasTimeline) return null;
|
|
1079
|
+
const tlDecl = findTimelineDeclarationStatement(parsed.ast, parsed.timelineVar);
|
|
1080
|
+
return tlDecl?.end ?? parsed.ast.end;
|
|
1081
|
+
}
|
|
1082
|
+
function updateAnimationInScript(script, animationId, updates) {
|
|
1083
|
+
if (!Object.keys(updates).length) return script;
|
|
1084
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1085
|
+
if (!parsed) return script;
|
|
1086
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1087
|
+
if (!target) return script;
|
|
1088
|
+
const ms = new MagicString(script);
|
|
1089
|
+
const { call } = target;
|
|
1090
|
+
if (updates.properties) {
|
|
1091
|
+
const overrides = {};
|
|
1092
|
+
if (updates.duration !== void 0) overrides.duration = updates.duration;
|
|
1093
|
+
if (updates.ease !== void 0) overrides.ease = updates.ease;
|
|
1094
|
+
if (updates.extras) Object.assign(overrides, updates.extras);
|
|
1095
|
+
reconcileEditableProps(ms, call.varsArg, script, updates.properties, overrides);
|
|
1096
|
+
} else {
|
|
1097
|
+
if (updates.duration !== void 0) {
|
|
1098
|
+
upsertProp(ms, call.varsArg, "duration", updates.duration);
|
|
1099
|
+
}
|
|
1100
|
+
const easeValue = updates.easeEach ?? updates.ease;
|
|
1101
|
+
if (easeValue !== void 0) {
|
|
1102
|
+
const kfNode = keyframesObjectNode(call.varsArg);
|
|
1103
|
+
if (kfNode) {
|
|
1104
|
+
upsertProp(ms, kfNode, "easeEach", easeValue);
|
|
1105
|
+
if (updates.resetKeyframeEases) {
|
|
1106
|
+
for (const kfEntry of kfNode.properties ?? []) {
|
|
1107
|
+
if (!isObjectProperty2(kfEntry)) continue;
|
|
1108
|
+
const val = kfEntry.value;
|
|
1109
|
+
if (val?.type !== "ObjectExpression") continue;
|
|
1110
|
+
const easeNode = findPropertyNode2(val, "ease");
|
|
1111
|
+
if (easeNode) removeProp(ms, easeNode, val.properties);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
} else {
|
|
1115
|
+
upsertProp(ms, call.varsArg, "ease", easeValue);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (updates.extras) {
|
|
1119
|
+
for (const [key, value] of Object.entries(updates.extras)) {
|
|
1120
|
+
upsertProp(ms, call.varsArg, key, value);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (updates.fromProperties && call.method === "fromTo" && call.fromArg) {
|
|
1125
|
+
reconcileEditableProps(ms, call.fromArg, script, updates.fromProperties);
|
|
1126
|
+
}
|
|
1127
|
+
if (updates.position !== void 0) {
|
|
1128
|
+
overwritePosition(ms, call, updates.position);
|
|
1129
|
+
}
|
|
1130
|
+
return ms.toString();
|
|
1131
|
+
}
|
|
1132
|
+
function overwritePosition(ms, call, position) {
|
|
1133
|
+
if (call.positionArg) {
|
|
1134
|
+
ms.overwrite(call.positionArg.start, call.positionArg.end, valueToCode(position));
|
|
1135
|
+
} else {
|
|
1136
|
+
ms.appendLeft(call.node.end - 1, `, ${valueToCode(position)}`);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function shiftPositionsInScript(script, targetSelector, delta) {
|
|
1140
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1141
|
+
if (!parsed) return script;
|
|
1142
|
+
const ms = new MagicString(script);
|
|
1143
|
+
let changed = false;
|
|
1144
|
+
for (const entry of parsed.located) {
|
|
1145
|
+
if (entry.animation.targetSelector !== targetSelector) continue;
|
|
1146
|
+
if (typeof entry.animation.position !== "number") continue;
|
|
1147
|
+
const newPos = Math.max(0, Math.round((entry.animation.position + delta) * 1e3) / 1e3);
|
|
1148
|
+
overwritePosition(ms, entry.call, newPos);
|
|
1149
|
+
changed = true;
|
|
1150
|
+
}
|
|
1151
|
+
return changed ? ms.toString() : script;
|
|
1152
|
+
}
|
|
1153
|
+
function scalePositionsInScript(script, targetSelector, oldStart, oldDuration, newStart, newDuration) {
|
|
1154
|
+
if (oldDuration <= 0 || newDuration <= 0) return script;
|
|
1155
|
+
const ratio = newDuration / oldDuration;
|
|
1156
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1157
|
+
if (!parsed) return script;
|
|
1158
|
+
const ms = new MagicString(script);
|
|
1159
|
+
let changed = false;
|
|
1160
|
+
for (const entry of parsed.located) {
|
|
1161
|
+
if (entry.animation.targetSelector !== targetSelector) continue;
|
|
1162
|
+
if (typeof entry.animation.position !== "number") continue;
|
|
1163
|
+
const newPos = Math.max(
|
|
1164
|
+
0,
|
|
1165
|
+
Math.round((newStart + (entry.animation.position - oldStart) * ratio) * 1e3) / 1e3
|
|
1166
|
+
);
|
|
1167
|
+
overwritePosition(ms, entry.call, newPos);
|
|
1168
|
+
if (typeof entry.animation.duration === "number" && entry.animation.duration > 0) {
|
|
1169
|
+
const newDur = Math.max(1e-3, Math.round(entry.animation.duration * ratio * 1e3) / 1e3);
|
|
1170
|
+
upsertProp(ms, entry.call.varsArg, "duration", newDur);
|
|
1171
|
+
}
|
|
1172
|
+
changed = true;
|
|
1173
|
+
}
|
|
1174
|
+
return changed ? ms.toString() : script;
|
|
1175
|
+
}
|
|
1176
|
+
function addAnimationToScript(script, animation) {
|
|
1177
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1178
|
+
if (!parsed) return { script, id: "" };
|
|
1179
|
+
const insertionPoint = findInsertionPoint(parsed);
|
|
1180
|
+
if (insertionPoint === null) return { script, id: "" };
|
|
1181
|
+
const ms = new MagicString(script);
|
|
1182
|
+
const statementCode = buildTweenStatementCode(parsed.timelineVar, animation);
|
|
1183
|
+
ms.appendLeft(insertionPoint, "\n" + statementCode);
|
|
1184
|
+
const result = ms.toString();
|
|
1185
|
+
const reParsed = parseGsapScriptAcornForWrite(result);
|
|
1186
|
+
const newId = reParsed?.located[reParsed.located.length - 1]?.id ?? "";
|
|
1187
|
+
return { script: result, id: newId };
|
|
1188
|
+
}
|
|
1189
|
+
function removeAnimationFromScript(script, animationId) {
|
|
1190
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1191
|
+
if (!parsed) return script;
|
|
1192
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1193
|
+
if (!target) return script;
|
|
1194
|
+
const ms = new MagicString(script);
|
|
1195
|
+
const N = target.call.node;
|
|
1196
|
+
const exprStmt = findEnclosingExpressionStatement(target.call.ancestors);
|
|
1197
|
+
if (N.callee?.object?.type !== "CallExpression" && exprStmt?.expression === N) {
|
|
1198
|
+
const end = exprStmt.end < script.length && script[exprStmt.end] === "\n" ? exprStmt.end + 1 : exprStmt.end;
|
|
1199
|
+
ms.remove(exprStmt.start, end);
|
|
1200
|
+
} else {
|
|
1201
|
+
ms.remove(N.callee.object.end, N.end);
|
|
1202
|
+
}
|
|
1203
|
+
return ms.toString();
|
|
1204
|
+
}
|
|
1205
|
+
var CSS_IDENTITY2 = {
|
|
1206
|
+
opacity: 1,
|
|
1207
|
+
autoAlpha: 1,
|
|
1208
|
+
scale: 1,
|
|
1209
|
+
scaleX: 1,
|
|
1210
|
+
scaleY: 1
|
|
1211
|
+
};
|
|
1212
|
+
function cssIdentityValue2(prop) {
|
|
1213
|
+
return CSS_IDENTITY2[prop] ?? 0;
|
|
1214
|
+
}
|
|
1215
|
+
var NON_EDITABLE_VAR_KEYS = /* @__PURE__ */ new Set([
|
|
1216
|
+
"duration",
|
|
1217
|
+
"delay",
|
|
1218
|
+
"onComplete",
|
|
1219
|
+
"onStart",
|
|
1220
|
+
"onUpdate",
|
|
1221
|
+
"onRepeat",
|
|
1222
|
+
"stagger",
|
|
1223
|
+
"yoyo",
|
|
1224
|
+
"repeat",
|
|
1225
|
+
"repeatDelay",
|
|
1226
|
+
"snap",
|
|
1227
|
+
"overwrite",
|
|
1228
|
+
"immediateRender"
|
|
1229
|
+
]);
|
|
1230
|
+
function identityProps(properties) {
|
|
1231
|
+
const identity = {};
|
|
1232
|
+
for (const [k, v] of Object.entries(properties)) {
|
|
1233
|
+
if (v != null) identity[k] = typeof v === "number" ? cssIdentityValue2(k) : v;
|
|
1234
|
+
}
|
|
1235
|
+
return identity;
|
|
1236
|
+
}
|
|
1237
|
+
function conversionEndpoints(animation) {
|
|
1238
|
+
if (animation.method === "from") {
|
|
1239
|
+
return { fromProps: { ...animation.properties }, toProps: identityProps(animation.properties) };
|
|
1240
|
+
}
|
|
1241
|
+
if (animation.method === "fromTo") {
|
|
1242
|
+
return {
|
|
1243
|
+
fromProps: { ...animation.fromProperties ?? {} },
|
|
1244
|
+
toProps: { ...animation.properties }
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
return { fromProps: identityProps(animation.properties), toProps: { ...animation.properties } };
|
|
1248
|
+
}
|
|
1249
|
+
function preservedVarsEntries(varsNode, source) {
|
|
1250
|
+
const entries = [];
|
|
1251
|
+
if (varsNode?.type !== "ObjectExpression") return entries;
|
|
1252
|
+
for (const prop of varsNode.properties ?? []) {
|
|
1253
|
+
if (!isObjectProperty2(prop)) continue;
|
|
1254
|
+
const key = propKeyName2(prop);
|
|
1255
|
+
if (typeof key !== "string" || !NON_EDITABLE_VAR_KEYS.has(key)) continue;
|
|
1256
|
+
entries.push(`${safeKey(key)}: ${source.slice(prop.value.start, prop.value.end)}`);
|
|
1257
|
+
}
|
|
1258
|
+
return entries;
|
|
1259
|
+
}
|
|
1260
|
+
function buildConvertedVarsCode(animation, varsNode, source) {
|
|
1261
|
+
const { fromProps, toProps } = conversionEndpoints(animation);
|
|
1262
|
+
const easeEach = animation.ease;
|
|
1263
|
+
const easeEachEntry = easeEach ? `, easeEach: ${JSON.stringify(easeEach)}` : "";
|
|
1264
|
+
const kfCode = `{ "0%": ${recordToCode(fromProps)}, "100%": ${recordToCode(toProps)}${easeEachEntry} }`;
|
|
1265
|
+
const entries = [`keyframes: ${kfCode}`, ...preservedVarsEntries(varsNode, source)];
|
|
1266
|
+
if (easeEach) entries.push(`ease: "none"`);
|
|
1267
|
+
return `{ ${entries.join(", ")} }`;
|
|
1268
|
+
}
|
|
1269
|
+
function convertMethodToTo(ms, animation, call, varsNode) {
|
|
1270
|
+
if (animation.method !== "from" && animation.method !== "fromTo") return;
|
|
1271
|
+
const calleeProp = call.node.callee?.property;
|
|
1272
|
+
if (calleeProp) ms.overwrite(calleeProp.start, calleeProp.end, "to");
|
|
1273
|
+
if (animation.method === "fromTo" && call.fromArg) ms.remove(call.fromArg.start, varsNode.start);
|
|
1274
|
+
}
|
|
1275
|
+
function convertFlatTweenToKeyframes(script, target) {
|
|
1276
|
+
const animation = target.animation;
|
|
1277
|
+
if (animation.keyframes || animation.method === "set") return script;
|
|
1278
|
+
const call = target.call;
|
|
1279
|
+
const varsNode = call.varsArg;
|
|
1280
|
+
if (varsNode?.type !== "ObjectExpression") return script;
|
|
1281
|
+
const ms = new MagicString(script);
|
|
1282
|
+
ms.overwrite(varsNode.start, varsNode.end, buildConvertedVarsCode(animation, varsNode, script));
|
|
1283
|
+
convertMethodToTo(ms, animation, call, varsNode);
|
|
1284
|
+
return ms.toString();
|
|
1285
|
+
}
|
|
1286
|
+
var PERCENTAGE_KEY_RE2 = /^(\d+(?:\.\d+)?)%$/;
|
|
1287
|
+
var PCT_TOLERANCE = 2;
|
|
1288
|
+
function percentageFromKey(key) {
|
|
1289
|
+
const m = PERCENTAGE_KEY_RE2.exec(key);
|
|
1290
|
+
return m ? Number.parseFloat(m[1] ?? "0") : Number.NaN;
|
|
1291
|
+
}
|
|
1292
|
+
function recordToCode(record) {
|
|
1293
|
+
const entries = Object.entries(record).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`);
|
|
1294
|
+
return `{ ${entries.join(", ")} }`;
|
|
1295
|
+
}
|
|
1296
|
+
function percentagePropsOf(kfNode) {
|
|
1297
|
+
return (kfNode.properties ?? []).filter((p) => {
|
|
1298
|
+
if (!isObjectProperty2(p)) return false;
|
|
1299
|
+
const key = propKeyName2(p);
|
|
1300
|
+
return typeof key === "string" && PERCENTAGE_KEY_RE2.test(key);
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
var LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["Literal", "NumericLiteral", "StringLiteral"]);
|
|
1304
|
+
function readValueNode(v, source) {
|
|
1305
|
+
if (LITERAL_NODE_TYPES.has(v?.type) && (typeof v.value === "number" || typeof v.value === "string")) {
|
|
1306
|
+
return v.value;
|
|
1307
|
+
}
|
|
1308
|
+
if (v?.type === "UnaryExpression" && v.operator === "-" && typeof v.argument?.value === "number") {
|
|
1309
|
+
return -v.argument.value;
|
|
1310
|
+
}
|
|
1311
|
+
return `__raw:${source.slice(v.start, v.end)}`;
|
|
1312
|
+
}
|
|
1313
|
+
function valueNodeToRecord(valueNode, source) {
|
|
1314
|
+
const record = {};
|
|
1315
|
+
if (valueNode?.type !== "ObjectExpression") return record;
|
|
1316
|
+
for (const prop of valueNode.properties ?? []) {
|
|
1317
|
+
if (!isObjectProperty2(prop)) continue;
|
|
1318
|
+
const key = propKeyName2(prop);
|
|
1319
|
+
if (typeof key !== "string") continue;
|
|
1320
|
+
record[key] = readValueNode(prop.value, source);
|
|
1321
|
+
}
|
|
1322
|
+
return record;
|
|
1323
|
+
}
|
|
1324
|
+
function recordHasAuto(record) {
|
|
1325
|
+
return "_auto" in record;
|
|
1326
|
+
}
|
|
1327
|
+
function autoEndpointOverwrites(kfNode, source, percentage, properties) {
|
|
1328
|
+
const result = /* @__PURE__ */ new Map();
|
|
1329
|
+
if (percentage <= 0 || percentage >= 100) return result;
|
|
1330
|
+
const pctProps = percentagePropsOf(kfNode);
|
|
1331
|
+
const allPcts = pctProps.map((p) => percentageFromKey(propKeyName2(p) ?? "")).filter((n) => !Number.isNaN(n) && n !== percentage).sort((a, b) => a - b);
|
|
1332
|
+
const leftNeighbor = allPcts.filter((p) => p < percentage).pop();
|
|
1333
|
+
const rightNeighbor = allPcts.find((p) => p > percentage);
|
|
1334
|
+
for (const endPct of [0, 100]) {
|
|
1335
|
+
const isNeighbor = endPct === 0 ? leftNeighbor === 0 : rightNeighbor === 100;
|
|
1336
|
+
if (!isNeighbor) continue;
|
|
1337
|
+
const endProp = pctProps.find((p) => percentageFromKey(propKeyName2(p) ?? "") === endPct);
|
|
1338
|
+
if (!endProp) continue;
|
|
1339
|
+
const rec = valueNodeToRecord(endProp.value, source);
|
|
1340
|
+
if (!recordHasAuto(rec)) continue;
|
|
1341
|
+
result.set(endProp, { ...properties, _auto: 1 });
|
|
1342
|
+
}
|
|
1343
|
+
return result;
|
|
1344
|
+
}
|
|
1345
|
+
function findKfPropByPct(kfNode, percentage) {
|
|
1346
|
+
const props = kfNode.properties ?? [];
|
|
1347
|
+
let best = null;
|
|
1348
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
1349
|
+
for (let i = 0; i < props.length; i++) {
|
|
1350
|
+
const prop = props[i];
|
|
1351
|
+
if (!isObjectProperty2(prop)) continue;
|
|
1352
|
+
const key = propKeyName2(prop);
|
|
1353
|
+
if (typeof key !== "string") continue;
|
|
1354
|
+
const dist = Math.abs(percentageFromKey(key) - percentage);
|
|
1355
|
+
if (dist <= PCT_TOLERANCE && dist < bestDist) {
|
|
1356
|
+
best = { prop, idx: i };
|
|
1357
|
+
bestDist = dist;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return best;
|
|
1361
|
+
}
|
|
1362
|
+
function updateKeyframeInScript(script, animationId, percentage, properties, ease) {
|
|
1363
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1364
|
+
if (!parsed) return script;
|
|
1365
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1366
|
+
if (!target) return script;
|
|
1367
|
+
const kfPropNode = findPropertyNode2(target.call.varsArg, "keyframes");
|
|
1368
|
+
if (!kfPropNode) return script;
|
|
1369
|
+
if (kfPropNode.value?.type === "ArrayExpression") {
|
|
1370
|
+
return updateArrayKeyframeByPct(script, kfPropNode.value, percentage, properties, ease);
|
|
1371
|
+
}
|
|
1372
|
+
if (kfPropNode.value?.type !== "ObjectExpression") return script;
|
|
1373
|
+
const match = findKfPropByPct(kfPropNode.value, percentage);
|
|
1374
|
+
if (!match) return script;
|
|
1375
|
+
const record = { ...properties };
|
|
1376
|
+
if (ease) record.ease = ease;
|
|
1377
|
+
const ms = new MagicString(script);
|
|
1378
|
+
ms.overwrite(match.prop.value.start, match.prop.value.end, recordToCode(record));
|
|
1379
|
+
return ms.toString();
|
|
1380
|
+
}
|
|
1381
|
+
function updateArrayKeyframeByPct(script, arrayNode, percentage, properties, ease) {
|
|
1382
|
+
const elements = (arrayNode.elements ?? []).filter(
|
|
1383
|
+
(el2) => !!el2 && el2.type === "ObjectExpression"
|
|
1384
|
+
);
|
|
1385
|
+
const n = elements.length;
|
|
1386
|
+
if (n === 0) return script;
|
|
1387
|
+
const idx = n > 1 ? Math.round(percentage / 100 * (n - 1)) : 0;
|
|
1388
|
+
const el = elements[Math.max(0, Math.min(n - 1, idx))];
|
|
1389
|
+
if (!el) return script;
|
|
1390
|
+
const merged = {
|
|
1391
|
+
...valueNodeToRecord(el, script),
|
|
1392
|
+
...properties
|
|
1393
|
+
};
|
|
1394
|
+
if (ease) merged.ease = ease;
|
|
1395
|
+
const ms = new MagicString(script);
|
|
1396
|
+
ms.overwrite(el.start, el.end, recordToCode(merged));
|
|
1397
|
+
return ms.toString();
|
|
1398
|
+
}
|
|
1399
|
+
function buildTargetRecord(existing, source, properties, ease) {
|
|
1400
|
+
if (!existing || existing.prop.value?.type !== "ObjectExpression") {
|
|
1401
|
+
const record = { ...properties };
|
|
1402
|
+
if (ease) record.ease = ease;
|
|
1403
|
+
return record;
|
|
1404
|
+
}
|
|
1405
|
+
const existingRecord = valueNodeToRecord(existing.prop.value, source);
|
|
1406
|
+
const existingEase = typeof existingRecord.ease === "string" ? existingRecord.ease : void 0;
|
|
1407
|
+
const merged = { ...existingRecord };
|
|
1408
|
+
for (const [k, v] of Object.entries(properties)) merged[k] = v;
|
|
1409
|
+
const finalEase = ease ?? existingEase;
|
|
1410
|
+
if (finalEase) merged.ease = finalEase;
|
|
1411
|
+
else delete merged.ease;
|
|
1412
|
+
return merged;
|
|
1413
|
+
}
|
|
1414
|
+
function backfilledSiblingRecord(valueNode, source, newPropKeys, backfillDefaults) {
|
|
1415
|
+
if (valueNode?.type !== "ObjectExpression") return null;
|
|
1416
|
+
const record = valueNodeToRecord(valueNode, source);
|
|
1417
|
+
let changed = false;
|
|
1418
|
+
for (const pk of newPropKeys) {
|
|
1419
|
+
const defaultVal = backfillDefaults[pk];
|
|
1420
|
+
if (pk in record || defaultVal == null) continue;
|
|
1421
|
+
record[pk] = defaultVal;
|
|
1422
|
+
changed = true;
|
|
1423
|
+
}
|
|
1424
|
+
return changed ? record : null;
|
|
1425
|
+
}
|
|
1426
|
+
function locateWithKeyframes(script, animationId) {
|
|
1427
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1428
|
+
if (!parsed) return null;
|
|
1429
|
+
const convertedId = animationId.replace(/-from-|-fromTo-/, "-to-");
|
|
1430
|
+
const target = parsed.located.find((l) => l.id === animationId) ?? parsed.located.find((l) => l.id === convertedId);
|
|
1431
|
+
if (!target) return null;
|
|
1432
|
+
const kfPropNode = findPropertyNode2(target.call.varsArg, "keyframes");
|
|
1433
|
+
if (!kfPropNode || kfPropNode.value?.type !== "ObjectExpression") return null;
|
|
1434
|
+
return { script, parsed, target, kfNode: kfPropNode.value };
|
|
1435
|
+
}
|
|
1436
|
+
function convertArrayKeyframesToObject(script, target) {
|
|
1437
|
+
const kfPropNode = findPropertyNode2(target.call.varsArg, "keyframes");
|
|
1438
|
+
if (!kfPropNode || kfPropNode.value?.type !== "ArrayExpression") return script;
|
|
1439
|
+
const els = (kfPropNode.value.elements ?? []).filter(
|
|
1440
|
+
(el) => !!el && el.type === "ObjectExpression"
|
|
1441
|
+
);
|
|
1442
|
+
const n = els.length;
|
|
1443
|
+
if (n === 0) return script;
|
|
1444
|
+
const entries = els.map((el, i) => {
|
|
1445
|
+
const pct = n > 1 ? Math.round(i / (n - 1) * 1e3) / 10 : 0;
|
|
1446
|
+
return `${JSON.stringify(`${pct}%`)}: ${script.slice(el.start, el.end)}`;
|
|
1447
|
+
});
|
|
1448
|
+
const ms = new MagicString(script);
|
|
1449
|
+
ms.overwrite(kfPropNode.value.start, kfPropNode.value.end, `{ ${entries.join(", ")} }`);
|
|
1450
|
+
return ms.toString();
|
|
1451
|
+
}
|
|
1452
|
+
function ensureKeyframesNode(script, animationId) {
|
|
1453
|
+
const direct = locateWithKeyframes(script, animationId);
|
|
1454
|
+
if (direct) return direct;
|
|
1455
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1456
|
+
const target = parsed?.located.find((l) => l.id === animationId);
|
|
1457
|
+
if (!target) return null;
|
|
1458
|
+
const kfProp = findPropertyNode2(target.call.varsArg, "keyframes");
|
|
1459
|
+
if (kfProp?.value?.type === "ArrayExpression") {
|
|
1460
|
+
const normalized = convertArrayKeyframesToObject(script, target);
|
|
1461
|
+
if (normalized !== script) return locateWithKeyframes(normalized, animationId);
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
const converted = convertFlatTweenToKeyframes(script, target);
|
|
1465
|
+
if (converted === script) return null;
|
|
1466
|
+
return locateWithKeyframes(converted, animationId);
|
|
1467
|
+
}
|
|
1468
|
+
function collectBackfillOverwrites(kfNode, src, properties, backfillDefaults, skip) {
|
|
1469
|
+
const result = /* @__PURE__ */ new Map();
|
|
1470
|
+
if (!backfillDefaults) return result;
|
|
1471
|
+
const newPropKeys = Object.keys(properties);
|
|
1472
|
+
for (const prop of percentagePropsOf(kfNode)) {
|
|
1473
|
+
if (prop === skip.existingProp || skip.endpoints.has(prop)) continue;
|
|
1474
|
+
const rec = backfilledSiblingRecord(prop.value, src, newPropKeys, backfillDefaults);
|
|
1475
|
+
if (rec) result.set(prop, rec);
|
|
1476
|
+
}
|
|
1477
|
+
return result;
|
|
1478
|
+
}
|
|
1479
|
+
function addKeyframeToScript(script, animationId, percentage, properties, ease, backfillDefaults) {
|
|
1480
|
+
const located = ensureKeyframesNode(script, animationId);
|
|
1481
|
+
if (!located) return script;
|
|
1482
|
+
const { script: src, kfNode } = located;
|
|
1483
|
+
const existing = findKfPropByPct(kfNode, percentage);
|
|
1484
|
+
const targetRecord = buildTargetRecord(existing, src, properties, ease);
|
|
1485
|
+
const endpointOverwrites = existing ? /* @__PURE__ */ new Map() : autoEndpointOverwrites(kfNode, src, percentage, properties);
|
|
1486
|
+
const backfillOverwrites = collectBackfillOverwrites(kfNode, src, properties, backfillDefaults, {
|
|
1487
|
+
existingProp: existing?.prop,
|
|
1488
|
+
endpoints: endpointOverwrites
|
|
1489
|
+
});
|
|
1490
|
+
const ms = new MagicString(src);
|
|
1491
|
+
if (existing) {
|
|
1492
|
+
if (existing.prop.value?.type === "ObjectExpression") {
|
|
1493
|
+
for (const [k, v] of Object.entries(properties)) {
|
|
1494
|
+
upsertProp(ms, existing.prop.value, k, v);
|
|
1495
|
+
}
|
|
1496
|
+
if (ease !== void 0) upsertProp(ms, existing.prop.value, "ease", ease);
|
|
1497
|
+
} else {
|
|
1498
|
+
ms.overwrite(existing.prop.value.start, existing.prop.value.end, recordToCode(targetRecord));
|
|
1499
|
+
}
|
|
1500
|
+
} else {
|
|
1501
|
+
insertNewKeyframe(ms, kfNode, percentage, `${percentage}%`, recordToCode(targetRecord));
|
|
1502
|
+
}
|
|
1503
|
+
for (const [prop, rec] of [...endpointOverwrites, ...backfillOverwrites]) {
|
|
1504
|
+
ms.overwrite(prop.value.start, prop.value.end, recordToCode(rec));
|
|
1505
|
+
}
|
|
1506
|
+
return ms.toString();
|
|
1507
|
+
}
|
|
1508
|
+
function insertNewKeyframe(ms, kfNode, percentage, pctKey, valueCode) {
|
|
1509
|
+
const allProps = (kfNode.properties ?? []).filter((p) => isObjectProperty2(p));
|
|
1510
|
+
let insertBeforeProp = null;
|
|
1511
|
+
for (const prop of allProps) {
|
|
1512
|
+
const key = propKeyName2(prop);
|
|
1513
|
+
if (typeof key === "string" && percentageFromKey(key) > percentage) {
|
|
1514
|
+
insertBeforeProp = prop;
|
|
1515
|
+
break;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
if (insertBeforeProp) {
|
|
1519
|
+
ms.appendLeft(insertBeforeProp.start, `${JSON.stringify(pctKey)}: ${valueCode}, `);
|
|
1520
|
+
} else {
|
|
1521
|
+
const sep = allProps.length > 0 ? ", " : "";
|
|
1522
|
+
ms.appendLeft(kfNode.end - 1, `${sep}${JSON.stringify(pctKey)}: ${valueCode}`);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function collapseKeyframesToFlat(ms, varsNode, source, remainingRecord) {
|
|
1526
|
+
if (varsNode?.type !== "ObjectExpression") return;
|
|
1527
|
+
const dropKeyframeKeys = (key) => key === "keyframes" || key === "easeEach";
|
|
1528
|
+
const { entries } = preservedEntries(varsNode, source, dropKeyframeKeys, {});
|
|
1529
|
+
for (const [k, v] of Object.entries(remainingRecord)) {
|
|
1530
|
+
if (k !== "ease") entries.push(`${safeKey(k)}: ${valueToCode(v)}`);
|
|
1531
|
+
}
|
|
1532
|
+
ms.overwrite(varsNode.start, varsNode.end, `{ ${entries.join(", ")} }`);
|
|
1533
|
+
}
|
|
1534
|
+
function arrayKeyframePct(i, n) {
|
|
1535
|
+
return n > 1 ? i / (n - 1) * 100 : 0;
|
|
1536
|
+
}
|
|
1537
|
+
function removeArrayKeyframe(ms, varsArg, arrNode, script, percentage) {
|
|
1538
|
+
const elements = (arrNode.elements ?? []).filter(
|
|
1539
|
+
(e) => !!e && e.type === "ObjectExpression"
|
|
1540
|
+
);
|
|
1541
|
+
const n = elements.length;
|
|
1542
|
+
if (n === 0) return false;
|
|
1543
|
+
let matchIdx = -1;
|
|
1544
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
1545
|
+
for (let i = 0; i < n; i++) {
|
|
1546
|
+
const dist = Math.abs(arrayKeyframePct(i, n) - percentage);
|
|
1547
|
+
if (dist <= PCT_TOLERANCE && dist < bestDist) {
|
|
1548
|
+
matchIdx = i;
|
|
1549
|
+
bestDist = dist;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
if (matchIdx === -1) return false;
|
|
1553
|
+
const remaining = elements.filter((_, i) => i !== matchIdx);
|
|
1554
|
+
if (remaining.length < 2) {
|
|
1555
|
+
const sole = remaining[0];
|
|
1556
|
+
const record = sole ? valueNodeToRecord(sole, script) : {};
|
|
1557
|
+
collapseKeyframesToFlat(ms, varsArg, script, record);
|
|
1558
|
+
return true;
|
|
1559
|
+
}
|
|
1560
|
+
removeProp(ms, elements[matchIdx], elements);
|
|
1561
|
+
return true;
|
|
1562
|
+
}
|
|
1563
|
+
function removeKeyframeFromScript(script, animationId, percentage) {
|
|
1564
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1565
|
+
if (!parsed) return script;
|
|
1566
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1567
|
+
if (!target) return script;
|
|
1568
|
+
const kfPropNode = findPropertyNode2(target.call.varsArg, "keyframes");
|
|
1569
|
+
if (!kfPropNode) return script;
|
|
1570
|
+
if (kfPropNode.value?.type === "ArrayExpression") {
|
|
1571
|
+
const ms2 = new MagicString(script);
|
|
1572
|
+
return removeArrayKeyframe(ms2, target.call.varsArg, kfPropNode.value, script, percentage) ? ms2.toString() : script;
|
|
1573
|
+
}
|
|
1574
|
+
if (kfPropNode.value?.type !== "ObjectExpression") return script;
|
|
1575
|
+
const kfNode = kfPropNode.value;
|
|
1576
|
+
const match = findKfPropByPct(kfNode, percentage);
|
|
1577
|
+
if (!match) return script;
|
|
1578
|
+
const ms = new MagicString(script);
|
|
1579
|
+
const remaining = percentagePropsOf(kfNode).filter((p) => p !== match.prop);
|
|
1580
|
+
if (remaining.length < 2) {
|
|
1581
|
+
const sole = remaining[0];
|
|
1582
|
+
const record = sole ? valueNodeToRecord(sole.value, script) : {};
|
|
1583
|
+
collapseKeyframesToFlat(ms, target.call.varsArg, script, record);
|
|
1584
|
+
return ms.toString();
|
|
1585
|
+
}
|
|
1586
|
+
const allProps = (kfNode.properties ?? []).filter((p) => isObjectProperty2(p));
|
|
1587
|
+
removeProp(ms, match.prop, allProps);
|
|
1588
|
+
return ms.toString();
|
|
1589
|
+
}
|
|
1590
|
+
function removePropertyFromAnimation(script, animationId, property, from = false) {
|
|
1591
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1592
|
+
if (!parsed) return script;
|
|
1593
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1594
|
+
if (!target) return script;
|
|
1595
|
+
const { call } = target;
|
|
1596
|
+
const objNode = from ? call.method === "fromTo" ? call.fromArg : null : call.varsArg;
|
|
1597
|
+
if (!objNode) return script;
|
|
1598
|
+
const propNode = findPropertyNode2(objNode, property);
|
|
1599
|
+
if (!propNode) return script;
|
|
1600
|
+
const allProps = (objNode.properties ?? []).filter((p) => isObjectProperty2(p));
|
|
1601
|
+
const ms = new MagicString(script);
|
|
1602
|
+
removeProp(ms, propNode, allProps);
|
|
1603
|
+
return ms.toString();
|
|
1604
|
+
}
|
|
1605
|
+
function removeAllKeyframesFromScript(script, animationId) {
|
|
1606
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1607
|
+
if (!parsed) return script;
|
|
1608
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1609
|
+
if (!target) return script;
|
|
1610
|
+
const kfs = target.animation.keyframes?.keyframes;
|
|
1611
|
+
if (!kfs || kfs.length === 0) return script;
|
|
1612
|
+
const sorted = [...kfs].sort((a, b) => a.percentage - b.percentage);
|
|
1613
|
+
const collapse = target.call.method === "from" ? sorted[0] : sorted[sorted.length - 1];
|
|
1614
|
+
if (!collapse) return script;
|
|
1615
|
+
const ms = new MagicString(script);
|
|
1616
|
+
overwriteVarsArg(
|
|
1617
|
+
ms,
|
|
1618
|
+
target.call,
|
|
1619
|
+
buildVarsObjectCode(buildCollapsedFlatVars(target.animation, collapse))
|
|
1620
|
+
);
|
|
1621
|
+
return ms.toString();
|
|
1622
|
+
}
|
|
1623
|
+
function buildCollapsedFlatVars(animation, collapse) {
|
|
1624
|
+
const flat = { ...animation.properties };
|
|
1625
|
+
for (const [k, v] of Object.entries(collapse.properties)) {
|
|
1626
|
+
if (k !== "ease") flat[k] = v;
|
|
1627
|
+
}
|
|
1628
|
+
if (animation.duration !== void 0) flat.duration = animation.duration;
|
|
1629
|
+
if (animation.ease) flat.ease = animation.ease;
|
|
1630
|
+
for (const [k, v] of Object.entries(animation.extras ?? {})) {
|
|
1631
|
+
if (typeof v === "number" || typeof v === "string") flat[k] = v;
|
|
1632
|
+
}
|
|
1633
|
+
return flat;
|
|
1634
|
+
}
|
|
1635
|
+
function buildKeyframesVarsCode(animation, fromProps, toProps, varsNode, source, setDuration) {
|
|
1636
|
+
const fromEntries = Object.entries(fromProps).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`);
|
|
1637
|
+
const toEntries = Object.entries(toProps).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`);
|
|
1638
|
+
const easeEntry = animation.ease ? `, easeEach: ${JSON.stringify(animation.ease)}` : "";
|
|
1639
|
+
const kfCode = `{ "0%": { ${fromEntries.join(", ")} }, "100%": { ${toEntries.join(", ")} }${easeEntry} }`;
|
|
1640
|
+
let preserved = preservedVarsEntries(varsNode, source);
|
|
1641
|
+
if (setDuration !== void 0) {
|
|
1642
|
+
preserved = preserved.filter((e) => !/^\s*(immediateRender|data|duration)\s*:/.test(e));
|
|
1643
|
+
}
|
|
1644
|
+
const parts = [`keyframes: ${kfCode}`, ...preserved];
|
|
1645
|
+
if (setDuration !== void 0) parts.push(`duration: ${Math.max(1e-3, setDuration)}`);
|
|
1646
|
+
if (animation.ease) parts.push(`ease: "none"`);
|
|
1647
|
+
return `{ ${parts.join(", ")} }`;
|
|
1648
|
+
}
|
|
1649
|
+
function convertToKeyframesFromScript(script, animationId, resolvedFromValues, setDuration = 1) {
|
|
1650
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1651
|
+
if (!parsed) return script;
|
|
1652
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1653
|
+
if (!target) return script;
|
|
1654
|
+
const { animation, call } = target;
|
|
1655
|
+
if (animation.keyframes) return script;
|
|
1656
|
+
const isSet = call.method === "set";
|
|
1657
|
+
const { fromProps, toProps } = resolveConversionProps(animation, resolvedFromValues);
|
|
1658
|
+
const ms = new MagicString(script);
|
|
1659
|
+
if (isSet && animation.global) {
|
|
1660
|
+
const calleeObj = call.node.callee.object;
|
|
1661
|
+
if (calleeObj?.type === "Identifier") {
|
|
1662
|
+
ms.overwrite(calleeObj.start, calleeObj.end, parsed.timelineVar);
|
|
1663
|
+
}
|
|
1664
|
+
const args = call.node.arguments;
|
|
1665
|
+
if (args.length > 0 && args.length < 3) {
|
|
1666
|
+
ms.appendLeft(args[args.length - 1].end, ", 0");
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
if (call.method === "from" || call.method === "fromTo" || isSet) {
|
|
1670
|
+
ms.overwrite(call.node.callee.property.start, call.node.callee.property.end, "to");
|
|
1671
|
+
}
|
|
1672
|
+
if (call.method === "fromTo" && call.fromArg) {
|
|
1673
|
+
ms.remove(call.fromArg.start, call.varsArg.start);
|
|
1674
|
+
}
|
|
1675
|
+
overwriteVarsArg(
|
|
1676
|
+
ms,
|
|
1677
|
+
call,
|
|
1678
|
+
buildKeyframesVarsCode(
|
|
1679
|
+
animation,
|
|
1680
|
+
fromProps,
|
|
1681
|
+
toProps,
|
|
1682
|
+
call.varsArg,
|
|
1683
|
+
script,
|
|
1684
|
+
isSet ? setDuration : void 0
|
|
1685
|
+
)
|
|
1686
|
+
);
|
|
1687
|
+
return ms.toString();
|
|
1688
|
+
}
|
|
1689
|
+
function buildKeyframeObjectCode(keyframes, easeEach) {
|
|
1690
|
+
const entries = keyframes.map((kf) => {
|
|
1691
|
+
const props = Object.entries(kf.properties).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`);
|
|
1692
|
+
if (kf.ease) props.push(`ease: ${JSON.stringify(kf.ease)}`);
|
|
1693
|
+
if (kf.auto) props.push(`_auto: 1`);
|
|
1694
|
+
return `${JSON.stringify(`${kf.percentage}%`)}: { ${props.join(", ")} }`;
|
|
1695
|
+
});
|
|
1696
|
+
if (easeEach) entries.push(`easeEach: ${JSON.stringify(easeEach)}`);
|
|
1697
|
+
return `{ ${entries.join(", ")} }`;
|
|
1698
|
+
}
|
|
1699
|
+
function materializeKeyframesFromScript(script, animationId, keyframes, easeEach, resolvedSelector) {
|
|
1700
|
+
if (keyframes.length === 0) return script;
|
|
1701
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1702
|
+
if (!parsed) return script;
|
|
1703
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1704
|
+
if (!target) return script;
|
|
1705
|
+
const { call } = target;
|
|
1706
|
+
const sorted = [...keyframes].sort((a, b) => a.percentage - b.percentage);
|
|
1707
|
+
const kfObjCode = buildKeyframeObjectCode(sorted, easeEach);
|
|
1708
|
+
const ms = new MagicString(script);
|
|
1709
|
+
if (resolvedSelector) {
|
|
1710
|
+
const selectorArg = call.node.arguments[0];
|
|
1711
|
+
if (selectorArg)
|
|
1712
|
+
ms.overwrite(selectorArg.start, selectorArg.end, JSON.stringify(resolvedSelector));
|
|
1713
|
+
}
|
|
1714
|
+
const kfProp = findPropertyNode2(call.varsArg, "keyframes");
|
|
1715
|
+
if (kfProp) {
|
|
1716
|
+
ms.overwrite(kfProp.value.start, kfProp.value.end, kfObjCode);
|
|
1717
|
+
} else if (call.varsArg?.type === "ObjectExpression") {
|
|
1718
|
+
const vars = call.varsArg;
|
|
1719
|
+
if (vars.properties.length > 0) {
|
|
1720
|
+
ms.prependLeft(vars.properties[0].start, `keyframes: ${kfObjCode}, `);
|
|
1721
|
+
} else {
|
|
1722
|
+
ms.appendLeft(vars.end - 1, `keyframes: ${kfObjCode}`);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
const eachProp = findPropertyNode2(call.varsArg, "easeEach");
|
|
1726
|
+
if (eachProp) {
|
|
1727
|
+
const allProps = (call.varsArg.properties ?? []).filter((p) => isObjectProperty2(p));
|
|
1728
|
+
removeProp(ms, eachProp, allProps);
|
|
1729
|
+
}
|
|
1730
|
+
return ms.toString();
|
|
1731
|
+
}
|
|
1732
|
+
function addAnimationWithKeyframesToScript(script, targetSelector, position, duration, keyframes, ease, easeEach) {
|
|
1733
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1734
|
+
if (!parsed) return { script, id: "" };
|
|
1735
|
+
const insertionPoint = findInsertionPoint(parsed);
|
|
1736
|
+
if (insertionPoint === null) return { script, id: "" };
|
|
1737
|
+
const sorted = [...keyframes].sort((a, b) => a.percentage - b.percentage);
|
|
1738
|
+
const kfObjCode = buildKeyframeObjectCode(sorted, easeEach);
|
|
1739
|
+
const varParts = [`keyframes: ${kfObjCode}`, `duration: ${valueToCode(duration)}`];
|
|
1740
|
+
if (ease) varParts.push(`ease: ${JSON.stringify(ease)}`);
|
|
1741
|
+
const stmtCode = `${parsed.timelineVar}.to(${JSON.stringify(targetSelector)}, { ${varParts.join(", ")} }, ${valueToCode(position)});`;
|
|
1742
|
+
const ms = new MagicString(script);
|
|
1743
|
+
ms.appendLeft(insertionPoint, "\n" + stmtCode);
|
|
1744
|
+
const result = ms.toString();
|
|
1745
|
+
const reParsed = parseGsapScriptAcornForWrite(result);
|
|
1746
|
+
const newId = reParsed?.located[reParsed.located.length - 1]?.id ?? "";
|
|
1747
|
+
return { script: result, id: newId };
|
|
1748
|
+
}
|
|
1749
|
+
function collectPropertyKeys(anim) {
|
|
1750
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1751
|
+
if (anim.keyframes) {
|
|
1752
|
+
for (const kf of anim.keyframes.keyframes) {
|
|
1753
|
+
for (const k of Object.keys(kf.properties)) keys.add(k);
|
|
1754
|
+
}
|
|
1755
|
+
} else {
|
|
1756
|
+
for (const k of Object.keys(anim.properties)) keys.add(k);
|
|
1757
|
+
}
|
|
1758
|
+
return keys;
|
|
1759
|
+
}
|
|
1760
|
+
function partitionPropertyGroups(keys) {
|
|
1761
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1762
|
+
for (const key of keys) {
|
|
1763
|
+
if (key === "transformOrigin") continue;
|
|
1764
|
+
const group = classifyPropertyGroup(key);
|
|
1765
|
+
let arr = groups.get(group);
|
|
1766
|
+
if (!arr) {
|
|
1767
|
+
arr = [];
|
|
1768
|
+
groups.set(group, arr);
|
|
1769
|
+
}
|
|
1770
|
+
arr.push(key);
|
|
1771
|
+
}
|
|
1772
|
+
return groups;
|
|
1773
|
+
}
|
|
1774
|
+
function assignTransformOrigin(groupProps) {
|
|
1775
|
+
let largestGroup;
|
|
1776
|
+
let largestCount = 0;
|
|
1777
|
+
for (const [group, props] of groupProps) {
|
|
1778
|
+
if (props.length > largestCount) {
|
|
1779
|
+
largestCount = props.length;
|
|
1780
|
+
largestGroup = group;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
const largest = largestGroup ? groupProps.get(largestGroup) : void 0;
|
|
1784
|
+
if (largest) largest.push("transformOrigin");
|
|
1785
|
+
}
|
|
1786
|
+
function filterGroupKeyframes(kfs, propSet) {
|
|
1787
|
+
const result = [];
|
|
1788
|
+
for (const kf of kfs) {
|
|
1789
|
+
const filtered = {};
|
|
1790
|
+
for (const [k, v] of Object.entries(kf.properties)) {
|
|
1791
|
+
if (propSet.has(k)) filtered[k] = v;
|
|
1792
|
+
}
|
|
1793
|
+
if (Object.keys(filtered).length > 0) {
|
|
1794
|
+
result.push({
|
|
1795
|
+
percentage: kf.percentage,
|
|
1796
|
+
properties: filtered,
|
|
1797
|
+
...kf.ease ? { ease: kf.ease } : {}
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
return result;
|
|
1802
|
+
}
|
|
1803
|
+
function filterGroupProperties(properties, propSet) {
|
|
1804
|
+
const result = {};
|
|
1805
|
+
for (const [k, v] of Object.entries(properties)) {
|
|
1806
|
+
if (propSet.has(k)) result[k] = v;
|
|
1807
|
+
}
|
|
1808
|
+
return result;
|
|
1809
|
+
}
|
|
1810
|
+
function addGroupAnimToScript(script, anim, propSet) {
|
|
1811
|
+
if (anim.keyframes) {
|
|
1812
|
+
const groupKeyframes = filterGroupKeyframes(anim.keyframes.keyframes, propSet);
|
|
1813
|
+
if (groupKeyframes.length === 0) return { script, id: "" };
|
|
1814
|
+
const pos = typeof anim.position === "number" ? anim.position : 0;
|
|
1815
|
+
return addAnimationWithKeyframesToScript(
|
|
1816
|
+
script,
|
|
1817
|
+
anim.targetSelector,
|
|
1818
|
+
pos,
|
|
1819
|
+
anim.duration ?? 0.5,
|
|
1820
|
+
groupKeyframes,
|
|
1821
|
+
anim.keyframes.easeEach ?? anim.ease
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
const groupProperties = filterGroupProperties(anim.properties, propSet);
|
|
1825
|
+
if (Object.keys(groupProperties).length === 0) return { script, id: "" };
|
|
1826
|
+
const fromProperties = anim.method === "fromTo" && anim.fromProperties ? filterGroupProperties(anim.fromProperties, propSet) : void 0;
|
|
1827
|
+
return addAnimationToScript(script, {
|
|
1828
|
+
targetSelector: anim.targetSelector,
|
|
1829
|
+
method: anim.method,
|
|
1830
|
+
position: anim.position,
|
|
1831
|
+
duration: anim.duration,
|
|
1832
|
+
ease: anim.ease,
|
|
1833
|
+
properties: groupProperties,
|
|
1834
|
+
fromProperties,
|
|
1835
|
+
extras: anim.extras
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
function splitIntoPropertyGroupsFromScript(script, animationId) {
|
|
1839
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1840
|
+
if (!parsed) return { script, ids: [animationId] };
|
|
1841
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1842
|
+
if (!target) return { script, ids: [animationId] };
|
|
1843
|
+
const { animation } = target;
|
|
1844
|
+
const allPropKeys = collectPropertyKeys(animation);
|
|
1845
|
+
const groupProps = partitionPropertyGroups(allPropKeys);
|
|
1846
|
+
if (groupProps.size <= 1) return { script, ids: [animationId] };
|
|
1847
|
+
if (allPropKeys.has("transformOrigin")) assignTransformOrigin(groupProps);
|
|
1848
|
+
let result = removeAnimationFromScript(script, animationId);
|
|
1849
|
+
for (const [, props] of groupProps) {
|
|
1850
|
+
const { script: next, id } = addGroupAnimToScript(result, animation, new Set(props));
|
|
1851
|
+
if (id) result = next;
|
|
1852
|
+
}
|
|
1853
|
+
const reParsed = parseGsapScriptAcornForWrite(result);
|
|
1854
|
+
const newIds = (reParsed?.located ?? []).filter((l) => l.animation.targetSelector === animation.targetSelector).map((l) => l.id);
|
|
1855
|
+
return { script: result, ids: newIds };
|
|
1856
|
+
}
|
|
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;
|
|
1859
|
+
}
|
|
1860
|
+
function isAddLabelCall(expr, timelineVar, name) {
|
|
1861
|
+
const firstArg = expr?.arguments?.[0];
|
|
1862
|
+
return isTimelineMethodCall(expr, timelineVar, "addLabel") && firstArg?.type === "Literal" && firstArg.value === name;
|
|
1863
|
+
}
|
|
1864
|
+
function findLabelStatements(parsed, name) {
|
|
1865
|
+
const targets = [];
|
|
1866
|
+
acornWalk2.simple(parsed.ast, {
|
|
1867
|
+
ExpressionStatement(node) {
|
|
1868
|
+
if (isAddLabelCall(node.expression, parsed.timelineVar, name)) targets.push(node);
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
return targets;
|
|
1872
|
+
}
|
|
1873
|
+
function addLabelToScript(script, name, position) {
|
|
1874
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1875
|
+
if (!parsed) return script;
|
|
1876
|
+
const existing = findLabelStatements(parsed, name)[0];
|
|
1877
|
+
if (existing) {
|
|
1878
|
+
const ms2 = new MagicString(script);
|
|
1879
|
+
const posArg = existing.expression.arguments?.[1];
|
|
1880
|
+
if (posArg) ms2.overwrite(posArg.start, posArg.end, valueToCode(position));
|
|
1881
|
+
else ms2.appendLeft(existing.expression.end - 1, `, ${valueToCode(position)}`);
|
|
1882
|
+
return ms2.toString();
|
|
1883
|
+
}
|
|
1884
|
+
const insertionPoint = findInsertionPoint(parsed);
|
|
1885
|
+
if (insertionPoint === null) return script;
|
|
1886
|
+
const ms = new MagicString(script);
|
|
1887
|
+
const labelCode = `${parsed.timelineVar}.addLabel(${JSON.stringify(name)}, ${valueToCode(position)});`;
|
|
1888
|
+
ms.appendLeft(insertionPoint, "\n" + labelCode);
|
|
1889
|
+
return ms.toString();
|
|
1890
|
+
}
|
|
1891
|
+
function removeLabelFromScript(script, name) {
|
|
1892
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1893
|
+
if (!parsed) return script;
|
|
1894
|
+
const targets = findLabelStatements(parsed, name);
|
|
1895
|
+
if (!targets.length) return script;
|
|
1896
|
+
const ms = new MagicString(script);
|
|
1897
|
+
for (const target of targets) {
|
|
1898
|
+
const end = target.end < script.length && script[target.end] === "\n" ? target.end + 1 : target.end;
|
|
1899
|
+
ms.remove(target.start, end);
|
|
1900
|
+
}
|
|
1901
|
+
return ms.toString();
|
|
1902
|
+
}
|
|
1903
|
+
function removePropsByKey(ms, objNode, keys) {
|
|
1904
|
+
if (objNode?.type !== "ObjectExpression") return;
|
|
1905
|
+
const allProps = (objNode.properties ?? []).filter(isObjectProperty2);
|
|
1906
|
+
const marked = allProps.map((p) => keys.has(propKeyName2(p) ?? ""));
|
|
1907
|
+
let i = 0;
|
|
1908
|
+
while (i < allProps.length) {
|
|
1909
|
+
if (!marked[i]) {
|
|
1910
|
+
i++;
|
|
1911
|
+
continue;
|
|
1912
|
+
}
|
|
1913
|
+
const blockStart = i;
|
|
1914
|
+
while (i < allProps.length && marked[i]) i++;
|
|
1915
|
+
ms.remove(...blockRemoveRange(allProps, blockStart, i));
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
function blockRemoveRange(allProps, blockStart, blockEnd) {
|
|
1919
|
+
if (blockStart === 0 && blockEnd === allProps.length)
|
|
1920
|
+
return [allProps[0].start, allProps[allProps.length - 1].end];
|
|
1921
|
+
if (blockStart === 0) return [allProps[0].start, allProps[blockEnd].start];
|
|
1922
|
+
return [allProps[blockStart - 1].end, allProps[blockEnd - 1].end];
|
|
1923
|
+
}
|
|
1924
|
+
function readLastWaypointXY(mpVal) {
|
|
1925
|
+
if (mpVal?.type !== "ObjectExpression") return { x: null, y: null };
|
|
1926
|
+
const pathProp = findPropertyNode2(mpVal, "path");
|
|
1927
|
+
if (pathProp?.value?.type !== "ArrayExpression") return { x: null, y: null };
|
|
1928
|
+
const elems = pathProp.value.elements ?? [];
|
|
1929
|
+
const last = elems[elems.length - 1];
|
|
1930
|
+
if (last?.type !== "ObjectExpression") return { x: null, y: null };
|
|
1931
|
+
return {
|
|
1932
|
+
x: readNumericLiteralNode(findPropertyNode2(last, "x")?.value),
|
|
1933
|
+
y: readNumericLiteralNode(findPropertyNode2(last, "y")?.value)
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
function readNumericLiteralNode(v) {
|
|
1937
|
+
if (LITERAL_NODE_TYPES.has(v?.type) && typeof v.value === "number") return v.value;
|
|
1938
|
+
if (v?.type === "UnaryExpression" && v.operator === "-" && typeof v.argument?.value === "number") {
|
|
1939
|
+
return -v.argument.value;
|
|
1940
|
+
}
|
|
1941
|
+
return null;
|
|
1942
|
+
}
|
|
1943
|
+
function disableArcPath(ms, call) {
|
|
1944
|
+
const mpProp = findPropertyNode2(call.varsArg, "motionPath");
|
|
1945
|
+
if (!mpProp) return false;
|
|
1946
|
+
const { x, y } = readLastWaypointXY(mpProp.value);
|
|
1947
|
+
if (x === null && y === null) {
|
|
1948
|
+
const allProps = (call.varsArg.properties ?? []).filter(isObjectProperty2);
|
|
1949
|
+
removeProp(ms, mpProp, allProps);
|
|
1950
|
+
return true;
|
|
1951
|
+
}
|
|
1952
|
+
const parts = [];
|
|
1953
|
+
if (x !== null) parts.push(`x: ${x}`);
|
|
1954
|
+
if (y !== null) parts.push(`y: ${y}`);
|
|
1955
|
+
ms.overwrite(mpProp.start, mpProp.end, parts.join(", "));
|
|
1956
|
+
return true;
|
|
1957
|
+
}
|
|
1958
|
+
function stripXYFromKeyframes(ms, kfPropNode) {
|
|
1959
|
+
if (kfPropNode?.value?.type !== "ObjectExpression") return;
|
|
1960
|
+
const xyKeys = /* @__PURE__ */ new Set(["x", "y"]);
|
|
1961
|
+
for (const pctProp of (kfPropNode.value.properties ?? []).filter(isObjectProperty2)) {
|
|
1962
|
+
const k = propKeyName2(pctProp);
|
|
1963
|
+
if (typeof k === "string" && k.endsWith("%") && pctProp.value?.type === "ObjectExpression") {
|
|
1964
|
+
removePropsByKey(ms, pctProp.value, xyKeys);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
function enableArcPath(ms, call, animation, config) {
|
|
1969
|
+
const waypoints = extractArcWaypoints(animation);
|
|
1970
|
+
if (waypoints.length < 2) return false;
|
|
1971
|
+
const segments = config.segments.length === waypoints.length - 1 ? config.segments : Array.from({ length: waypoints.length - 1 }, () => ({ curviness: 1 }));
|
|
1972
|
+
const motionPathCode = buildMotionPathObjectCode({
|
|
1973
|
+
waypoints,
|
|
1974
|
+
segments,
|
|
1975
|
+
autoRotate: config.autoRotate
|
|
1976
|
+
});
|
|
1977
|
+
const vars = call.varsArg;
|
|
1978
|
+
if (vars?.type !== "ObjectExpression") return false;
|
|
1979
|
+
const editable = (vars.properties ?? []).filter(isObjectProperty2);
|
|
1980
|
+
const survivesRemoval = editable.some((p) => {
|
|
1981
|
+
const k = propKeyName2(p);
|
|
1982
|
+
return k !== "x" && k !== "y";
|
|
1983
|
+
});
|
|
1984
|
+
const sep = survivesRemoval ? ", " : "";
|
|
1985
|
+
ms.appendRight(vars.start + 1, ` motionPath: ${motionPathCode}${sep}`);
|
|
1986
|
+
stripXYFromKeyframes(ms, findPropertyNode2(call.varsArg, "keyframes"));
|
|
1987
|
+
removePropsByKey(ms, call.varsArg, /* @__PURE__ */ new Set(["x", "y"]));
|
|
1988
|
+
return true;
|
|
1989
|
+
}
|
|
1990
|
+
function setArcPathInScript(script, animationId, config) {
|
|
1991
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
1992
|
+
if (!parsed) return script;
|
|
1993
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
1994
|
+
if (!target) return script;
|
|
1995
|
+
const ms = new MagicString(script);
|
|
1996
|
+
const handled = config.enabled ? enableArcPath(ms, target.call, target.animation, config) : disableArcPath(ms, target.call);
|
|
1997
|
+
return handled ? ms.toString() : script;
|
|
1998
|
+
}
|
|
1999
|
+
function updateArcSegmentInScript(script, animationId, segmentIndex, update) {
|
|
2000
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
2001
|
+
if (!parsed) return script;
|
|
2002
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
2003
|
+
if (!target) return script;
|
|
2004
|
+
const { call, animation } = target;
|
|
2005
|
+
if (!animation.arcPath?.enabled) return script;
|
|
2006
|
+
const segments = [...animation.arcPath.segments];
|
|
2007
|
+
const existingSeg = segments[segmentIndex];
|
|
2008
|
+
if (segmentIndex < 0 || segmentIndex >= segments.length || !existingSeg) return script;
|
|
2009
|
+
segments[segmentIndex] = { ...existingSeg, ...update };
|
|
2010
|
+
const waypoints = extractArcWaypoints(animation);
|
|
2011
|
+
if (waypoints.length < 2) return script;
|
|
2012
|
+
const motionPathCode = buildMotionPathObjectCode({
|
|
2013
|
+
waypoints,
|
|
2014
|
+
segments,
|
|
2015
|
+
autoRotate: animation.arcPath.autoRotate
|
|
2016
|
+
});
|
|
2017
|
+
const mpProp = findPropertyNode2(call.varsArg, "motionPath");
|
|
2018
|
+
if (!mpProp) return script;
|
|
2019
|
+
const ms = new MagicString(script);
|
|
2020
|
+
ms.overwrite(mpProp.value.start, mpProp.value.end, motionPathCode);
|
|
2021
|
+
return ms.toString();
|
|
2022
|
+
}
|
|
2023
|
+
function removeArcPathFromScript(script, animationId) {
|
|
2024
|
+
return setArcPathInScript(script, animationId, {
|
|
2025
|
+
enabled: false,
|
|
2026
|
+
autoRotate: false,
|
|
2027
|
+
segments: []
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
function updateAnimationSelectorInScript(script, animationId, newSelector) {
|
|
2031
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
2032
|
+
if (!parsed) return script;
|
|
2033
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
2034
|
+
if (!target) return script;
|
|
2035
|
+
const selectorArg = target.call.node.arguments?.[0];
|
|
2036
|
+
if (!selectorArg) return script;
|
|
2037
|
+
const ms = new MagicString(script);
|
|
2038
|
+
ms.overwrite(selectorArg.start, selectorArg.end, JSON.stringify(newSelector));
|
|
2039
|
+
return ms.toString();
|
|
2040
|
+
}
|
|
2041
|
+
function insertInheritedStateSetInScript(script, selector, position, properties) {
|
|
2042
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
2043
|
+
if (!parsed) return script;
|
|
2044
|
+
const props = Object.entries(properties).map(([k, v]) => `${safeKey(k)}: ${valueToCode(v)}`).join(", ");
|
|
2045
|
+
const code = `${parsed.timelineVar}.set(${JSON.stringify(selector)}, { ${props} }, ${position});`;
|
|
2046
|
+
const ms = new MagicString(script);
|
|
2047
|
+
const tlDecl = findTimelineDeclarationStatement(parsed.ast, parsed.timelineVar);
|
|
2048
|
+
const firstLocated = parsed.located[0];
|
|
2049
|
+
if (tlDecl) {
|
|
2050
|
+
ms.appendLeft(tlDecl.end, "\n" + code);
|
|
2051
|
+
} else if (firstLocated) {
|
|
2052
|
+
const firstCall = firstLocated.call;
|
|
2053
|
+
const exprStmt = findEnclosingExpressionStatement(firstCall.ancestors);
|
|
2054
|
+
const insertAt = exprStmt?.start ?? firstCall.node.start;
|
|
2055
|
+
ms.prependLeft(insertAt, code + "\n");
|
|
2056
|
+
} else {
|
|
2057
|
+
ms.append("\n" + code);
|
|
2058
|
+
}
|
|
2059
|
+
return ms.toString();
|
|
2060
|
+
}
|
|
2061
|
+
function computeForwardBaselines(matching, splitTime) {
|
|
2062
|
+
const before = [];
|
|
2063
|
+
const acc = {};
|
|
2064
|
+
for (const anim of matching) {
|
|
2065
|
+
before.push({ ...acc });
|
|
2066
|
+
const pos = typeof anim.position === "number" ? anim.position : 0;
|
|
2067
|
+
const dur = anim.duration ?? 0;
|
|
2068
|
+
const animEnd = pos + dur;
|
|
2069
|
+
if (anim.keyframes) {
|
|
2070
|
+
const kfs = anim.keyframes.keyframes;
|
|
2071
|
+
if (pos >= splitTime) {
|
|
2072
|
+
} else if (animEnd > splitTime) {
|
|
2073
|
+
for (const kf of kfs) {
|
|
2074
|
+
const kfTime = pos + kf.percentage / 100 * dur;
|
|
2075
|
+
if (kfTime <= splitTime) {
|
|
2076
|
+
for (const [k, v] of Object.entries(kf.properties)) acc[k] = v;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
} else {
|
|
2080
|
+
const lastKf = kfs[kfs.length - 1];
|
|
2081
|
+
if (lastKf) {
|
|
2082
|
+
for (const [k, v] of Object.entries(lastKf.properties)) acc[k] = v;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
if (animEnd <= splitTime) {
|
|
2088
|
+
for (const [k, v] of Object.entries(anim.properties)) acc[k] = v;
|
|
2089
|
+
continue;
|
|
2090
|
+
}
|
|
2091
|
+
if (pos >= splitTime) continue;
|
|
2092
|
+
const progress = dur > 0 ? (splitTime - pos) / dur : 0;
|
|
2093
|
+
const fromSource = anim.fromProperties ?? acc;
|
|
2094
|
+
for (const [k, v] of Object.entries(anim.properties)) {
|
|
2095
|
+
if (typeof v !== "number") {
|
|
2096
|
+
acc[k] = v;
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2099
|
+
const fromVal = typeof fromSource[k] === "number" ? fromSource[k] : 0;
|
|
2100
|
+
acc[k] = fromVal + (v - fromVal) * progress;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
return { before, final: { ...acc } };
|
|
2104
|
+
}
|
|
2105
|
+
function buildSpanningSplit(result, anim, pos, dur, fromSource, ctx) {
|
|
2106
|
+
const progress = dur > 0 ? (ctx.splitTime - pos) / dur : 0;
|
|
2107
|
+
const midProps = {};
|
|
2108
|
+
for (const [k, v] of Object.entries(anim.properties)) {
|
|
2109
|
+
if (typeof v !== "number") {
|
|
2110
|
+
midProps[k] = v;
|
|
2111
|
+
continue;
|
|
2112
|
+
}
|
|
2113
|
+
const fromVal = typeof fromSource[k] === "number" ? fromSource[k] : 0;
|
|
2114
|
+
midProps[k] = fromVal + (v - fromVal) * progress;
|
|
2115
|
+
}
|
|
2116
|
+
const trimmed = updateAnimationInScript(result, anim.id, {
|
|
2117
|
+
duration: ctx.splitTime - pos,
|
|
2118
|
+
properties: midProps
|
|
2119
|
+
});
|
|
2120
|
+
return addAnimationToScript(trimmed, {
|
|
2121
|
+
targetSelector: ctx.newSelector,
|
|
2122
|
+
method: "fromTo",
|
|
2123
|
+
position: ctx.newElementStart,
|
|
2124
|
+
duration: pos + dur - ctx.splitTime,
|
|
2125
|
+
properties: { ...anim.properties },
|
|
2126
|
+
fromProperties: { ...midProps },
|
|
2127
|
+
ease: anim.ease,
|
|
2128
|
+
extras: anim.extras
|
|
2129
|
+
}).script;
|
|
2130
|
+
}
|
|
2131
|
+
function applyTweenSplit(result, anim, baselineBefore, ctx, skippedSelectors) {
|
|
2132
|
+
const pos = typeof anim.position === "number" ? anim.position : 0;
|
|
2133
|
+
const dur = anim.duration ?? 0;
|
|
2134
|
+
const animEnd = pos + dur;
|
|
2135
|
+
if (anim.keyframes) {
|
|
2136
|
+
if (pos >= ctx.splitTime)
|
|
2137
|
+
return updateAnimationSelectorInScript(result, anim.id, ctx.newSelector);
|
|
2138
|
+
if (animEnd > ctx.splitTime) {
|
|
2139
|
+
skippedSelectors.push(`${ctx.originalSelector} (keyframes spanning split)`);
|
|
2140
|
+
}
|
|
2141
|
+
return result;
|
|
2142
|
+
}
|
|
2143
|
+
if (animEnd <= ctx.splitTime) return result;
|
|
2144
|
+
if (pos >= ctx.splitTime)
|
|
2145
|
+
return updateAnimationSelectorInScript(result, anim.id, ctx.newSelector);
|
|
2146
|
+
const fromSource = anim.fromProperties ?? baselineBefore;
|
|
2147
|
+
return buildSpanningSplit(result, anim, pos, dur, fromSource, ctx);
|
|
2148
|
+
}
|
|
2149
|
+
function splitAnimationsInScript(script, opts) {
|
|
2150
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
2151
|
+
if (!parsed) return { script, skippedSelectors: [] };
|
|
2152
|
+
const originalSelector = `#${opts.originalId}`;
|
|
2153
|
+
const newSelector = `#${opts.newId}`;
|
|
2154
|
+
const animations = parsed.located.map((l) => l.animation);
|
|
2155
|
+
const skippedSelectors = [];
|
|
2156
|
+
for (const a of animations) {
|
|
2157
|
+
if (a.targetSelector !== originalSelector && a.targetSelector.includes(opts.originalId)) {
|
|
2158
|
+
skippedSelectors.push(a.targetSelector);
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
const matching = animations.filter((a) => a.targetSelector === originalSelector);
|
|
2162
|
+
if (matching.length === 0) return { script, skippedSelectors };
|
|
2163
|
+
let result = script;
|
|
2164
|
+
const newElementStart = opts.splitTime;
|
|
2165
|
+
const { before: baselineBefore, final: finalInheritedProps } = computeForwardBaselines(
|
|
2166
|
+
matching,
|
|
2167
|
+
opts.splitTime
|
|
2168
|
+
);
|
|
2169
|
+
const ctx = { splitTime: opts.splitTime, originalSelector, newSelector, newElementStart };
|
|
2170
|
+
for (let i = matching.length - 1; i >= 0; i--) {
|
|
2171
|
+
const anim = matching[i];
|
|
2172
|
+
if (!anim) continue;
|
|
2173
|
+
result = applyTweenSplit(result, anim, baselineBefore[i] ?? {}, ctx, skippedSelectors);
|
|
2174
|
+
}
|
|
2175
|
+
if (Object.keys(finalInheritedProps).length > 0) {
|
|
2176
|
+
result = insertInheritedStateSetInScript(
|
|
2177
|
+
result,
|
|
2178
|
+
newSelector,
|
|
2179
|
+
newElementStart,
|
|
2180
|
+
finalInheritedProps
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
return { script: result, skippedSelectors };
|
|
2184
|
+
}
|
|
2185
|
+
function isLoopNode(node) {
|
|
2186
|
+
const t = node?.type;
|
|
2187
|
+
return t === "ForStatement" || t === "ForInStatement" || t === "ForOfStatement" || t === "WhileStatement";
|
|
2188
|
+
}
|
|
2189
|
+
function isForEachStatement(node) {
|
|
2190
|
+
return node?.type === "ExpressionStatement" && node.expression?.type === "CallExpression" && node.expression.callee?.property?.name === "forEach";
|
|
2191
|
+
}
|
|
2192
|
+
function findEnclosingLoopNode(ancestors) {
|
|
2193
|
+
for (let i = ancestors.length - 2; i >= 0; i--) {
|
|
2194
|
+
const node = ancestors[i];
|
|
2195
|
+
if (isLoopNode(node) || isForEachStatement(node)) return node;
|
|
2196
|
+
}
|
|
2197
|
+
return null;
|
|
2198
|
+
}
|
|
2199
|
+
function loopBodyStatements(loopNode) {
|
|
2200
|
+
let body;
|
|
2201
|
+
if (loopNode?.type === "ExpressionStatement") {
|
|
2202
|
+
const cb = loopNode.expression?.arguments?.[0];
|
|
2203
|
+
body = cb?.body;
|
|
2204
|
+
} else {
|
|
2205
|
+
body = loopNode?.body;
|
|
2206
|
+
}
|
|
2207
|
+
if (body?.type !== "BlockStatement") return null;
|
|
2208
|
+
return (body.body ?? []).filter((s) => s?.type === "ExpressionStatement");
|
|
2209
|
+
}
|
|
2210
|
+
function loopIndexVarName(loopNode) {
|
|
2211
|
+
if (loopNode?.type === "ForStatement") {
|
|
2212
|
+
const decl = loopNode.init?.declarations?.[0];
|
|
2213
|
+
return typeof decl?.id?.name === "string" ? decl.id.name : null;
|
|
2214
|
+
}
|
|
2215
|
+
return null;
|
|
2216
|
+
}
|
|
2217
|
+
function isIndexBindingPosition(node, parent) {
|
|
2218
|
+
if (parent?.type === "MemberExpression") return parent.property === node && !parent.computed;
|
|
2219
|
+
if (parent?.type === "Property" || parent?.type === "ObjectProperty") {
|
|
2220
|
+
return parent.key === node && !parent.computed;
|
|
2221
|
+
}
|
|
2222
|
+
return false;
|
|
2223
|
+
}
|
|
2224
|
+
function substituteLoopIndex(stmt, indexVar, idx, script) {
|
|
2225
|
+
const base = stmt.start;
|
|
2226
|
+
const src = script.slice(base, stmt.end);
|
|
2227
|
+
const ranges = [];
|
|
2228
|
+
acornWalk2.ancestor(stmt, {
|
|
2229
|
+
Identifier(node, _state, ancestors) {
|
|
2230
|
+
if (node.name !== indexVar) return;
|
|
2231
|
+
if (isIndexBindingPosition(node, ancestors[ancestors.length - 2])) return;
|
|
2232
|
+
ranges.push([node.start - base, node.end - base]);
|
|
2233
|
+
}
|
|
2234
|
+
});
|
|
2235
|
+
if (ranges.length === 0) return src;
|
|
2236
|
+
ranges.sort((a, b) => b[0] - a[0]);
|
|
2237
|
+
let out = src;
|
|
2238
|
+
for (const [s, e] of ranges) out = out.slice(0, s) + String(idx) + out.slice(e);
|
|
2239
|
+
return out;
|
|
2240
|
+
}
|
|
2241
|
+
function buildUnrollReplacement(timelineVar, animation, elements) {
|
|
2242
|
+
const duration = typeof animation.duration === "number" ? animation.duration : 8;
|
|
2243
|
+
const ease = typeof animation.ease === "string" ? animation.ease : "none";
|
|
2244
|
+
const pos = animation.position ?? 0;
|
|
2245
|
+
const posCode = typeof pos === "number" ? String(pos) : JSON.stringify(pos);
|
|
2246
|
+
const calls = elements.map((el) => {
|
|
2247
|
+
const sorted = [...el.keyframes].sort((a, b) => a.percentage - b.percentage);
|
|
2248
|
+
const kfCode = buildKeyframeObjectCode(sorted, el.easeEach);
|
|
2249
|
+
return `${timelineVar}.to(${JSON.stringify(el.selector)}, { keyframes: ${kfCode}, duration: ${duration}, ease: ${JSON.stringify(ease)} }, ${posCode});`;
|
|
2250
|
+
});
|
|
2251
|
+
return calls.join("\n ");
|
|
2252
|
+
}
|
|
2253
|
+
function buildUnrollCallForElement(timelineVar, animation, el) {
|
|
2254
|
+
const duration = typeof animation.duration === "number" ? animation.duration : 8;
|
|
2255
|
+
const ease = typeof animation.ease === "string" ? animation.ease : "none";
|
|
2256
|
+
const pos = animation.position ?? 0;
|
|
2257
|
+
const posCode = typeof pos === "number" ? String(pos) : JSON.stringify(pos);
|
|
2258
|
+
const sorted = [...el.keyframes].sort((a, b) => a.percentage - b.percentage);
|
|
2259
|
+
const kfCode = buildKeyframeObjectCode(sorted, el.easeEach);
|
|
2260
|
+
return `${timelineVar}.to(${JSON.stringify(el.selector)}, { keyframes: ${kfCode}, duration: ${duration}, ease: ${JSON.stringify(ease)} }, ${posCode});`;
|
|
2261
|
+
}
|
|
2262
|
+
var REFUSE_UNROLL = /* @__PURE__ */ Symbol("refuse-unroll");
|
|
2263
|
+
function loopBodyRawStatements(loopNode) {
|
|
2264
|
+
const body = loopNode?.type === "ExpressionStatement" ? loopNode.expression?.arguments?.[0]?.body : loopNode?.body;
|
|
2265
|
+
return body?.type === "BlockStatement" ? body.body ?? [] : [];
|
|
2266
|
+
}
|
|
2267
|
+
function rebindsIndex(node, indexVar) {
|
|
2268
|
+
if (node.type === "VariableDeclarator") return node.id?.name === indexVar;
|
|
2269
|
+
if (node.type === "FunctionExpression" || node.type === "FunctionDeclaration" || node.type === "ArrowFunctionExpression") {
|
|
2270
|
+
return (node.params ?? []).some((p) => p?.name === indexVar);
|
|
2271
|
+
}
|
|
2272
|
+
return false;
|
|
2273
|
+
}
|
|
2274
|
+
function isShorthandIndexUse(node, indexVar) {
|
|
2275
|
+
return (node.type === "Property" || node.type === "ObjectProperty") && node.shorthand === true && propKeyName2(node) === indexVar;
|
|
2276
|
+
}
|
|
2277
|
+
function hasUnsafeLoopIndexUse(stmt, indexVar) {
|
|
2278
|
+
let unsafe = false;
|
|
2279
|
+
acornWalk2.full(stmt, (node) => {
|
|
2280
|
+
if (!unsafe && (isShorthandIndexUse(node, indexVar) || rebindsIndex(node, indexVar))) {
|
|
2281
|
+
unsafe = true;
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
return unsafe;
|
|
2285
|
+
}
|
|
2286
|
+
function unrollSiblingStrategy(loopNode, targetStmt, stmts, indexVar) {
|
|
2287
|
+
const siblings = stmts.filter((s) => s !== targetStmt);
|
|
2288
|
+
const hasUnmodeledSibling = loopBodyRawStatements(loopNode).some(
|
|
2289
|
+
(s) => s !== targetStmt && !stmts.includes(s)
|
|
2290
|
+
);
|
|
2291
|
+
if (siblings.length === 0 && !hasUnmodeledSibling) return "blanket";
|
|
2292
|
+
if (hasUnmodeledSibling || !indexVar) return "refuse";
|
|
2293
|
+
return siblings.some((s) => hasUnsafeLoopIndexUse(s, indexVar)) ? "refuse" : "preserve";
|
|
2294
|
+
}
|
|
2295
|
+
function emitUnrolledLines(stmts, targetStmt, elements, timelineVar, animation, indexVar, script) {
|
|
2296
|
+
const lines = [];
|
|
2297
|
+
for (let idx = 0; idx < elements.length; idx++) {
|
|
2298
|
+
const el = elements[idx];
|
|
2299
|
+
if (!el) continue;
|
|
2300
|
+
for (const stmt of stmts) {
|
|
2301
|
+
lines.push(
|
|
2302
|
+
stmt === targetStmt ? buildUnrollCallForElement(timelineVar, animation, el) : substituteLoopIndex(stmt, indexVar, idx, script)
|
|
2303
|
+
);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return lines.join("\n ");
|
|
2307
|
+
}
|
|
2308
|
+
function buildLoopUnrollPreserving(script, timelineVar, animation, elements, loopNode, targetStmt) {
|
|
2309
|
+
const stmts = loopBodyStatements(loopNode);
|
|
2310
|
+
if (!stmts || !stmts.includes(targetStmt)) return null;
|
|
2311
|
+
const indexVar = loopIndexVarName(loopNode);
|
|
2312
|
+
const strategy = unrollSiblingStrategy(loopNode, targetStmt, stmts, indexVar);
|
|
2313
|
+
if (strategy === "blanket") return null;
|
|
2314
|
+
if (strategy === "refuse" || !indexVar) return REFUSE_UNROLL;
|
|
2315
|
+
return emitUnrolledLines(stmts, targetStmt, elements, timelineVar, animation, indexVar, script);
|
|
2316
|
+
}
|
|
2317
|
+
function unrollDynamicAnimations(script, animationId, elements) {
|
|
2318
|
+
if (elements.length === 0) return script;
|
|
2319
|
+
const parsed = parseGsapScriptAcornForWrite(script);
|
|
2320
|
+
if (!parsed) return script;
|
|
2321
|
+
const target = parsed.located.find((l) => l.id === animationId);
|
|
2322
|
+
if (!target) return script;
|
|
2323
|
+
const ms = new MagicString(script);
|
|
2324
|
+
const loopNode = findEnclosingLoopNode(target.call.ancestors);
|
|
2325
|
+
if (loopNode) {
|
|
2326
|
+
const targetStmt = findEnclosingExpressionStatement(target.call.ancestors);
|
|
2327
|
+
const preserving = targetStmt ? buildLoopUnrollPreserving(
|
|
2328
|
+
script,
|
|
2329
|
+
parsed.timelineVar,
|
|
2330
|
+
target.animation,
|
|
2331
|
+
elements,
|
|
2332
|
+
loopNode,
|
|
2333
|
+
targetStmt
|
|
2334
|
+
) : null;
|
|
2335
|
+
if (preserving === REFUSE_UNROLL) return script;
|
|
2336
|
+
const replacement = preserving ?? buildUnrollReplacement(parsed.timelineVar, target.animation, elements);
|
|
2337
|
+
ms.overwrite(loopNode.start, loopNode.end, replacement);
|
|
2338
|
+
} else {
|
|
2339
|
+
const stmt = findEnclosingExpressionStatement(target.call.ancestors);
|
|
2340
|
+
if (!stmt) return script;
|
|
2341
|
+
const replacement = buildUnrollReplacement(parsed.timelineVar, target.animation, elements);
|
|
2342
|
+
ms.overwrite(stmt.start, stmt.end, replacement);
|
|
2343
|
+
}
|
|
2344
|
+
return ms.toString();
|
|
2345
|
+
}
|
|
2346
|
+
export {
|
|
2347
|
+
addAnimationToScript,
|
|
2348
|
+
addAnimationWithKeyframesToScript,
|
|
2349
|
+
addKeyframeToScript,
|
|
2350
|
+
addLabelToScript,
|
|
2351
|
+
convertToKeyframesFromScript,
|
|
2352
|
+
materializeKeyframesFromScript,
|
|
2353
|
+
removeAllKeyframesFromScript,
|
|
2354
|
+
removeAnimationFromScript,
|
|
2355
|
+
removeArcPathFromScript,
|
|
2356
|
+
removeKeyframeFromScript,
|
|
2357
|
+
removeLabelFromScript,
|
|
2358
|
+
removePropertyFromAnimation,
|
|
2359
|
+
scalePositionsInScript,
|
|
2360
|
+
setArcPathInScript,
|
|
2361
|
+
shiftPositionsInScript,
|
|
2362
|
+
splitAnimationsInScript,
|
|
2363
|
+
splitIntoPropertyGroupsFromScript,
|
|
2364
|
+
unrollDynamicAnimations,
|
|
2365
|
+
updateAnimationInScript,
|
|
2366
|
+
updateArcSegmentInScript,
|
|
2367
|
+
updateKeyframeInScript
|
|
2368
|
+
};
|
|
2369
|
+
//# sourceMappingURL=gsapWriterAcorn.js.map
|