@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,1294 @@
|
|
|
1
|
+
// src/gsapParserAcorn.ts
|
|
2
|
+
import * as acorn from "acorn";
|
|
3
|
+
import * as acornWalk from "acorn-walk";
|
|
4
|
+
|
|
5
|
+
// src/gsapConstants.ts
|
|
6
|
+
var PROPERTY_GROUPS = {
|
|
7
|
+
position: /* @__PURE__ */ new Set(["x", "y", "xPercent", "yPercent"]),
|
|
8
|
+
scale: /* @__PURE__ */ new Set(["scale", "scaleX", "scaleY"]),
|
|
9
|
+
size: /* @__PURE__ */ new Set(["width", "height"]),
|
|
10
|
+
rotation: /* @__PURE__ */ new Set(["rotation", "skewX", "skewY"]),
|
|
11
|
+
visual: /* @__PURE__ */ new Set(["opacity", "autoAlpha"]),
|
|
12
|
+
other: /* @__PURE__ */ new Set()
|
|
13
|
+
};
|
|
14
|
+
var PROP_TO_GROUP = /* @__PURE__ */ new Map();
|
|
15
|
+
for (const [group, props] of Object.entries(PROPERTY_GROUPS)) {
|
|
16
|
+
for (const p of props) PROP_TO_GROUP.set(p, group);
|
|
17
|
+
}
|
|
18
|
+
function classifyPropertyGroup(prop) {
|
|
19
|
+
return PROP_TO_GROUP.get(prop) ?? "other";
|
|
20
|
+
}
|
|
21
|
+
function classifyTweenPropertyGroup(properties) {
|
|
22
|
+
const groups = /* @__PURE__ */ new Set();
|
|
23
|
+
for (const key of Object.keys(properties)) {
|
|
24
|
+
if (key === "transformOrigin" || key === "_auto" || key === "data") continue;
|
|
25
|
+
const g = classifyPropertyGroup(key);
|
|
26
|
+
groups.add(g);
|
|
27
|
+
}
|
|
28
|
+
if (groups.size === 1) return groups.values().next().value;
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/gsapSerialize.ts
|
|
33
|
+
function editabilityForProvenance(provenance) {
|
|
34
|
+
if (!provenance || provenance.kind === "literal") return "direct";
|
|
35
|
+
if (provenance.kind === "runtime-dynamic") return "source";
|
|
36
|
+
return "unroll";
|
|
37
|
+
}
|
|
38
|
+
function buildArcPath(coords, curviness, autoRotate, isCubic) {
|
|
39
|
+
const first = coords[0];
|
|
40
|
+
if (coords.length < 2 || !first) return void 0;
|
|
41
|
+
const segments = [];
|
|
42
|
+
let waypoints;
|
|
43
|
+
if (isCubic && coords.length >= 4) {
|
|
44
|
+
waypoints = [first];
|
|
45
|
+
for (let i = 1; i + 2 < coords.length; i += 3) {
|
|
46
|
+
const cp1 = coords[i];
|
|
47
|
+
const cp2 = coords[i + 1];
|
|
48
|
+
const anchor = coords[i + 2];
|
|
49
|
+
if (!cp1 || !cp2 || !anchor) continue;
|
|
50
|
+
waypoints.push(anchor);
|
|
51
|
+
segments.push({ curviness, cp1, cp2 });
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
waypoints = coords;
|
|
55
|
+
for (let i = 0; i < waypoints.length - 1; i++) segments.push({ curviness });
|
|
56
|
+
}
|
|
57
|
+
return { arcPath: { enabled: true, autoRotate, segments }, waypoints };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/gsapInline.ts
|
|
61
|
+
var SKIP_KEYS = /* @__PURE__ */ new Set(["type", "start", "end", "loc", "range", "__hfProvenance", "__hfOrder"]);
|
|
62
|
+
var FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
63
|
+
"ArrowFunctionExpression",
|
|
64
|
+
"FunctionExpression",
|
|
65
|
+
"FunctionDeclaration"
|
|
66
|
+
]);
|
|
67
|
+
var GSAP_METHODS = /* @__PURE__ */ new Set(["set", "to", "from", "fromTo"]);
|
|
68
|
+
var MAX_DEPTH = 8;
|
|
69
|
+
var MAX_ITERS = 512;
|
|
70
|
+
function isFunctionNode(node) {
|
|
71
|
+
return !!node && FUNCTION_TYPES.has(node.type);
|
|
72
|
+
}
|
|
73
|
+
function isNode(x) {
|
|
74
|
+
return !!x && typeof x === "object" && typeof x.type === "string";
|
|
75
|
+
}
|
|
76
|
+
function transformChildren(node, fn) {
|
|
77
|
+
for (const key of Object.keys(node)) {
|
|
78
|
+
if (SKIP_KEYS.has(key) || isNonValueIdentifierSlot(node, key)) continue;
|
|
79
|
+
const child = node[key];
|
|
80
|
+
if (Array.isArray(child)) {
|
|
81
|
+
for (let i = 0; i < child.length; i++) child[i] = fn(child[i]);
|
|
82
|
+
} else {
|
|
83
|
+
node[key] = fn(child);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function cloneNode(node) {
|
|
88
|
+
return structuredClone(node);
|
|
89
|
+
}
|
|
90
|
+
function collectPatternNames(pattern, out) {
|
|
91
|
+
if (pattern?.type === "Identifier") out.add(pattern.name);
|
|
92
|
+
else if (pattern?.type === "AssignmentPattern") collectPatternNames(pattern.left, out);
|
|
93
|
+
else if (pattern?.type === "RestElement") collectPatternNames(pattern.argument, out);
|
|
94
|
+
}
|
|
95
|
+
function collectBoundNames(root) {
|
|
96
|
+
const names = /* @__PURE__ */ new Set();
|
|
97
|
+
const visit = (node) => {
|
|
98
|
+
if (!isNode(node)) return node;
|
|
99
|
+
if (isFunctionNode(node)) for (const p of node.params ?? []) collectPatternNames(p, names);
|
|
100
|
+
else if (node.type === "VariableDeclarator") collectPatternNames(node.id, names);
|
|
101
|
+
else if (node.type === "CatchClause") collectPatternNames(node.param, names);
|
|
102
|
+
transformChildren(node, visit);
|
|
103
|
+
return node;
|
|
104
|
+
};
|
|
105
|
+
visit(root);
|
|
106
|
+
return names;
|
|
107
|
+
}
|
|
108
|
+
function isNonValueIdentifierSlot(node, key) {
|
|
109
|
+
if (node.computed) return false;
|
|
110
|
+
return node.type === "MemberExpression" && key === "property" || node.type === "Property" && key === "key";
|
|
111
|
+
}
|
|
112
|
+
function substituteParams(node, bindings) {
|
|
113
|
+
const shadowed = collectBoundNames(node);
|
|
114
|
+
let effective = bindings;
|
|
115
|
+
if (shadowed.size > 0) {
|
|
116
|
+
effective = new Map(bindings);
|
|
117
|
+
for (const name of shadowed) effective.delete(name);
|
|
118
|
+
}
|
|
119
|
+
if (effective.size === 0) return node;
|
|
120
|
+
return replace(node, effective);
|
|
121
|
+
}
|
|
122
|
+
function replace(node, bindings) {
|
|
123
|
+
if (!isNode(node)) return node;
|
|
124
|
+
if (node.type === "Identifier" && bindings.has(node.name)) {
|
|
125
|
+
return cloneNode(bindings.get(node.name));
|
|
126
|
+
}
|
|
127
|
+
transformChildren(node, (child) => replace(child, bindings));
|
|
128
|
+
return node;
|
|
129
|
+
}
|
|
130
|
+
function tagProvenance(node, provenance) {
|
|
131
|
+
if (node && typeof node === "object") node.__hfProvenance = provenance;
|
|
132
|
+
return node;
|
|
133
|
+
}
|
|
134
|
+
function readProvenance(node) {
|
|
135
|
+
return node?.__hfProvenance;
|
|
136
|
+
}
|
|
137
|
+
function numericLiteral(value) {
|
|
138
|
+
return { type: "Literal", value, raw: String(value) };
|
|
139
|
+
}
|
|
140
|
+
function walkNodes(node, fn) {
|
|
141
|
+
if (!isNode(node)) return;
|
|
142
|
+
fn(node);
|
|
143
|
+
for (const key of Object.keys(node)) {
|
|
144
|
+
if (SKIP_KEYS.has(key)) continue;
|
|
145
|
+
const child = node[key];
|
|
146
|
+
if (Array.isArray(child)) for (const c of child) walkNodes(c, fn);
|
|
147
|
+
else walkNodes(child, fn);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function timelineRootName(call) {
|
|
151
|
+
let obj = call.callee?.object;
|
|
152
|
+
while (obj?.type === "CallExpression") obj = obj.callee?.object;
|
|
153
|
+
return obj?.type === "Identifier" ? obj.name : null;
|
|
154
|
+
}
|
|
155
|
+
function isTimelineRooted(call, timelineVar) {
|
|
156
|
+
if (timelineRootName(call) !== timelineVar) return false;
|
|
157
|
+
return call.callee?.property?.type === "Identifier" && GSAP_METHODS.has(call.callee.property.name);
|
|
158
|
+
}
|
|
159
|
+
function containsTimelineCall(node, timelineVar) {
|
|
160
|
+
let found = false;
|
|
161
|
+
walkNodes(node, (n) => {
|
|
162
|
+
if (n.type === "CallExpression" && isTimelineRooted(n, timelineVar)) found = true;
|
|
163
|
+
});
|
|
164
|
+
return found;
|
|
165
|
+
}
|
|
166
|
+
function rangeOf(node) {
|
|
167
|
+
return typeof node.start === "number" && typeof node.end === "number" ? [node.start, node.end] : void 0;
|
|
168
|
+
}
|
|
169
|
+
function isShapeEligible(fn) {
|
|
170
|
+
return isFunctionNode(fn) && fn.body?.type === "BlockStatement" && !(fn.params ?? []).some((p) => p.type !== "Identifier");
|
|
171
|
+
}
|
|
172
|
+
function callsAny(node, names) {
|
|
173
|
+
let hit = false;
|
|
174
|
+
walkNodes(node, (n) => {
|
|
175
|
+
if (n.type === "CallExpression" && n.callee?.type === "Identifier" && names.has(n.callee.name)) {
|
|
176
|
+
hit = true;
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
return hit;
|
|
180
|
+
}
|
|
181
|
+
function varDeclHelper(stmt) {
|
|
182
|
+
if (stmt.declarations?.length !== 1) return null;
|
|
183
|
+
const d = stmt.declarations[0];
|
|
184
|
+
return d.id?.type === "Identifier" && isShapeEligible(d.init) ? [d.id.name, d.init] : null;
|
|
185
|
+
}
|
|
186
|
+
function helperFromStatement(stmt) {
|
|
187
|
+
if (stmt.type === "FunctionDeclaration") {
|
|
188
|
+
return stmt.id && isShapeEligible(stmt) ? [stmt.id.name, stmt] : null;
|
|
189
|
+
}
|
|
190
|
+
if (stmt.type === "VariableDeclaration") return varDeclHelper(stmt);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
function gatherHelperCandidates(program) {
|
|
194
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
195
|
+
for (const stmt of program.body ?? []) {
|
|
196
|
+
const helper = helperFromStatement(stmt);
|
|
197
|
+
if (helper) candidates.set(helper[0], helper[1]);
|
|
198
|
+
}
|
|
199
|
+
return candidates;
|
|
200
|
+
}
|
|
201
|
+
function timelineBuildingNames(candidates, timelineVar) {
|
|
202
|
+
const building = /* @__PURE__ */ new Set();
|
|
203
|
+
for (const [name, fn] of candidates) {
|
|
204
|
+
if (containsTimelineCall(fn.body, timelineVar)) building.add(name);
|
|
205
|
+
}
|
|
206
|
+
for (let changed = true; changed; ) {
|
|
207
|
+
changed = false;
|
|
208
|
+
for (const [name, fn] of candidates) {
|
|
209
|
+
if (!building.has(name) && callsAny(fn.body, building)) {
|
|
210
|
+
building.add(name);
|
|
211
|
+
changed = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return building;
|
|
216
|
+
}
|
|
217
|
+
function bump(counts, key) {
|
|
218
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
219
|
+
}
|
|
220
|
+
function safelyDroppable(program, candidates) {
|
|
221
|
+
const names = new Set(candidates.keys());
|
|
222
|
+
const totalIds = /* @__PURE__ */ new Map();
|
|
223
|
+
const stmtCalls = /* @__PURE__ */ new Map();
|
|
224
|
+
walkNodes(program, (n) => {
|
|
225
|
+
if (n.type === "Identifier" && names.has(n.name)) bump(totalIds, n.name);
|
|
226
|
+
const e = n.type === "ExpressionStatement" ? n.expression : void 0;
|
|
227
|
+
if (e?.type === "CallExpression" && e.callee?.type === "Identifier" && names.has(e.callee.name)) {
|
|
228
|
+
bump(stmtCalls, e.callee.name);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
const safe = /* @__PURE__ */ new Map();
|
|
232
|
+
for (const [name, fn] of candidates) {
|
|
233
|
+
if ((totalIds.get(name) ?? 0) === 1 + (stmtCalls.get(name) ?? 0)) safe.set(name, fn);
|
|
234
|
+
}
|
|
235
|
+
return safe;
|
|
236
|
+
}
|
|
237
|
+
function collectInlinableHelpers(program, timelineVar) {
|
|
238
|
+
const candidates = gatherHelperCandidates(program);
|
|
239
|
+
if (candidates.size === 0) return candidates;
|
|
240
|
+
const building = timelineBuildingNames(candidates, timelineVar);
|
|
241
|
+
for (const name of [...candidates.keys()]) if (!building.has(name)) candidates.delete(name);
|
|
242
|
+
if (candidates.size === 0) return candidates;
|
|
243
|
+
return safelyDroppable(program, candidates);
|
|
244
|
+
}
|
|
245
|
+
function isHelperDecl(stmt, helpers) {
|
|
246
|
+
if (stmt.type === "FunctionDeclaration") return !!stmt.id && helpers.get(stmt.id.name) === stmt;
|
|
247
|
+
if (stmt.type === "VariableDeclaration" && stmt.declarations?.length === 1) {
|
|
248
|
+
const d = stmt.declarations[0];
|
|
249
|
+
return d.id?.type === "Identifier" && helpers.get(d.id.name) === d.init;
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
function bodyStatements(node) {
|
|
254
|
+
if (node?.type === "BlockStatement") return node.body ?? [];
|
|
255
|
+
return node ? [{ type: "ExpressionStatement", expression: node }] : [];
|
|
256
|
+
}
|
|
257
|
+
function tagTimelineCalls(stmts, prov, ctx) {
|
|
258
|
+
for (const stmt of stmts) {
|
|
259
|
+
walkNodes(stmt, (n) => {
|
|
260
|
+
if (n.type === "CallExpression" && isTimelineRooted(n, ctx.timelineVar)) {
|
|
261
|
+
tagProvenance(n, { ...prov });
|
|
262
|
+
n.__hfOrder = ctx.order.n++;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function expandBody(bodyStmts, bindings, prov, ctx) {
|
|
268
|
+
const block = substituteParams(cloneNode({ type: "BlockStatement", body: bodyStmts }), bindings);
|
|
269
|
+
tagTimelineCalls(block.body, prov, ctx);
|
|
270
|
+
return expandStatements(block.body, { ...ctx, depth: ctx.depth + 1 });
|
|
271
|
+
}
|
|
272
|
+
function inlineHelper(call, ctx) {
|
|
273
|
+
const fn = ctx.helpers.get(call.callee.name);
|
|
274
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
275
|
+
(fn.params ?? []).forEach((p, i) => {
|
|
276
|
+
const arg = call.arguments?.[i];
|
|
277
|
+
if (arg) bindings.set(p.name, arg);
|
|
278
|
+
});
|
|
279
|
+
const prov = {
|
|
280
|
+
kind: "helper",
|
|
281
|
+
fn: call.callee.name,
|
|
282
|
+
callSite: ++ctx.site.n,
|
|
283
|
+
sourceRange: rangeOf(call)
|
|
284
|
+
};
|
|
285
|
+
return expandBody(fn.body.body, bindings, prov, ctx);
|
|
286
|
+
}
|
|
287
|
+
function assignStep(update, resolve) {
|
|
288
|
+
if (update.operator === "+=") return asNum(resolve(update.right));
|
|
289
|
+
if (update.operator === "-=") {
|
|
290
|
+
const s = asNum(resolve(update.right));
|
|
291
|
+
return s === void 0 ? void 0 : -s;
|
|
292
|
+
}
|
|
293
|
+
if (update.operator === "=" && update.right?.type === "BinaryExpression") {
|
|
294
|
+
return asNum(resolve(update.right.right));
|
|
295
|
+
}
|
|
296
|
+
return void 0;
|
|
297
|
+
}
|
|
298
|
+
function updatedVarName(update) {
|
|
299
|
+
if (update?.type === "UpdateExpression") return update.argument?.name ?? null;
|
|
300
|
+
if (update?.type === "AssignmentExpression") return update.left?.name ?? null;
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
function loopStep(update, varName, resolve) {
|
|
304
|
+
if (updatedVarName(update) !== varName) return void 0;
|
|
305
|
+
if (update.type === "UpdateExpression") return update.operator === "++" ? 1 : -1;
|
|
306
|
+
return assignStep(update, resolve);
|
|
307
|
+
}
|
|
308
|
+
function asNum(v) {
|
|
309
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
310
|
+
}
|
|
311
|
+
function loopSatisfied(op, x, end) {
|
|
312
|
+
if (op === "<") return x < end;
|
|
313
|
+
if (op === "<=") return x <= end;
|
|
314
|
+
if (op === ">") return x > end;
|
|
315
|
+
if (op === ">=") return x >= end;
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
function forInitVar(init) {
|
|
319
|
+
if (init?.type !== "VariableDeclaration" || init.declarations?.length !== 1) return null;
|
|
320
|
+
const d = init.declarations[0];
|
|
321
|
+
return d.id?.type === "Identifier" ? { name: d.id.name, initExpr: d.init } : null;
|
|
322
|
+
}
|
|
323
|
+
function parseForHeader(stmt, resolve) {
|
|
324
|
+
const iv = forInitVar(stmt.init);
|
|
325
|
+
const test = stmt.test;
|
|
326
|
+
if (!iv || test?.type !== "BinaryExpression" || test.left?.name !== iv.name) return null;
|
|
327
|
+
const start = asNum(resolve(iv.initExpr));
|
|
328
|
+
const end = asNum(resolve(test.right));
|
|
329
|
+
const step = loopStep(stmt.update, iv.name, resolve);
|
|
330
|
+
if (start === void 0 || end === void 0 || !step) return null;
|
|
331
|
+
return { v: iv.name, start, end, op: test.operator, step };
|
|
332
|
+
}
|
|
333
|
+
function unrollFor(stmt, ctx) {
|
|
334
|
+
const h = parseForHeader(stmt, ctx.resolve);
|
|
335
|
+
if (!h) return null;
|
|
336
|
+
const body = bodyStatements(stmt.body);
|
|
337
|
+
const out = [];
|
|
338
|
+
const site = ++ctx.site.n;
|
|
339
|
+
let iteration = 0;
|
|
340
|
+
for (let x = h.start; loopSatisfied(h.op, x, h.end); x += h.step) {
|
|
341
|
+
if (iteration >= MAX_ITERS) return null;
|
|
342
|
+
const prov = {
|
|
343
|
+
kind: "loop",
|
|
344
|
+
callSite: site,
|
|
345
|
+
iteration,
|
|
346
|
+
sourceRange: rangeOf(stmt)
|
|
347
|
+
};
|
|
348
|
+
out.push(...expandBody(body, /* @__PURE__ */ new Map([[h.v, numericLiteral(x)]]), prov, ctx));
|
|
349
|
+
iteration++;
|
|
350
|
+
}
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
function forOfVarName(left) {
|
|
354
|
+
if (left?.type === "VariableDeclaration") {
|
|
355
|
+
const id = left.declarations?.[0]?.id;
|
|
356
|
+
return id?.type === "Identifier" ? id.name : null;
|
|
357
|
+
}
|
|
358
|
+
return left?.type === "Identifier" ? left.name : null;
|
|
359
|
+
}
|
|
360
|
+
function unrollOverArray(elements, body, elName, idxName, range, ctx) {
|
|
361
|
+
const out = [];
|
|
362
|
+
const site = ++ctx.site.n;
|
|
363
|
+
elements.forEach((el, i) => {
|
|
364
|
+
if (!el) return;
|
|
365
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
366
|
+
if (elName) bindings.set(elName, el);
|
|
367
|
+
if (idxName) bindings.set(idxName, numericLiteral(i));
|
|
368
|
+
const prov = { kind: "loop", callSite: site, iteration: i, sourceRange: range };
|
|
369
|
+
out.push(...expandBody(body, bindings, prov, ctx));
|
|
370
|
+
});
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
function unrollForOf(stmt, ctx) {
|
|
374
|
+
if (stmt.right?.type !== "ArrayExpression") return null;
|
|
375
|
+
const elName = forOfVarName(stmt.left);
|
|
376
|
+
if (!elName) return null;
|
|
377
|
+
return unrollOverArray(
|
|
378
|
+
stmt.right.elements ?? [],
|
|
379
|
+
bodyStatements(stmt.body),
|
|
380
|
+
elName,
|
|
381
|
+
null,
|
|
382
|
+
rangeOf(stmt),
|
|
383
|
+
ctx
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
function callbackParamNames(cb) {
|
|
387
|
+
const names = [];
|
|
388
|
+
for (const p of [cb.params?.[0], cb.params?.[1]]) {
|
|
389
|
+
if (!p) names.push(null);
|
|
390
|
+
else if (p.type !== "Identifier") return null;
|
|
391
|
+
else names.push(p.name);
|
|
392
|
+
}
|
|
393
|
+
return { el: names[0], idx: names[1] };
|
|
394
|
+
}
|
|
395
|
+
function isForEachCall(callee) {
|
|
396
|
+
return callee?.type === "MemberExpression" && callee.property?.name === "forEach" && callee.object?.type === "ArrayExpression";
|
|
397
|
+
}
|
|
398
|
+
function forEachTarget(call) {
|
|
399
|
+
if (!isForEachCall(call.callee)) return null;
|
|
400
|
+
const cb = call.arguments?.[0];
|
|
401
|
+
return isFunctionNode(cb) ? { elements: call.callee.object.elements ?? [], cb } : null;
|
|
402
|
+
}
|
|
403
|
+
function unrollForEach(call, ctx) {
|
|
404
|
+
const target = forEachTarget(call);
|
|
405
|
+
if (!target) return null;
|
|
406
|
+
const params = callbackParamNames(target.cb);
|
|
407
|
+
if (!params) return null;
|
|
408
|
+
return unrollOverArray(
|
|
409
|
+
target.elements,
|
|
410
|
+
bodyStatements(target.cb.body),
|
|
411
|
+
params.el,
|
|
412
|
+
params.idx,
|
|
413
|
+
rangeOf(call),
|
|
414
|
+
ctx
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
function expandCall(call, ctx) {
|
|
418
|
+
if (call.callee?.type === "Identifier" && ctx.helpers.has(call.callee.name)) {
|
|
419
|
+
return inlineHelper(call, ctx);
|
|
420
|
+
}
|
|
421
|
+
return unrollForEach(call, ctx);
|
|
422
|
+
}
|
|
423
|
+
function expandStatement(stmt, ctx) {
|
|
424
|
+
if (ctx.depth >= MAX_DEPTH) return null;
|
|
425
|
+
if (stmt.type === "ForStatement") return unrollFor(stmt, ctx);
|
|
426
|
+
if (stmt.type === "ForOfStatement") return unrollForOf(stmt, ctx);
|
|
427
|
+
if (stmt.type === "ExpressionStatement" && stmt.expression?.type === "CallExpression") {
|
|
428
|
+
return expandCall(stmt.expression, ctx);
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
function expandStatements(stmts, ctx) {
|
|
433
|
+
const out = [];
|
|
434
|
+
for (const stmt of stmts) {
|
|
435
|
+
const expanded = expandStatement(stmt, ctx);
|
|
436
|
+
if (expanded) out.push(...expanded);
|
|
437
|
+
else out.push(stmt);
|
|
438
|
+
}
|
|
439
|
+
return out;
|
|
440
|
+
}
|
|
441
|
+
function inlineComputedTimelines(ast, timelineVar, resolve) {
|
|
442
|
+
const helpers = collectInlinableHelpers(ast, timelineVar);
|
|
443
|
+
const ctx = {
|
|
444
|
+
helpers,
|
|
445
|
+
timelineVar,
|
|
446
|
+
resolve,
|
|
447
|
+
depth: 0,
|
|
448
|
+
site: { n: 0 },
|
|
449
|
+
order: { n: 0 }
|
|
450
|
+
};
|
|
451
|
+
const body = (ast.body ?? []).filter((stmt) => !isHelperDecl(stmt, helpers));
|
|
452
|
+
ast.body = expandStatements(body, ctx);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/gsapParserAcorn.ts
|
|
456
|
+
var GSAP_METHODS2 = /* @__PURE__ */ new Set(["set", "to", "from", "fromTo"]);
|
|
457
|
+
var QUERY_METHODS = /* @__PURE__ */ new Set(["querySelector", "querySelectorAll"]);
|
|
458
|
+
var ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map"]);
|
|
459
|
+
var SCOPE_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
460
|
+
"Program",
|
|
461
|
+
"FunctionDeclaration",
|
|
462
|
+
"FunctionExpression",
|
|
463
|
+
"ArrowFunctionExpression"
|
|
464
|
+
]);
|
|
465
|
+
function resolveNode(node, scope) {
|
|
466
|
+
if (!node) return void 0;
|
|
467
|
+
if (node.type === "NumericLiteral" || node.type === "Literal" && typeof node.value === "number")
|
|
468
|
+
return node.value;
|
|
469
|
+
if (node.type === "StringLiteral" || node.type === "Literal" && typeof node.value === "string")
|
|
470
|
+
return node.value;
|
|
471
|
+
if (node.type === "BooleanLiteral" || node.type === "Literal" && typeof node.value === "boolean")
|
|
472
|
+
return node.value;
|
|
473
|
+
if (node.type === "UnaryExpression" && node.operator === "-" && node.argument) {
|
|
474
|
+
const val = resolveNode(node.argument, scope);
|
|
475
|
+
return typeof val === "number" ? -val : void 0;
|
|
476
|
+
}
|
|
477
|
+
if (node.type === "BinaryExpression") {
|
|
478
|
+
const left = resolveNode(node.left, scope);
|
|
479
|
+
const right = resolveNode(node.right, scope);
|
|
480
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
481
|
+
switch (node.operator) {
|
|
482
|
+
case "+":
|
|
483
|
+
return left + right;
|
|
484
|
+
case "-":
|
|
485
|
+
return left - right;
|
|
486
|
+
case "*":
|
|
487
|
+
return left * right;
|
|
488
|
+
case "/":
|
|
489
|
+
return right !== 0 ? left / right : void 0;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (typeof left === "string" && node.operator === "+") return left + String(right ?? "");
|
|
493
|
+
if (typeof right === "string" && node.operator === "+") return String(left ?? "") + right;
|
|
494
|
+
}
|
|
495
|
+
if (node.type === "Identifier" && scope.has(node.name)) {
|
|
496
|
+
return scope.get(node.name);
|
|
497
|
+
}
|
|
498
|
+
if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
|
|
499
|
+
return node.quasis?.[0]?.value?.cooked ?? void 0;
|
|
500
|
+
}
|
|
501
|
+
return void 0;
|
|
502
|
+
}
|
|
503
|
+
function extractLiteralValue(node, scope) {
|
|
504
|
+
return resolveNode(node, scope);
|
|
505
|
+
}
|
|
506
|
+
function selectorFromQueryCall(node, scope) {
|
|
507
|
+
if (node?.type !== "CallExpression") return null;
|
|
508
|
+
const callee = node.callee;
|
|
509
|
+
if (callee?.type !== "MemberExpression" || callee.property?.type !== "Identifier") return null;
|
|
510
|
+
const method = callee.property.name;
|
|
511
|
+
const argValue = resolveNode(node.arguments?.[0], scope);
|
|
512
|
+
if (typeof argValue !== "string" || argValue.length === 0) return null;
|
|
513
|
+
if (QUERY_METHODS.has(method) || method === "toArray") return argValue;
|
|
514
|
+
if (method === "getElementById") return `#${argValue}`;
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
function enclosingScopeNodeFromAncestors(ancestors) {
|
|
518
|
+
for (let i = ancestors.length - 2; i >= 0; i--) {
|
|
519
|
+
const node = ancestors[i];
|
|
520
|
+
if (node && SCOPE_NODE_TYPES.has(node.type)) return node;
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
function scopeChainFromAncestors(ancestors) {
|
|
525
|
+
const chain = [];
|
|
526
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
527
|
+
const node = ancestors[i];
|
|
528
|
+
if (node && SCOPE_NODE_TYPES.has(node.type)) chain.push(node);
|
|
529
|
+
}
|
|
530
|
+
return chain;
|
|
531
|
+
}
|
|
532
|
+
function addBinding(bindings, scopeNode, name, selector) {
|
|
533
|
+
let scoped = bindings.get(scopeNode);
|
|
534
|
+
if (!scoped) {
|
|
535
|
+
scoped = /* @__PURE__ */ new Map();
|
|
536
|
+
bindings.set(scopeNode, scoped);
|
|
537
|
+
}
|
|
538
|
+
if (!scoped.has(name)) scoped.set(name, selector);
|
|
539
|
+
}
|
|
540
|
+
function lookupBindingFromAncestors(name, ancestors, bindings) {
|
|
541
|
+
for (const scopeNode of scopeChainFromAncestors(ancestors)) {
|
|
542
|
+
const selector = bindings.get(scopeNode)?.get(name);
|
|
543
|
+
if (selector !== void 0) return selector;
|
|
544
|
+
}
|
|
545
|
+
return bindings.get(null)?.get(name) ?? null;
|
|
546
|
+
}
|
|
547
|
+
function isFunctionNode2(node) {
|
|
548
|
+
return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression" || node?.type === "FunctionDeclaration";
|
|
549
|
+
}
|
|
550
|
+
function resolveCollectionSelector(node, ancestors, scope, bindings) {
|
|
551
|
+
if (node?.type === "Identifier")
|
|
552
|
+
return lookupBindingFromAncestors(node.name, ancestors, bindings);
|
|
553
|
+
if (node?.type === "CallExpression") return selectorFromQueryCall(node, scope);
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
function collectScopeBindings(ast) {
|
|
557
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
558
|
+
acornWalk.simple(ast, {
|
|
559
|
+
VariableDeclarator(node) {
|
|
560
|
+
const name = node.id?.name;
|
|
561
|
+
const init = node.init;
|
|
562
|
+
if (name && init) {
|
|
563
|
+
const val = resolveNode(init, bindings);
|
|
564
|
+
if (val !== void 0) bindings.set(name, val);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
return bindings;
|
|
569
|
+
}
|
|
570
|
+
function collectTargetBindings(ast, scope) {
|
|
571
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
572
|
+
acornWalk.ancestor(ast, {
|
|
573
|
+
VariableDeclarator(node, _, ancestors) {
|
|
574
|
+
const name = node.id?.name;
|
|
575
|
+
const selector = selectorFromQueryCall(node.init, scope);
|
|
576
|
+
if (name && selector !== null) {
|
|
577
|
+
addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), name, selector);
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
AssignmentExpression(node, _, ancestors) {
|
|
581
|
+
const left = node.left;
|
|
582
|
+
const selector = selectorFromQueryCall(node.right, scope);
|
|
583
|
+
if (left?.type === "Identifier" && selector !== null) {
|
|
584
|
+
addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), left.name, selector);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
acornWalk.ancestor(ast, {
|
|
589
|
+
// fallow-ignore-next-line complexity
|
|
590
|
+
CallExpression(node, _, ancestors) {
|
|
591
|
+
const callee = node.callee;
|
|
592
|
+
if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && ITERATION_METHODS.has(callee.property.name)) {
|
|
593
|
+
const collectionSelector = resolveCollectionSelector(
|
|
594
|
+
callee.object,
|
|
595
|
+
ancestors,
|
|
596
|
+
scope,
|
|
597
|
+
bindings
|
|
598
|
+
);
|
|
599
|
+
const fn = node.arguments?.[0];
|
|
600
|
+
const param = fn?.params?.[0];
|
|
601
|
+
if (collectionSelector && param?.type === "Identifier" && isFunctionNode2(fn)) {
|
|
602
|
+
addBinding(bindings, fn, param.name, collectionSelector);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
return bindings;
|
|
608
|
+
}
|
|
609
|
+
function resolveTargetSelector(node, ancestors, scope, bindings) {
|
|
610
|
+
if (!node) return null;
|
|
611
|
+
if (node.type === "StringLiteral" || node.type === "Literal") {
|
|
612
|
+
return typeof node.value === "string" ? node.value : null;
|
|
613
|
+
}
|
|
614
|
+
if (node.type === "Identifier") {
|
|
615
|
+
return lookupBindingFromAncestors(node.name, ancestors, bindings);
|
|
616
|
+
}
|
|
617
|
+
if (node.type === "CallExpression") {
|
|
618
|
+
return selectorFromQueryCall(node, scope);
|
|
619
|
+
}
|
|
620
|
+
if (node.type === "ArrayExpression") {
|
|
621
|
+
const parts = node.elements.map((el) => resolveTargetSelector(el, ancestors, scope, bindings)).filter((s) => typeof s === "string" && s.length > 0);
|
|
622
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
623
|
+
}
|
|
624
|
+
if (node.type === "MemberExpression" && node.object?.type === "Identifier") {
|
|
625
|
+
return lookupBindingFromAncestors(node.object.name, ancestors, bindings);
|
|
626
|
+
}
|
|
627
|
+
return null;
|
|
628
|
+
}
|
|
629
|
+
function isObjectProperty(prop) {
|
|
630
|
+
return prop?.type === "ObjectProperty" || prop?.type === "Property";
|
|
631
|
+
}
|
|
632
|
+
function propKeyName(prop) {
|
|
633
|
+
return prop?.key?.name ?? prop?.key?.value;
|
|
634
|
+
}
|
|
635
|
+
function findPropertyNode(varsArgNode, key) {
|
|
636
|
+
if (varsArgNode?.type !== "ObjectExpression") return void 0;
|
|
637
|
+
for (const prop of varsArgNode.properties ?? []) {
|
|
638
|
+
if (!isObjectProperty(prop)) continue;
|
|
639
|
+
if (propKeyName(prop) === key) return prop.value;
|
|
640
|
+
}
|
|
641
|
+
return void 0;
|
|
642
|
+
}
|
|
643
|
+
function extractRawPropertySource(varsArgNode, key, source) {
|
|
644
|
+
const node = findPropertyNode(varsArgNode, key);
|
|
645
|
+
return node ? source.slice(node.start, node.end) : void 0;
|
|
646
|
+
}
|
|
647
|
+
function objectExpressionToRecord(node, scope, source) {
|
|
648
|
+
const result = {};
|
|
649
|
+
if (node?.type !== "ObjectExpression") return result;
|
|
650
|
+
for (const prop of node.properties ?? []) {
|
|
651
|
+
if (!isObjectProperty(prop)) continue;
|
|
652
|
+
const key = prop.key?.name ?? prop.key?.value;
|
|
653
|
+
if (!key) continue;
|
|
654
|
+
const resolved = resolveNode(prop.value, scope);
|
|
655
|
+
if (resolved !== void 0) {
|
|
656
|
+
result[key] = resolved;
|
|
657
|
+
} else {
|
|
658
|
+
result[key] = `__raw:${source.slice(prop.value.start, prop.value.end)}`;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
function isGsapTimelineCall(node) {
|
|
664
|
+
return node?.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.object?.name === "gsap" && node.callee.property?.name === "timeline";
|
|
665
|
+
}
|
|
666
|
+
function extractTimelineDefaults(callNode, scope) {
|
|
667
|
+
const arg = callNode.arguments?.[0];
|
|
668
|
+
if (!arg || arg.type !== "ObjectExpression") return void 0;
|
|
669
|
+
const defaultsProp = arg.properties?.find(
|
|
670
|
+
(p) => isObjectProperty(p) && propKeyName(p) === "defaults"
|
|
671
|
+
);
|
|
672
|
+
if (!defaultsProp?.value || defaultsProp.value.type !== "ObjectExpression") return void 0;
|
|
673
|
+
const result = {};
|
|
674
|
+
for (const prop of defaultsProp.value.properties ?? []) {
|
|
675
|
+
if (!isObjectProperty(prop)) continue;
|
|
676
|
+
const key = propKeyName(prop);
|
|
677
|
+
const val = resolveNode(prop.value, scope);
|
|
678
|
+
if (key === "ease" && typeof val === "string") result.ease = val;
|
|
679
|
+
if (key === "duration" && typeof val === "number") result.duration = val;
|
|
680
|
+
}
|
|
681
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
682
|
+
}
|
|
683
|
+
function findTimelineVar(ast, scope) {
|
|
684
|
+
let timelineVar = null;
|
|
685
|
+
let timelineCount = 0;
|
|
686
|
+
let defaults;
|
|
687
|
+
const emptyScope = scope ?? /* @__PURE__ */ new Map();
|
|
688
|
+
acornWalk.simple(ast, {
|
|
689
|
+
VariableDeclarator(node) {
|
|
690
|
+
if (isGsapTimelineCall(node.init)) {
|
|
691
|
+
timelineCount += 1;
|
|
692
|
+
if (!timelineVar) {
|
|
693
|
+
timelineVar = node.id?.name ?? null;
|
|
694
|
+
defaults = extractTimelineDefaults(node.init, emptyScope);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
AssignmentExpression(node) {
|
|
699
|
+
if (isGsapTimelineCall(node.right)) {
|
|
700
|
+
timelineCount += 1;
|
|
701
|
+
if (!timelineVar) {
|
|
702
|
+
const left = node.left;
|
|
703
|
+
if (left?.type === "Identifier") timelineVar = left.name;
|
|
704
|
+
defaults = extractTimelineDefaults(node.right, emptyScope);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
return { timelineVar, timelineCount, defaults };
|
|
710
|
+
}
|
|
711
|
+
var BUILTIN_VAR_KEYS = /* @__PURE__ */ new Set(["duration", "ease", "delay"]);
|
|
712
|
+
var DROPPED_VAR_KEYS = /* @__PURE__ */ new Set(["onComplete", "onStart", "onUpdate", "onRepeat"]);
|
|
713
|
+
var EXTRAS_KEYS = /* @__PURE__ */ new Set([
|
|
714
|
+
"stagger",
|
|
715
|
+
"yoyo",
|
|
716
|
+
"repeat",
|
|
717
|
+
"repeatDelay",
|
|
718
|
+
"snap",
|
|
719
|
+
"overwrite",
|
|
720
|
+
"immediateRender"
|
|
721
|
+
]);
|
|
722
|
+
function isTimelineRootedCall(callNode, timelineVar) {
|
|
723
|
+
let obj = callNode.callee?.object;
|
|
724
|
+
while (obj?.type === "CallExpression") {
|
|
725
|
+
obj = obj.callee?.object;
|
|
726
|
+
}
|
|
727
|
+
return obj?.type === "Identifier" && obj.name === timelineVar;
|
|
728
|
+
}
|
|
729
|
+
function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
|
|
730
|
+
const results = [];
|
|
731
|
+
function visit(node, ancestors) {
|
|
732
|
+
if (!node || typeof node !== "object") return;
|
|
733
|
+
const nodeAncestors = [...ancestors, node];
|
|
734
|
+
if (node.type === "CallExpression") {
|
|
735
|
+
const callee = node.callee;
|
|
736
|
+
const gsapSetArg = node.arguments?.[0];
|
|
737
|
+
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");
|
|
738
|
+
if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && (isTimelineRootedCall(node, timelineVar) || isGlobalSet) && GSAP_METHODS2.has(callee.property.name)) {
|
|
739
|
+
const method = callee.property.name;
|
|
740
|
+
const args = node.arguments;
|
|
741
|
+
const selectorValue = args.length >= 1 ? resolveTargetSelector(args[0], nodeAncestors, scope, targetBindings) ?? "__unresolved__" : "__unresolved__";
|
|
742
|
+
if (method === "fromTo" && args.length >= 3) {
|
|
743
|
+
results.push({
|
|
744
|
+
node,
|
|
745
|
+
ancestors: nodeAncestors,
|
|
746
|
+
method: "fromTo",
|
|
747
|
+
selector: selectorValue,
|
|
748
|
+
fromArg: args[1],
|
|
749
|
+
varsArg: args[2],
|
|
750
|
+
positionArg: args[3]
|
|
751
|
+
});
|
|
752
|
+
} else if (method !== "fromTo" && args.length >= 2) {
|
|
753
|
+
results.push({
|
|
754
|
+
node,
|
|
755
|
+
ancestors: nodeAncestors,
|
|
756
|
+
method,
|
|
757
|
+
selector: selectorValue,
|
|
758
|
+
varsArg: args[1],
|
|
759
|
+
positionArg: args[2],
|
|
760
|
+
...isGlobalSet ? { global: true } : {}
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
for (const key of Object.keys(node)) {
|
|
766
|
+
if (key === "type" || key === "start" || key === "end" || key === "loc") continue;
|
|
767
|
+
const child = node[key];
|
|
768
|
+
if (Array.isArray(child)) {
|
|
769
|
+
for (const item of child) {
|
|
770
|
+
if (item && typeof item === "object" && item.type) visit(item, nodeAncestors);
|
|
771
|
+
}
|
|
772
|
+
} else if (child && typeof child === "object" && child.type) {
|
|
773
|
+
visit(child, nodeAncestors);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
visit(ast, []);
|
|
778
|
+
return results;
|
|
779
|
+
}
|
|
780
|
+
var PERCENTAGE_KEY_RE = /^(\d+(?:\.\d+)?)%$/;
|
|
781
|
+
function tryResolveStringProp(propValue, scope) {
|
|
782
|
+
const val = resolveNode(propValue, scope);
|
|
783
|
+
return typeof val === "string" ? val : void 0;
|
|
784
|
+
}
|
|
785
|
+
function parsePercentageKeyframes(node, scope, source) {
|
|
786
|
+
const keyframes = [];
|
|
787
|
+
let ease;
|
|
788
|
+
let easeEach;
|
|
789
|
+
for (const prop of node.properties ?? []) {
|
|
790
|
+
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
791
|
+
const key = prop.key?.value ?? prop.key?.name;
|
|
792
|
+
if (typeof key !== "string") continue;
|
|
793
|
+
const pctMatch = PERCENTAGE_KEY_RE.exec(key);
|
|
794
|
+
if (pctMatch) {
|
|
795
|
+
const percentage = Number.parseFloat(pctMatch[1] ?? "0");
|
|
796
|
+
const record = objectExpressionToRecord(prop.value, scope, source);
|
|
797
|
+
const properties = {};
|
|
798
|
+
let kfEase;
|
|
799
|
+
for (const [k, v] of Object.entries(record)) {
|
|
800
|
+
if (k === "ease" && typeof v === "string") {
|
|
801
|
+
kfEase = v;
|
|
802
|
+
} else if (typeof v === "number" || typeof v === "string") {
|
|
803
|
+
properties[k] = v;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
keyframes.push({ percentage, properties, ...kfEase ? { ease: kfEase } : {} });
|
|
807
|
+
} else if (key === "ease") {
|
|
808
|
+
ease = tryResolveStringProp(prop.value, scope) ?? ease;
|
|
809
|
+
} else if (key === "easeEach") {
|
|
810
|
+
easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
keyframes.sort((a, b) => a.percentage - b.percentage);
|
|
814
|
+
return {
|
|
815
|
+
format: "percentage",
|
|
816
|
+
keyframes,
|
|
817
|
+
...ease ? { ease } : {},
|
|
818
|
+
...easeEach ? { easeEach } : {}
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function computeKeyframesTotalDuration(varsNode, scope, source) {
|
|
822
|
+
const kfNode = (varsNode.properties ?? []).find(
|
|
823
|
+
(p) => (p.key?.name ?? p.key?.value) === "keyframes"
|
|
824
|
+
)?.value;
|
|
825
|
+
if (!kfNode || kfNode.type !== "ArrayExpression") return void 0;
|
|
826
|
+
let total = 0;
|
|
827
|
+
for (const el of kfNode.elements ?? []) {
|
|
828
|
+
if (!el || el.type !== "ObjectExpression") continue;
|
|
829
|
+
const r = objectExpressionToRecord(el, scope, source);
|
|
830
|
+
if (typeof r.duration === "number") total += r.duration;
|
|
831
|
+
}
|
|
832
|
+
return total > 0 ? total : void 0;
|
|
833
|
+
}
|
|
834
|
+
function parseObjectArrayKeyframes(node, scope, source) {
|
|
835
|
+
const elements = node.elements ?? [];
|
|
836
|
+
const raw = [];
|
|
837
|
+
for (const el of elements) {
|
|
838
|
+
if (!el || el.type !== "ObjectExpression") continue;
|
|
839
|
+
const record = objectExpressionToRecord(el, scope, source);
|
|
840
|
+
const properties = {};
|
|
841
|
+
let duration;
|
|
842
|
+
let ease;
|
|
843
|
+
for (const [k, v] of Object.entries(record)) {
|
|
844
|
+
if (k === "duration" && typeof v === "number") {
|
|
845
|
+
duration = v;
|
|
846
|
+
} else if (k === "ease" && typeof v === "string") {
|
|
847
|
+
ease = v;
|
|
848
|
+
} else if (typeof v === "number" || typeof v === "string") {
|
|
849
|
+
properties[k] = v;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
raw.push({ properties, duration, ease });
|
|
853
|
+
}
|
|
854
|
+
const totalDuration = raw.reduce((sum, r) => sum + (r.duration ?? 0), 0);
|
|
855
|
+
const keyframes = [];
|
|
856
|
+
if (totalDuration > 0) {
|
|
857
|
+
let cumulative = 0;
|
|
858
|
+
for (const entry of raw) {
|
|
859
|
+
cumulative += entry.duration ?? 0;
|
|
860
|
+
const percentage = Math.round(cumulative / totalDuration * 100);
|
|
861
|
+
keyframes.push({
|
|
862
|
+
percentage,
|
|
863
|
+
properties: entry.properties,
|
|
864
|
+
...entry.ease ? { ease: entry.ease } : {}
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
} else {
|
|
868
|
+
for (let i = 0; i < raw.length; i++) {
|
|
869
|
+
const entry = raw[i];
|
|
870
|
+
if (!entry) continue;
|
|
871
|
+
const percentage = raw.length > 1 ? Math.round(i / (raw.length - 1) * 100) : 0;
|
|
872
|
+
keyframes.push({
|
|
873
|
+
percentage,
|
|
874
|
+
properties: entry.properties,
|
|
875
|
+
...entry.ease ? { ease: entry.ease } : {}
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return { format: "object-array", keyframes };
|
|
880
|
+
}
|
|
881
|
+
function parseSimpleArrayKeyframes(node, scope) {
|
|
882
|
+
const arrayProps = /* @__PURE__ */ new Map();
|
|
883
|
+
let ease;
|
|
884
|
+
let easeEach;
|
|
885
|
+
for (const prop of node.properties ?? []) {
|
|
886
|
+
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
887
|
+
const key = prop.key?.name ?? prop.key?.value;
|
|
888
|
+
if (typeof key !== "string") continue;
|
|
889
|
+
if (prop.value?.type === "ArrayExpression") {
|
|
890
|
+
const values = [];
|
|
891
|
+
for (const el of prop.value.elements ?? []) {
|
|
892
|
+
const val = resolveNode(el, scope);
|
|
893
|
+
if (typeof val === "number" || typeof val === "string") {
|
|
894
|
+
values.push(val);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (values.length > 0) arrayProps.set(key, values);
|
|
898
|
+
} else if (key === "ease") {
|
|
899
|
+
ease = tryResolveStringProp(prop.value, scope) ?? ease;
|
|
900
|
+
} else if (key === "easeEach") {
|
|
901
|
+
easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
const maxLen = Math.max(...[...arrayProps.values()].map((a) => a.length), 0);
|
|
905
|
+
const keyframes = [];
|
|
906
|
+
for (let i = 0; i < maxLen; i++) {
|
|
907
|
+
const percentage = maxLen > 1 ? Math.round(i / (maxLen - 1) * 100) : 0;
|
|
908
|
+
const properties = {};
|
|
909
|
+
for (const [key, values] of arrayProps) {
|
|
910
|
+
if (i < values.length) properties[key] = values[i];
|
|
911
|
+
}
|
|
912
|
+
keyframes.push({ percentage, properties });
|
|
913
|
+
}
|
|
914
|
+
return {
|
|
915
|
+
format: "simple-array",
|
|
916
|
+
keyframes,
|
|
917
|
+
...ease ? { ease } : {},
|
|
918
|
+
...easeEach ? { easeEach } : {}
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
function parseKeyframesNode(node, scope, source) {
|
|
922
|
+
if (!node) return void 0;
|
|
923
|
+
if (node.type === "ArrayExpression") {
|
|
924
|
+
return parseObjectArrayKeyframes(node, scope, source);
|
|
925
|
+
}
|
|
926
|
+
if (node.type !== "ObjectExpression") return void 0;
|
|
927
|
+
const props = node.properties ?? [];
|
|
928
|
+
let hasPercentageKey = false;
|
|
929
|
+
let hasArrayValue = false;
|
|
930
|
+
for (const prop of props) {
|
|
931
|
+
if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
|
|
932
|
+
const key = prop.key?.value ?? prop.key?.name;
|
|
933
|
+
if (typeof key === "string" && PERCENTAGE_KEY_RE.test(key)) {
|
|
934
|
+
hasPercentageKey = true;
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
if (prop.value?.type === "ArrayExpression") {
|
|
938
|
+
hasArrayValue = true;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (hasPercentageKey) return parsePercentageKeyframes(node, scope, source);
|
|
942
|
+
if (hasArrayValue) return parseSimpleArrayKeyframes(node, scope);
|
|
943
|
+
return void 0;
|
|
944
|
+
}
|
|
945
|
+
function parseMotionPathNode(node, scope, source) {
|
|
946
|
+
if (!node) return void 0;
|
|
947
|
+
let pathNode;
|
|
948
|
+
let autoRotate = false;
|
|
949
|
+
let curviness = 1;
|
|
950
|
+
let isCubic = false;
|
|
951
|
+
if (node.type === "ObjectExpression") {
|
|
952
|
+
for (const prop of node.properties ?? []) {
|
|
953
|
+
if (!isObjectProperty(prop)) continue;
|
|
954
|
+
const key = propKeyName(prop);
|
|
955
|
+
if (key === "path") pathNode = prop.value;
|
|
956
|
+
else if (key === "autoRotate") {
|
|
957
|
+
const val = resolveNode(prop.value, scope);
|
|
958
|
+
autoRotate = typeof val === "number" ? val : val === true;
|
|
959
|
+
} else if (key === "curviness") {
|
|
960
|
+
const val = resolveNode(prop.value, scope);
|
|
961
|
+
if (typeof val === "number") curviness = val;
|
|
962
|
+
} else if (key === "type") {
|
|
963
|
+
const val = resolveNode(prop.value, scope);
|
|
964
|
+
if (val === "cubic") isCubic = true;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
} else if (node.type === "ArrayExpression") {
|
|
968
|
+
pathNode = node;
|
|
969
|
+
}
|
|
970
|
+
if (!pathNode || pathNode.type !== "ArrayExpression") return void 0;
|
|
971
|
+
const elements = pathNode.elements ?? [];
|
|
972
|
+
const coords = [];
|
|
973
|
+
for (const elem of elements) {
|
|
974
|
+
if (!elem || elem.type !== "ObjectExpression") continue;
|
|
975
|
+
const rec = objectExpressionToRecord(elem, scope, source);
|
|
976
|
+
const x = typeof rec.x === "number" ? rec.x : void 0;
|
|
977
|
+
const y = typeof rec.y === "number" ? rec.y : void 0;
|
|
978
|
+
if (x !== void 0 && y !== void 0) coords.push({ x, y });
|
|
979
|
+
}
|
|
980
|
+
return buildArcPath(coords, curviness, autoRotate, isCubic);
|
|
981
|
+
}
|
|
982
|
+
function tweenCallToAnimation(call, scope, source) {
|
|
983
|
+
const vars = objectExpressionToRecord(call.varsArg, scope, source);
|
|
984
|
+
const properties = {};
|
|
985
|
+
const extras = {};
|
|
986
|
+
let keyframesData;
|
|
987
|
+
let hasUnresolvedKeyframes = false;
|
|
988
|
+
let motionPathResult;
|
|
989
|
+
for (const [key, val] of Object.entries(vars)) {
|
|
990
|
+
if (BUILTIN_VAR_KEYS.has(key)) continue;
|
|
991
|
+
if (DROPPED_VAR_KEYS.has(key)) continue;
|
|
992
|
+
if (key === "keyframes") {
|
|
993
|
+
const kfNode = findPropertyNode(call.varsArg, "keyframes");
|
|
994
|
+
keyframesData = parseKeyframesNode(kfNode, scope, source);
|
|
995
|
+
if (!keyframesData && kfNode) hasUnresolvedKeyframes = true;
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
if (key === "motionPath") {
|
|
999
|
+
const mpNode = findPropertyNode(call.varsArg, "motionPath");
|
|
1000
|
+
motionPathResult = parseMotionPathNode(mpNode, scope, source);
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (key === "easeEach") continue;
|
|
1004
|
+
if (EXTRAS_KEYS.has(key)) {
|
|
1005
|
+
const rawSource = extractRawPropertySource(call.varsArg, key, source);
|
|
1006
|
+
if (rawSource !== void 0) {
|
|
1007
|
+
extras[key] = `__raw:${rawSource}`;
|
|
1008
|
+
} else if (val !== void 0) {
|
|
1009
|
+
extras[key] = val;
|
|
1010
|
+
}
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
if (typeof val === "number" || typeof val === "string") {
|
|
1014
|
+
properties[key] = val;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (keyframesData && typeof vars.easeEach === "string") {
|
|
1018
|
+
keyframesData.easeEach = vars.easeEach;
|
|
1019
|
+
}
|
|
1020
|
+
if (motionPathResult) {
|
|
1021
|
+
const { waypoints } = motionPathResult;
|
|
1022
|
+
if (!keyframesData) {
|
|
1023
|
+
const kf = waypoints.map((wp, i) => ({
|
|
1024
|
+
percentage: waypoints.length > 1 ? Math.round(i / (waypoints.length - 1) * 100) : 0,
|
|
1025
|
+
properties: { x: wp.x, y: wp.y }
|
|
1026
|
+
}));
|
|
1027
|
+
keyframesData = { format: "percentage", keyframes: kf };
|
|
1028
|
+
} else {
|
|
1029
|
+
const kfs = keyframesData.keyframes;
|
|
1030
|
+
if (kfs.length === waypoints.length) {
|
|
1031
|
+
for (let i = 0; i < kfs.length; i++) {
|
|
1032
|
+
const kf = kfs[i];
|
|
1033
|
+
const wp = waypoints[i];
|
|
1034
|
+
if (kf && wp) {
|
|
1035
|
+
kf.properties.x = wp.x;
|
|
1036
|
+
kf.properties.y = wp.y;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
let fromProperties;
|
|
1043
|
+
if (call.method === "fromTo" && call.fromArg) {
|
|
1044
|
+
fromProperties = {};
|
|
1045
|
+
const fromVars = objectExpressionToRecord(call.fromArg, scope, source);
|
|
1046
|
+
for (const [key, val] of Object.entries(fromVars)) {
|
|
1047
|
+
if (typeof val === "number" || typeof val === "string") {
|
|
1048
|
+
fromProperties[key] = val;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
const hasPositionArg = !!call.positionArg;
|
|
1053
|
+
const posVal = hasPositionArg ? extractLiteralValue(call.positionArg, scope) : 0;
|
|
1054
|
+
const position = typeof posVal === "number" ? posVal : typeof posVal === "string" ? posVal : 0;
|
|
1055
|
+
let duration = typeof vars.duration === "number" ? vars.duration : void 0;
|
|
1056
|
+
const ease = typeof vars.ease === "string" ? vars.ease : void 0;
|
|
1057
|
+
if (duration === void 0 && keyframesData) {
|
|
1058
|
+
duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
|
|
1059
|
+
}
|
|
1060
|
+
const anim = {
|
|
1061
|
+
targetSelector: call.selector,
|
|
1062
|
+
method: call.method,
|
|
1063
|
+
position,
|
|
1064
|
+
properties,
|
|
1065
|
+
fromProperties,
|
|
1066
|
+
duration,
|
|
1067
|
+
ease
|
|
1068
|
+
};
|
|
1069
|
+
if (!hasPositionArg) anim.implicitPosition = true;
|
|
1070
|
+
let group = classifyTweenPropertyGroup(properties);
|
|
1071
|
+
if (!group && keyframesData) {
|
|
1072
|
+
const kfProps = {};
|
|
1073
|
+
for (const kf of keyframesData.keyframes) {
|
|
1074
|
+
for (const k of Object.keys(kf.properties)) kfProps[k] = true;
|
|
1075
|
+
}
|
|
1076
|
+
group = classifyTweenPropertyGroup(kfProps);
|
|
1077
|
+
}
|
|
1078
|
+
if (group) anim.propertyGroup = group;
|
|
1079
|
+
if (call.global) anim.global = true;
|
|
1080
|
+
if (Object.keys(extras).length > 0) anim.extras = extras;
|
|
1081
|
+
if (keyframesData) anim.keyframes = keyframesData;
|
|
1082
|
+
if (motionPathResult) anim.arcPath = motionPathResult.arcPath;
|
|
1083
|
+
if (hasUnresolvedKeyframes) anim.hasUnresolvedKeyframes = true;
|
|
1084
|
+
if (call.selector === "__unresolved__") anim.hasUnresolvedSelector = true;
|
|
1085
|
+
const provenance = readProvenance(call.node);
|
|
1086
|
+
if (provenance) anim.provenance = provenance;
|
|
1087
|
+
return anim;
|
|
1088
|
+
}
|
|
1089
|
+
var GSAP_DEFAULT_DURATION = 0.5;
|
|
1090
|
+
function resolvePositionString(pos, cursor, prevStart) {
|
|
1091
|
+
const trimmed = pos.trim();
|
|
1092
|
+
if (trimmed === "") return cursor;
|
|
1093
|
+
if (trimmed.startsWith("+=")) {
|
|
1094
|
+
const n2 = Number.parseFloat(trimmed.slice(2));
|
|
1095
|
+
return Number.isFinite(n2) ? cursor + n2 : null;
|
|
1096
|
+
}
|
|
1097
|
+
if (trimmed.startsWith("-=")) {
|
|
1098
|
+
const n2 = Number.parseFloat(trimmed.slice(2));
|
|
1099
|
+
return Number.isFinite(n2) ? cursor - n2 : null;
|
|
1100
|
+
}
|
|
1101
|
+
if (trimmed === "<") return prevStart;
|
|
1102
|
+
if (trimmed === ">") return cursor;
|
|
1103
|
+
if (trimmed.startsWith("<")) {
|
|
1104
|
+
const n2 = Number.parseFloat(trimmed.slice(1));
|
|
1105
|
+
return Number.isFinite(n2) ? prevStart + n2 : null;
|
|
1106
|
+
}
|
|
1107
|
+
if (trimmed.startsWith(">")) {
|
|
1108
|
+
const n2 = Number.parseFloat(trimmed.slice(1));
|
|
1109
|
+
return Number.isFinite(n2) ? cursor + n2 : null;
|
|
1110
|
+
}
|
|
1111
|
+
const n = Number.parseFloat(trimmed);
|
|
1112
|
+
return Number.isFinite(n) ? n : null;
|
|
1113
|
+
}
|
|
1114
|
+
function applyTimelineDefaults(anims, defaults) {
|
|
1115
|
+
if (!defaults) return;
|
|
1116
|
+
for (const anim of anims) {
|
|
1117
|
+
if (anim.method === "set") continue;
|
|
1118
|
+
if (anim.duration === void 0 && defaults.duration !== void 0) {
|
|
1119
|
+
anim.duration = defaults.duration;
|
|
1120
|
+
}
|
|
1121
|
+
if (anim.ease === void 0 && defaults.ease !== void 0) {
|
|
1122
|
+
anim.ease = defaults.ease;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function resolveTimelinePositions(anims) {
|
|
1127
|
+
let cursor = 0;
|
|
1128
|
+
let prevStart = 0;
|
|
1129
|
+
for (const anim of anims) {
|
|
1130
|
+
if (anim.method === "set" && anim.global) {
|
|
1131
|
+
anim.resolvedStart = 0;
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION;
|
|
1135
|
+
let start;
|
|
1136
|
+
if (anim.implicitPosition) {
|
|
1137
|
+
start = cursor;
|
|
1138
|
+
} else if (typeof anim.position === "number") {
|
|
1139
|
+
start = anim.position;
|
|
1140
|
+
} else if (typeof anim.position === "string") {
|
|
1141
|
+
start = resolvePositionString(anim.position, cursor, prevStart);
|
|
1142
|
+
} else {
|
|
1143
|
+
start = cursor;
|
|
1144
|
+
}
|
|
1145
|
+
if (start != null) {
|
|
1146
|
+
anim.resolvedStart = Math.max(0, start);
|
|
1147
|
+
prevStart = anim.resolvedStart;
|
|
1148
|
+
cursor = Math.max(cursor, anim.resolvedStart + duration);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
function compareByLoc(a, b) {
|
|
1153
|
+
const aLoc = a.node.callee?.property?.loc?.start;
|
|
1154
|
+
const bLoc = b.node.callee?.property?.loc?.start;
|
|
1155
|
+
if (!aLoc || !bLoc) return 0;
|
|
1156
|
+
return aLoc.line - bLoc.line || aLoc.column - bLoc.column;
|
|
1157
|
+
}
|
|
1158
|
+
function compareCallOrder(a, b) {
|
|
1159
|
+
const ao = a.node.__hfOrder;
|
|
1160
|
+
const bo = b.node.__hfOrder;
|
|
1161
|
+
if (ao === void 0 && bo === void 0) return compareByLoc(a, b);
|
|
1162
|
+
if (ao === void 0) return -1;
|
|
1163
|
+
if (bo === void 0) return 1;
|
|
1164
|
+
return ao - bo;
|
|
1165
|
+
}
|
|
1166
|
+
function sortBySourcePosition(calls) {
|
|
1167
|
+
calls.sort(compareCallOrder);
|
|
1168
|
+
}
|
|
1169
|
+
function assignStableIds(anims) {
|
|
1170
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1171
|
+
return anims.map((anim) => {
|
|
1172
|
+
const posKey = typeof anim.position === "number" ? String(Math.round(anim.position * 1e3)) : String(anim.position);
|
|
1173
|
+
const groupSuffix = anim.propertyGroup ? `-${anim.propertyGroup}` : "";
|
|
1174
|
+
const base = `${anim.targetSelector}-${anim.method}-${posKey}${groupSuffix}`;
|
|
1175
|
+
const count = (counts.get(base) ?? 0) + 1;
|
|
1176
|
+
counts.set(base, count);
|
|
1177
|
+
const id = count === 1 ? base : `${base}-${count}`;
|
|
1178
|
+
return { ...anim, id };
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
function parseGsapScriptAcornForWrite(script) {
|
|
1182
|
+
try {
|
|
1183
|
+
const ast = acorn.parse(script, {
|
|
1184
|
+
ecmaVersion: "latest",
|
|
1185
|
+
sourceType: "script",
|
|
1186
|
+
locations: true
|
|
1187
|
+
});
|
|
1188
|
+
const scope = collectScopeBindings(ast);
|
|
1189
|
+
const targetBindings = collectTargetBindings(ast, scope);
|
|
1190
|
+
const detection = findTimelineVar(ast, scope);
|
|
1191
|
+
const timelineVar = detection.timelineVar ?? "tl";
|
|
1192
|
+
const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
|
|
1193
|
+
sortBySourcePosition(calls);
|
|
1194
|
+
const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
|
|
1195
|
+
applyTimelineDefaults(rawAnims, detection.defaults);
|
|
1196
|
+
resolveTimelinePositions(rawAnims);
|
|
1197
|
+
const animations = assignStableIds(rawAnims);
|
|
1198
|
+
const located = calls.map((call, i) => ({
|
|
1199
|
+
id: animations[i].id,
|
|
1200
|
+
call,
|
|
1201
|
+
animation: animations[i]
|
|
1202
|
+
}));
|
|
1203
|
+
return { ast, timelineVar, hasTimeline: detection.timelineVar !== null, located };
|
|
1204
|
+
} catch {
|
|
1205
|
+
return null;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
function parseGsapScriptAcorn(script) {
|
|
1209
|
+
try {
|
|
1210
|
+
const ast = acorn.parse(script, {
|
|
1211
|
+
ecmaVersion: "latest",
|
|
1212
|
+
sourceType: "script",
|
|
1213
|
+
locations: true
|
|
1214
|
+
});
|
|
1215
|
+
const scope = collectScopeBindings(ast);
|
|
1216
|
+
const detection = findTimelineVar(ast, scope);
|
|
1217
|
+
const timelineVar = detection.timelineVar ?? "tl";
|
|
1218
|
+
try {
|
|
1219
|
+
inlineComputedTimelines(ast, timelineVar, (node) => resolveNode(node, scope));
|
|
1220
|
+
} catch {
|
|
1221
|
+
}
|
|
1222
|
+
const targetBindings = collectTargetBindings(ast, scope);
|
|
1223
|
+
const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
|
|
1224
|
+
sortBySourcePosition(calls);
|
|
1225
|
+
const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
|
|
1226
|
+
applyTimelineDefaults(rawAnims, detection.defaults);
|
|
1227
|
+
resolveTimelinePositions(rawAnims);
|
|
1228
|
+
const animations = assignStableIds(rawAnims);
|
|
1229
|
+
const timelineMatch = script.match(
|
|
1230
|
+
new RegExp(
|
|
1231
|
+
`^[\\s\\S]*?(?:const|let|var)\\s+${timelineVar}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?`
|
|
1232
|
+
)
|
|
1233
|
+
);
|
|
1234
|
+
const preamble = timelineMatch?.[0] ?? `const ${timelineVar} = gsap.timeline({ paused: true });`;
|
|
1235
|
+
const lastCallIdx = script.lastIndexOf(`${timelineVar}.`);
|
|
1236
|
+
let postamble = "";
|
|
1237
|
+
if (lastCallIdx !== -1) {
|
|
1238
|
+
const afterLast = script.slice(lastCallIdx);
|
|
1239
|
+
const endOfCall = afterLast.indexOf(";");
|
|
1240
|
+
if (endOfCall !== -1) {
|
|
1241
|
+
postamble = script.slice(lastCallIdx + endOfCall + 1).trim();
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
const result = { animations, timelineVar, preamble, postamble };
|
|
1245
|
+
if (detection.timelineCount > 1) result.multipleTimelines = true;
|
|
1246
|
+
if (detection.timelineCount > 0 && detection.timelineVar === null)
|
|
1247
|
+
result.unsupportedTimelinePattern = true;
|
|
1248
|
+
return result;
|
|
1249
|
+
} catch {
|
|
1250
|
+
return { animations: [], timelineVar: "tl", preamble: "", postamble: "" };
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
function extractGsapLabels(script) {
|
|
1254
|
+
try {
|
|
1255
|
+
const ast = acorn.parse(script, {
|
|
1256
|
+
ecmaVersion: "latest",
|
|
1257
|
+
sourceType: "script",
|
|
1258
|
+
locations: true
|
|
1259
|
+
});
|
|
1260
|
+
const scope = collectScopeBindings(ast);
|
|
1261
|
+
const detection = findTimelineVar(ast, scope);
|
|
1262
|
+
const timelineVar = detection.timelineVar ?? "tl";
|
|
1263
|
+
const labels = [];
|
|
1264
|
+
acornWalk.simple(ast, {
|
|
1265
|
+
// fallow-ignore-next-line complexity
|
|
1266
|
+
ExpressionStatement(node) {
|
|
1267
|
+
const expr = node.expression;
|
|
1268
|
+
if (!expr || expr.type !== "CallExpression") return;
|
|
1269
|
+
const callee = expr.callee;
|
|
1270
|
+
if (callee?.type !== "MemberExpression" || callee.object?.name !== timelineVar || callee.property?.name !== "addLabel")
|
|
1271
|
+
return;
|
|
1272
|
+
const args = expr.arguments ?? [];
|
|
1273
|
+
const nameNode = args[0];
|
|
1274
|
+
const posNode = args[1];
|
|
1275
|
+
if (nameNode?.type !== "Literal" || typeof nameNode.value !== "string") return;
|
|
1276
|
+
if (!posNode) return;
|
|
1277
|
+
const pos = resolveNode(posNode, scope);
|
|
1278
|
+
if (typeof pos !== "number" || !Number.isFinite(pos)) return;
|
|
1279
|
+
labels.push({ name: nameNode.value, position: pos });
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
return labels;
|
|
1283
|
+
} catch {
|
|
1284
|
+
return [];
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
export {
|
|
1288
|
+
buildArcPath,
|
|
1289
|
+
editabilityForProvenance,
|
|
1290
|
+
extractGsapLabels,
|
|
1291
|
+
parseGsapScriptAcorn,
|
|
1292
|
+
parseGsapScriptAcornForWrite
|
|
1293
|
+
};
|
|
1294
|
+
//# sourceMappingURL=gsapParserAcorn.js.map
|