@hyperframes/parsers 0.8.50 → 0.8.52
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/dist/assetResolution.d.ts +3 -1
- package/dist/assetResolution.js +24 -2
- package/dist/assetResolution.js.map +1 -1
- package/dist/compositionDuration.d.ts +19 -0
- package/dist/compositionDuration.js +22 -0
- package/dist/compositionDuration.js.map +1 -0
- package/dist/gsapParser.d.ts +2 -2
- package/dist/gsapParser.js.map +1 -1
- package/dist/gsapParserAcorn.d.ts +2 -2
- package/dist/gsapParserAcorn.js +71 -21
- package/dist/gsapParserAcorn.js.map +1 -1
- package/dist/gsapParserExports.d.ts +1 -1
- package/dist/gsapParserExports.js +71 -21
- package/dist/gsapParserExports.js.map +1 -1
- package/dist/{gsapSerialize-QZcXUoVf.d.ts → gsapSerialize-b8lNzSbI.d.ts} +2 -0
- package/dist/gsapWriterAcorn.d.ts +1 -1
- package/dist/gsapWriterAcorn.js +30 -8
- package/dist/gsapWriterAcorn.js.map +1 -1
- package/dist/index.d.ts +37 -7
- package/dist/index.js +402 -40
- package/dist/index.js.map +1 -1
- package/dist/mediaDuration.d.ts +48 -0
- package/dist/mediaDuration.js +104 -0
- package/dist/mediaDuration.js.map +1 -0
- package/dist/mediaDurationFixtures.d.ts +14 -0
- package/dist/mediaDurationFixtures.js +84 -0
- package/dist/mediaDurationFixtures.js.map +1 -0
- package/package.json +14 -2
package/dist/index.js
CHANGED
|
@@ -494,7 +494,16 @@ import * as acorn from "acorn";
|
|
|
494
494
|
import * as acornWalk from "acorn-walk";
|
|
495
495
|
|
|
496
496
|
// src/gsapInline.ts
|
|
497
|
-
var SKIP_KEYS = /* @__PURE__ */ new Set([
|
|
497
|
+
var SKIP_KEYS = /* @__PURE__ */ new Set([
|
|
498
|
+
"type",
|
|
499
|
+
"start",
|
|
500
|
+
"end",
|
|
501
|
+
"loc",
|
|
502
|
+
"range",
|
|
503
|
+
"__hfProvenance",
|
|
504
|
+
"__hfOrder",
|
|
505
|
+
"__hfSiteStart"
|
|
506
|
+
]);
|
|
498
507
|
var FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
499
508
|
"ArrowFunctionExpression",
|
|
500
509
|
"FunctionExpression",
|
|
@@ -597,6 +606,9 @@ function isTimelineRooted(call, timelineVar) {
|
|
|
597
606
|
if (timelineRootName(call) !== timelineVar) return false;
|
|
598
607
|
return call.callee?.property?.type === "Identifier" && GSAP_METHODS.has(call.callee.property.name);
|
|
599
608
|
}
|
|
609
|
+
function isAddLabelCall(call, timelineVar) {
|
|
610
|
+
return timelineRootName(call) === timelineVar && call.callee?.property?.name === "addLabel";
|
|
611
|
+
}
|
|
600
612
|
function containsTimelineCall(node, timelineVar) {
|
|
601
613
|
let found = false;
|
|
602
614
|
walkNodes(node, (n) => {
|
|
@@ -803,12 +815,17 @@ function bodyStatements(node) {
|
|
|
803
815
|
}
|
|
804
816
|
function tagTimelineCalls(stmts, prov, ctx) {
|
|
805
817
|
for (const stmt of stmts) {
|
|
818
|
+
const calls = [];
|
|
806
819
|
walkNodes(stmt, (n) => {
|
|
807
|
-
if (n.type === "CallExpression" && isTimelineRooted(n, ctx.timelineVar))
|
|
808
|
-
|
|
809
|
-
n.__hfOrder = ctx.order.n++;
|
|
810
|
-
}
|
|
820
|
+
if (n.type === "CallExpression" && (isTimelineRooted(n, ctx.timelineVar) || isAddLabelCall(n, ctx.timelineVar)))
|
|
821
|
+
calls.push(n);
|
|
811
822
|
});
|
|
823
|
+
calls.sort((a, b) => a.callee.property.start - b.callee.property.start);
|
|
824
|
+
for (const n of calls) {
|
|
825
|
+
tagProvenance(n, { ...prov });
|
|
826
|
+
n.__hfOrder = ctx.order.n++;
|
|
827
|
+
n.__hfSiteStart = ctx.rootStart;
|
|
828
|
+
}
|
|
812
829
|
}
|
|
813
830
|
}
|
|
814
831
|
function expandBody(bodyStmts, bindings, prov, ctx) {
|
|
@@ -995,10 +1012,14 @@ function inlineComputedTimelines(ast, timelineVar, resolve) {
|
|
|
995
1012
|
resolve,
|
|
996
1013
|
depth: 0,
|
|
997
1014
|
site: { n: 0 },
|
|
998
|
-
order: { n: 0 }
|
|
1015
|
+
order: { n: 0 },
|
|
1016
|
+
rootStart: 0
|
|
999
1017
|
};
|
|
1000
1018
|
const body = (ast.body ?? []).filter((stmt) => !isHelperDecl(stmt, helpers));
|
|
1001
|
-
ast.body =
|
|
1019
|
+
ast.body = body.flatMap((stmt) => {
|
|
1020
|
+
ctx.rootStart = stmt.start;
|
|
1021
|
+
return expandStatements([stmt], ctx);
|
|
1022
|
+
});
|
|
1002
1023
|
}
|
|
1003
1024
|
|
|
1004
1025
|
// src/gsapParserAcorn.ts
|
|
@@ -1416,6 +1437,14 @@ function objectExpressionToRecord(node, scope, source) {
|
|
|
1416
1437
|
function isGsapTimelineCall(node) {
|
|
1417
1438
|
return node?.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.object?.name === "gsap" && node.callee.property?.name === "timeline";
|
|
1418
1439
|
}
|
|
1440
|
+
function hasUnknownDuration(varsNode, scope) {
|
|
1441
|
+
if (!varsNode) return false;
|
|
1442
|
+
if (varsNode.type !== "ObjectExpression") return true;
|
|
1443
|
+
const props = varsNode.properties ?? [];
|
|
1444
|
+
if (props.some((p) => p.type === "SpreadElement")) return true;
|
|
1445
|
+
const prop = props.find((p) => isObjectProperty(p) && propKeyName(p) === "duration");
|
|
1446
|
+
return prop !== void 0 && typeof resolveNode(prop.value, scope) !== "number";
|
|
1447
|
+
}
|
|
1419
1448
|
function staticMemberKey(node) {
|
|
1420
1449
|
if (!node || node.type !== "MemberExpression") return null;
|
|
1421
1450
|
if (node.computed) {
|
|
@@ -1457,7 +1486,10 @@ function extractTimelineDefaults(callNode, scope) {
|
|
|
1457
1486
|
const key = propKeyName(prop);
|
|
1458
1487
|
const val = resolveNode(prop.value, scope);
|
|
1459
1488
|
if (key === "ease" && typeof val === "string") result.ease = val;
|
|
1460
|
-
if (key === "duration"
|
|
1489
|
+
if (key === "duration") {
|
|
1490
|
+
if (typeof val === "number") result.duration = val;
|
|
1491
|
+
else result.durationUnresolved = true;
|
|
1492
|
+
}
|
|
1461
1493
|
}
|
|
1462
1494
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
1463
1495
|
}
|
|
@@ -1832,6 +1864,7 @@ function tweenCallToAnimation(call, scope, source, identifierBindings) {
|
|
|
1832
1864
|
if (duration === void 0 && keyframesData) {
|
|
1833
1865
|
duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
|
|
1834
1866
|
}
|
|
1867
|
+
const durationUnresolved = call.method !== "set" && duration === void 0 && hasUnknownDuration(call.varsArg, scope);
|
|
1835
1868
|
let selector = call.selector;
|
|
1836
1869
|
let targetIdentity;
|
|
1837
1870
|
if (selector === "__unresolved__") {
|
|
@@ -1879,6 +1912,7 @@ function tweenCallToAnimation(call, scope, source, identifierBindings) {
|
|
|
1879
1912
|
if (keyframesData) anim.keyframes = keyframesData;
|
|
1880
1913
|
if (motionPathResult) anim.arcPath = motionPathResult.arcPath;
|
|
1881
1914
|
if (hasUnresolvedKeyframes) anim.hasUnresolvedKeyframes = true;
|
|
1915
|
+
if (durationUnresolved) anim.durationUnresolved = true;
|
|
1882
1916
|
if (selector === "__unresolved__") anim.hasUnresolvedSelector = true;
|
|
1883
1917
|
if (provenance) anim.provenance = provenance;
|
|
1884
1918
|
return anim;
|
|
@@ -2002,8 +2036,9 @@ function applyTimelineDefaults(anims, defaults) {
|
|
|
2002
2036
|
if (!defaults) return;
|
|
2003
2037
|
for (const anim of anims) {
|
|
2004
2038
|
if (anim.method === "set") continue;
|
|
2005
|
-
if (anim.duration === void 0 &&
|
|
2006
|
-
anim.duration = defaults.duration;
|
|
2039
|
+
if (anim.duration === void 0 && !anim.durationUnresolved) {
|
|
2040
|
+
if (defaults.duration !== void 0) anim.duration = defaults.duration;
|
|
2041
|
+
else if (defaults.durationUnresolved) anim.durationUnresolved = true;
|
|
2007
2042
|
}
|
|
2008
2043
|
if (anim.ease === void 0 && defaults.ease !== void 0) {
|
|
2009
2044
|
anim.ease = defaults.ease;
|
|
@@ -2036,12 +2071,14 @@ function resolveAnimStart(anim, cursor, prevStart, labels) {
|
|
|
2036
2071
|
function resolveTimelinePositions(anims, labelDefs = []) {
|
|
2037
2072
|
let cursor = 0;
|
|
2038
2073
|
let prevStart = 0;
|
|
2074
|
+
let cursorKnown = true;
|
|
2039
2075
|
const labels = /* @__PURE__ */ new Map();
|
|
2040
2076
|
let labelIdx = 0;
|
|
2041
2077
|
const sortedLabels = [...labelDefs].sort((a, b) => a.order - b.order);
|
|
2042
2078
|
const defineLabel = (def) => {
|
|
2043
2079
|
let value;
|
|
2044
2080
|
if (typeof def.position === "number") value = def.position;
|
|
2081
|
+
else if (!cursorKnown) return;
|
|
2045
2082
|
else if (typeof def.position === "string") {
|
|
2046
2083
|
value = resolveLabelPosition(def.position, labels, cursor) ?? cursor;
|
|
2047
2084
|
} else value = cursor;
|
|
@@ -2057,7 +2094,9 @@ function resolveTimelinePositions(anims, labelDefs = []) {
|
|
|
2057
2094
|
return;
|
|
2058
2095
|
}
|
|
2059
2096
|
const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION;
|
|
2060
|
-
const
|
|
2097
|
+
const readsCursor = anim.implicitPosition || typeof anim.position !== "number";
|
|
2098
|
+
const start = cursorKnown || !readsCursor ? resolveAnimStart(anim, cursor, prevStart, labels) : null;
|
|
2099
|
+
if (start == null || anim.durationUnresolved) cursorKnown = false;
|
|
2061
2100
|
if (start != null) {
|
|
2062
2101
|
anim.resolvedStart = Math.max(0, start);
|
|
2063
2102
|
prevStart = anim.resolvedStart;
|
|
@@ -2067,7 +2106,15 @@ function resolveTimelinePositions(anims, labelDefs = []) {
|
|
|
2067
2106
|
while (labelIdx < sortedLabels.length) defineLabel(sortedLabels[labelIdx++]);
|
|
2068
2107
|
}
|
|
2069
2108
|
function collectAddLabelDefs(ast, ref, scope, sortedCalls) {
|
|
2070
|
-
const
|
|
2109
|
+
const callKeys = sortedCalls.map((c) => ({
|
|
2110
|
+
site: callSiteStart(c),
|
|
2111
|
+
order: c.node.__hfOrder
|
|
2112
|
+
}));
|
|
2113
|
+
const comesAfter = (site, order, labelSite, labelOrder) => {
|
|
2114
|
+
if (site === void 0) return false;
|
|
2115
|
+
if (site !== labelSite) return site > labelSite;
|
|
2116
|
+
return (order ?? -Infinity) > (labelOrder ?? -Infinity);
|
|
2117
|
+
};
|
|
2071
2118
|
const defs = [];
|
|
2072
2119
|
acornWalk.simple(ast, {
|
|
2073
2120
|
// fallow-ignore-next-line complexity
|
|
@@ -2081,12 +2128,11 @@ function collectAddLabelDefs(ast, ref, scope, sortedCalls) {
|
|
|
2081
2128
|
if (!name) return;
|
|
2082
2129
|
const posVal = resolveNode(node.arguments?.[1], scope);
|
|
2083
2130
|
const position = typeof posVal === "number" || typeof posVal === "string" ? posVal : void 0;
|
|
2084
|
-
const
|
|
2131
|
+
const labelStart = node.__hfSiteStart ?? callee.property?.start;
|
|
2132
|
+
const labelOrder = node.__hfOrder;
|
|
2085
2133
|
let order = sortedCalls.length;
|
|
2086
|
-
if (
|
|
2087
|
-
order =
|
|
2088
|
-
(l) => l && (l.line > labelLoc.line || l.line === labelLoc.line && l.column > labelLoc.column)
|
|
2089
|
-
);
|
|
2134
|
+
if (labelStart !== void 0) {
|
|
2135
|
+
order = callKeys.findIndex((k) => comesAfter(k.site, k.order, labelStart, labelOrder));
|
|
2090
2136
|
if (order === -1) order = sortedCalls.length;
|
|
2091
2137
|
}
|
|
2092
2138
|
defs.push({ name, position, order });
|
|
@@ -2100,13 +2146,17 @@ function compareByLoc(a, b) {
|
|
|
2100
2146
|
if (!aLoc || !bLoc) return 0;
|
|
2101
2147
|
return aLoc.line - bLoc.line || aLoc.column - bLoc.column;
|
|
2102
2148
|
}
|
|
2149
|
+
function callSiteStart(call) {
|
|
2150
|
+
return call.node.__hfSiteStart ?? call.node.callee?.property?.start;
|
|
2151
|
+
}
|
|
2103
2152
|
function compareCallOrder(a, b) {
|
|
2153
|
+
const aSite = callSiteStart(a);
|
|
2154
|
+
const bSite = callSiteStart(b);
|
|
2155
|
+
if (aSite !== void 0 && bSite !== void 0 && aSite !== bSite) return aSite - bSite;
|
|
2104
2156
|
const ao = a.node.__hfOrder;
|
|
2105
2157
|
const bo = b.node.__hfOrder;
|
|
2106
|
-
if (ao
|
|
2107
|
-
|
|
2108
|
-
if (bo === void 0) return 1;
|
|
2109
|
-
return ao - bo;
|
|
2158
|
+
if (ao !== void 0 && bo !== void 0) return ao - bo;
|
|
2159
|
+
return compareByLoc(a, b);
|
|
2110
2160
|
}
|
|
2111
2161
|
function sortBySourcePosition(calls) {
|
|
2112
2162
|
calls.sort(compareCallOrder);
|
|
@@ -3428,6 +3478,7 @@ function checkOutputResolutionCompatibility(input) {
|
|
|
3428
3478
|
|
|
3429
3479
|
// src/gsapUnroll.ts
|
|
3430
3480
|
import * as acorn2 from "acorn";
|
|
3481
|
+
import * as acornWalk3 from "acorn-walk";
|
|
3431
3482
|
import MagicString2 from "magic-string";
|
|
3432
3483
|
function propEntries(props) {
|
|
3433
3484
|
return Object.entries(props).map(([k, v]) => `${safeJsKey(k)}: ${serializeValue(v)}`);
|
|
@@ -3486,37 +3537,140 @@ function enclosingTopLevel(statements, start, end) {
|
|
|
3486
3537
|
}
|
|
3487
3538
|
return null;
|
|
3488
3539
|
}
|
|
3489
|
-
function
|
|
3490
|
-
if (stmt.type === "FunctionDeclaration") return
|
|
3491
|
-
|
|
3492
|
-
|
|
3540
|
+
function declaresFunction(stmt) {
|
|
3541
|
+
if (stmt.type === "FunctionDeclaration") return true;
|
|
3542
|
+
return stmt.type === "VariableDeclaration" && (stmt.declarations ?? []).some((d) => isFunctionNode(d.init));
|
|
3543
|
+
}
|
|
3544
|
+
function declaredNames(stmt) {
|
|
3545
|
+
if (stmt.type === "FunctionDeclaration") return stmt.id?.name ? [stmt.id.name] : [];
|
|
3546
|
+
if (stmt.type !== "VariableDeclaration") return [];
|
|
3547
|
+
return (stmt.declarations ?? []).flatMap((d) => d.id?.name ? [d.id.name] : []);
|
|
3548
|
+
}
|
|
3549
|
+
function mentionsIdentifier(text, name) {
|
|
3550
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3551
|
+
return new RegExp(`(?<![\\p{ID_Continue}$])${escaped}(?![\\p{ID_Continue}$])`, "u").test(text);
|
|
3552
|
+
}
|
|
3553
|
+
function unreferencedHelperDecls(statements, script, unrolled, helperNames) {
|
|
3554
|
+
let live = statements.filter((stmt) => !unrolled.has(stmt));
|
|
3555
|
+
const dead = [];
|
|
3556
|
+
for (let changed = true; changed; ) {
|
|
3557
|
+
changed = false;
|
|
3558
|
+
for (const decl of live) {
|
|
3559
|
+
const names = declaredNames(decl);
|
|
3560
|
+
if (names.length === 0 || !names.every((n) => helperNames.has(n))) continue;
|
|
3561
|
+
const others = live.filter((s) => s !== decl).map((s) => script.slice(s.start, s.end));
|
|
3562
|
+
if (names.some((n) => others.some((text) => mentionsIdentifier(text, n)))) continue;
|
|
3563
|
+
dead.push(decl);
|
|
3564
|
+
live = live.filter((s) => s !== decl);
|
|
3565
|
+
changed = true;
|
|
3566
|
+
}
|
|
3493
3567
|
}
|
|
3494
|
-
return
|
|
3568
|
+
return dead;
|
|
3495
3569
|
}
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3570
|
+
var EFFECT_NODES = /* @__PURE__ */ new Set([
|
|
3571
|
+
"CallExpression",
|
|
3572
|
+
"NewExpression",
|
|
3573
|
+
"UpdateExpression",
|
|
3574
|
+
"AssignmentExpression"
|
|
3575
|
+
]);
|
|
3576
|
+
function containsEffect(node) {
|
|
3577
|
+
let hit = false;
|
|
3578
|
+
acornWalk3.full(node, (n) => {
|
|
3579
|
+
if (EFFECT_NODES.has(n.type)) hit = true;
|
|
3580
|
+
});
|
|
3581
|
+
return hit;
|
|
3582
|
+
}
|
|
3583
|
+
function isPureTweenChain(expr, timelineVar) {
|
|
3584
|
+
let node = expr;
|
|
3585
|
+
while (node?.type === "CallExpression") {
|
|
3586
|
+
if (!isTimelineRooted(node, timelineVar) || node.arguments.some(containsEffect)) return false;
|
|
3587
|
+
node = node.callee?.object;
|
|
3588
|
+
}
|
|
3589
|
+
return node?.type === "Identifier";
|
|
3590
|
+
}
|
|
3591
|
+
var blockStatements = (block) => block?.type === "BlockStatement" ? block.body : block ? [block] : [];
|
|
3592
|
+
function forEachBody(call) {
|
|
3593
|
+
const callback = call.callee?.property?.name === "forEach" ? call.arguments[0] : null;
|
|
3594
|
+
return callback?.body ? blockStatements(callback.body) : null;
|
|
3595
|
+
}
|
|
3596
|
+
function calledBody(call, helpers) {
|
|
3597
|
+
if (call?.type !== "CallExpression") return null;
|
|
3598
|
+
if (call.callee?.type !== "Identifier") return forEachBody(call);
|
|
3599
|
+
return helpers.get(call.callee.name) ?? null;
|
|
3600
|
+
}
|
|
3601
|
+
function bodyOf(stmt, helpers) {
|
|
3602
|
+
if (stmt.type === "ExpressionStatement") return calledBody(stmt.expression, helpers);
|
|
3603
|
+
return stmt.body ? blockStatements(stmt.body) : null;
|
|
3604
|
+
}
|
|
3605
|
+
function onlyAddsTweens(stmts, ctx, seen = /* @__PURE__ */ new Set()) {
|
|
3606
|
+
if (seen.has(stmts)) return false;
|
|
3607
|
+
seen.add(stmts);
|
|
3608
|
+
return stmts.every((stmt) => {
|
|
3609
|
+
if (stmt.type === "VariableDeclaration") return !containsEffect(stmt);
|
|
3610
|
+
if (stmt.type === "ExpressionStatement" && isPureTweenChain(stmt.expression, ctx.timelineVar)) {
|
|
3611
|
+
return true;
|
|
3612
|
+
}
|
|
3613
|
+
const nested = bodyOf(stmt, ctx.helpers);
|
|
3614
|
+
return nested !== null && onlyAddsTweens(nested, ctx, seen);
|
|
3615
|
+
});
|
|
3616
|
+
}
|
|
3617
|
+
function declaredFunctions(stmt) {
|
|
3618
|
+
if (stmt.type === "FunctionDeclaration") {
|
|
3619
|
+
return stmt.id?.name ? [[stmt.id.name, blockStatements(stmt.body)]] : [];
|
|
3620
|
+
}
|
|
3621
|
+
if (stmt.type !== "VariableDeclaration") return [];
|
|
3622
|
+
return stmt.declarations.filter((d) => d.id?.name && isFunctionNode(d.init)).map((d) => [d.id.name, blockStatements(d.init.body)]);
|
|
3623
|
+
}
|
|
3624
|
+
var collectHelperBodies = (statements) => new Map(statements.flatMap(declaredFunctions));
|
|
3625
|
+
function dropStatementsUnsafeToUnroll(byStatement, ctx) {
|
|
3626
|
+
for (const [stmt, anims] of byStatement) {
|
|
3627
|
+
const unknownTiming = anims.some(
|
|
3628
|
+
(a) => a.durationUnresolved || a.resolvedStart === void 0 || a.hasUnresolvedSelector
|
|
3629
|
+
);
|
|
3630
|
+
const bodyStmts = unknownTiming ? null : bodyOf(stmt, ctx.helpers);
|
|
3631
|
+
if (bodyStmts === null || !onlyAddsTweens(bodyStmts, ctx)) byStatement.delete(stmt);
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
function groupByTopLevelStatement(computed, statements) {
|
|
3501
3635
|
const byStatement = /* @__PURE__ */ new Map();
|
|
3502
3636
|
const helperNames = /* @__PURE__ */ new Set();
|
|
3503
3637
|
for (const anim of computed) {
|
|
3504
|
-
if (anim.provenance?.fn) helperNames.add(anim.provenance.fn);
|
|
3505
3638
|
const [s, e] = anim.provenance.sourceRange;
|
|
3506
3639
|
const stmt = enclosingTopLevel(statements, s, e);
|
|
3640
|
+
if (stmt && declaresFunction(stmt)) return null;
|
|
3507
3641
|
if (!stmt) continue;
|
|
3642
|
+
if (anim.provenance?.fn) helperNames.add(anim.provenance.fn);
|
|
3508
3643
|
const list = byStatement.get(stmt) ?? [];
|
|
3509
3644
|
list.push(anim);
|
|
3510
3645
|
byStatement.set(stmt, list);
|
|
3511
3646
|
}
|
|
3647
|
+
return { byStatement, helperNames };
|
|
3648
|
+
}
|
|
3649
|
+
function unrollComputedTimeline(script) {
|
|
3650
|
+
const parsed = parseGsapScriptAcorn(script);
|
|
3651
|
+
const computed = parsed.animations.filter((a) => isComputed(a) && a.provenance?.sourceRange);
|
|
3652
|
+
if (computed.length === 0) return script;
|
|
3653
|
+
const statements = topLevelStatements(script);
|
|
3654
|
+
const grouped = groupByTopLevelStatement(computed, statements);
|
|
3655
|
+
if (!grouped) return script;
|
|
3656
|
+
const { byStatement, helperNames } = grouped;
|
|
3657
|
+
dropStatementsUnsafeToUnroll(byStatement, {
|
|
3658
|
+
timelineVar: parsed.timelineVar,
|
|
3659
|
+
helpers: collectHelperBodies(statements)
|
|
3660
|
+
});
|
|
3512
3661
|
if (byStatement.size === 0) return script;
|
|
3513
3662
|
const ms = new MagicString2(script);
|
|
3514
3663
|
for (const [stmt, anims] of byStatement) {
|
|
3515
3664
|
const literals = anims.map((a) => serializeTweenStatement(parsed.timelineVar, a)).join("\n");
|
|
3516
3665
|
ms.overwrite(stmt.start, stmt.end, literals);
|
|
3517
3666
|
}
|
|
3518
|
-
for (const
|
|
3519
|
-
|
|
3667
|
+
for (const decl of unreferencedHelperDecls(
|
|
3668
|
+
statements,
|
|
3669
|
+
script,
|
|
3670
|
+
new Set(byStatement.keys()),
|
|
3671
|
+
helperNames
|
|
3672
|
+
)) {
|
|
3673
|
+
ms.remove(decl.start, decl.end);
|
|
3520
3674
|
}
|
|
3521
3675
|
return ms.toString();
|
|
3522
3676
|
}
|
|
@@ -3560,6 +3714,201 @@ function trackKindOf(node) {
|
|
|
3560
3714
|
return { kind: "graphics", source: "tag" };
|
|
3561
3715
|
}
|
|
3562
3716
|
|
|
3717
|
+
// src/timingMismatches.ts
|
|
3718
|
+
import { parseHTML as parseHTML2 } from "linkedom";
|
|
3719
|
+
var EPSILON = 0.05;
|
|
3720
|
+
var NON_RENDERED = /* @__PURE__ */ new Set(["script", "style", "template", "noscript", "link", "meta"]);
|
|
3721
|
+
var toNode = (el) => ({
|
|
3722
|
+
tag: el.tagName,
|
|
3723
|
+
attrs: Object.fromEntries(Array.from(el.attributes, (a) => [a.name, a.value])),
|
|
3724
|
+
children: Array.from(el.children, toNode),
|
|
3725
|
+
el
|
|
3726
|
+
});
|
|
3727
|
+
var numeric = (value) => {
|
|
3728
|
+
if (value == null || value.trim() === "") return null;
|
|
3729
|
+
const n = Number(value);
|
|
3730
|
+
return Number.isFinite(n) ? n : null;
|
|
3731
|
+
};
|
|
3732
|
+
function queryAll(root, selector) {
|
|
3733
|
+
try {
|
|
3734
|
+
return Array.from(root.querySelectorAll(selector));
|
|
3735
|
+
} catch {
|
|
3736
|
+
return [];
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
var unresolvedReason = (anim) => {
|
|
3740
|
+
if (anim.durationUnresolved) return "duration";
|
|
3741
|
+
if (anim.resolvedStart === void 0) return "position";
|
|
3742
|
+
return anim.extras?.stagger === void 0 ? null : "stagger";
|
|
3743
|
+
};
|
|
3744
|
+
function rowMembership(root) {
|
|
3745
|
+
const rows = topLevelElements(toNode(root));
|
|
3746
|
+
const rowEls = new Set(rows.map((r) => r.el));
|
|
3747
|
+
const insideRow = /* @__PURE__ */ new Set();
|
|
3748
|
+
for (const row of rows) {
|
|
3749
|
+
insideRow.add(row.el);
|
|
3750
|
+
for (const d of Array.from(row.el.querySelectorAll("*"))) insideRow.add(d);
|
|
3751
|
+
}
|
|
3752
|
+
return { rowEls, insideRow };
|
|
3753
|
+
}
|
|
3754
|
+
var attrNumber = (el, name) => numeric(el.getAttribute(name));
|
|
3755
|
+
function windowFinding(el, selector, anim, start, end) {
|
|
3756
|
+
const dataStart = attrNumber(el, "data-start");
|
|
3757
|
+
const duration = attrNumber(el, "data-duration");
|
|
3758
|
+
if (dataStart === null) return null;
|
|
3759
|
+
const dataEnd = duration === null ? null : dataStart + duration;
|
|
3760
|
+
const beforeStart = anim.method !== "set" && start < dataStart - EPSILON;
|
|
3761
|
+
const afterEnd = dataEnd !== null && start > dataEnd + EPSILON;
|
|
3762
|
+
if (!beforeStart && !afterEnd) return null;
|
|
3763
|
+
const elementId = el.getAttribute("id");
|
|
3764
|
+
return {
|
|
3765
|
+
kind: "tween-outside-window",
|
|
3766
|
+
selector,
|
|
3767
|
+
elementId,
|
|
3768
|
+
dataStart,
|
|
3769
|
+
dataEnd,
|
|
3770
|
+
tweenStart: start,
|
|
3771
|
+
tweenEnd: end
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3774
|
+
var tweenEnd = (anim) => anim.resolvedStart + (anim.method === "set" ? 0 : anim.duration ?? 0.5);
|
|
3775
|
+
function checkTarget(el, selector, anim, layout, report) {
|
|
3776
|
+
if (!layout.insideRow.has(el)) {
|
|
3777
|
+
if (!NON_RENDERED.has(el.tagName.toLowerCase())) {
|
|
3778
|
+
report.findings.push({
|
|
3779
|
+
kind: "animated-without-timing",
|
|
3780
|
+
selector,
|
|
3781
|
+
elementId: el.getAttribute("id")
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
return;
|
|
3785
|
+
}
|
|
3786
|
+
if (!layout.rowEls.has(el) || isSubCompositionHost(toNode(el))) return;
|
|
3787
|
+
if (el.hasAttribute("data-start") && attrNumber(el, "data-start") === null) {
|
|
3788
|
+
report.unresolved.push({ selector, reason: "start-attribute" });
|
|
3789
|
+
return;
|
|
3790
|
+
}
|
|
3791
|
+
const found = windowFinding(el, selector, anim, anim.resolvedStart, tweenEnd(anim));
|
|
3792
|
+
if (found) report.findings.push(found);
|
|
3793
|
+
}
|
|
3794
|
+
function checkTween(anim, root, layout, seenUntimed, report) {
|
|
3795
|
+
const selector = anim.targetSelector;
|
|
3796
|
+
const targets = queryAll(root, selector).filter((el) => el !== root);
|
|
3797
|
+
const reason = unresolvedReason(anim);
|
|
3798
|
+
if (reason) {
|
|
3799
|
+
if (targets.length > 0) report.unresolved.push({ selector, reason });
|
|
3800
|
+
return null;
|
|
3801
|
+
}
|
|
3802
|
+
for (const el of targets) {
|
|
3803
|
+
const loose = !layout.insideRow.has(el);
|
|
3804
|
+
if (loose && seenUntimed.has(el)) continue;
|
|
3805
|
+
if (loose) seenUntimed.add(el);
|
|
3806
|
+
checkTarget(el, selector, anim, layout, report);
|
|
3807
|
+
}
|
|
3808
|
+
return tweenEnd(anim);
|
|
3809
|
+
}
|
|
3810
|
+
function timingMismatches(html) {
|
|
3811
|
+
const report = { findings: [], unresolved: [] };
|
|
3812
|
+
const { document } = parseHTML2(html);
|
|
3813
|
+
const root = document.querySelector("[data-composition-id]");
|
|
3814
|
+
if (!root) return report;
|
|
3815
|
+
const layout = rowMembership(root);
|
|
3816
|
+
const seenUntimed = /* @__PURE__ */ new Set();
|
|
3817
|
+
const scripts = Array.from(document.querySelectorAll("script:not([src])"));
|
|
3818
|
+
const anims = scripts.flatMap((s) => parseGsapScriptAcorn(s.textContent ?? "").animations);
|
|
3819
|
+
const ends = anims.filter((anim) => !anim.global && !anim.hasUnresolvedSelector).map((anim) => checkTween(anim, root, layout, seenUntimed, report));
|
|
3820
|
+
const timelineEnd = Math.max(0, ...ends.filter((e) => e !== null));
|
|
3821
|
+
const rootDuration = attrNumber(root, "data-duration");
|
|
3822
|
+
if (rootDuration !== null && timelineEnd > rootDuration + EPSILON) {
|
|
3823
|
+
report.findings.push({ kind: "timeline-exceeds-root-duration", rootDuration, timelineEnd });
|
|
3824
|
+
}
|
|
3825
|
+
return report;
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3828
|
+
// src/mediaDuration.ts
|
|
3829
|
+
var nonNegative = (raw) => {
|
|
3830
|
+
const value = parseNumeric(raw);
|
|
3831
|
+
return value !== null && value >= 0 ? value : null;
|
|
3832
|
+
};
|
|
3833
|
+
function readMediaOffsetSeconds(getAttr) {
|
|
3834
|
+
return nonNegative(getAttr("data-playback-start")) ?? nonNegative(getAttr("data-media-start")) ?? 0;
|
|
3835
|
+
}
|
|
3836
|
+
var MIN_PLAYBACK_RATE = 0.1;
|
|
3837
|
+
var MAX_PLAYBACK_RATE = 10;
|
|
3838
|
+
function clampPlaybackRate(raw) {
|
|
3839
|
+
return Number.isFinite(raw) && raw > 0 ? Math.max(MIN_PLAYBACK_RATE, Math.min(MAX_PLAYBACK_RATE, raw)) : 1;
|
|
3840
|
+
}
|
|
3841
|
+
function readPlaybackRate(getAttr, fallback = 1) {
|
|
3842
|
+
const authored = Number.parseFloat(getAttr("data-playback-rate") ?? "");
|
|
3843
|
+
return clampPlaybackRate(Number.isFinite(authored) && authored > 0 ? authored : fallback);
|
|
3844
|
+
}
|
|
3845
|
+
function readDataDurationSeconds(getAttr) {
|
|
3846
|
+
const duration = parseNumeric(getAttr("data-duration"));
|
|
3847
|
+
return duration !== null && duration > 0 ? duration : null;
|
|
3848
|
+
}
|
|
3849
|
+
function readAuthoredDurationSeconds(getAttr, startSeconds) {
|
|
3850
|
+
const duration = readDataDurationSeconds(getAttr);
|
|
3851
|
+
if (duration !== null) return duration;
|
|
3852
|
+
const end = parseNumeric(getAttr("data-end"));
|
|
3853
|
+
return end !== null ? end - startSeconds : null;
|
|
3854
|
+
}
|
|
3855
|
+
var DEFAULT_IMAGE_TIMELINE_DURATION_SECONDS = 3;
|
|
3856
|
+
function resolveNaturalDurationSeconds(sourceDurationSeconds, mediaStartSeconds, playbackRate) {
|
|
3857
|
+
if (!Number.isFinite(sourceDurationSeconds)) return null;
|
|
3858
|
+
const remaining = Math.max(0, sourceDurationSeconds - mediaStartSeconds);
|
|
3859
|
+
return remaining / clampPlaybackRate(playbackRate);
|
|
3860
|
+
}
|
|
3861
|
+
function resolveMediaDuration(input) {
|
|
3862
|
+
const { tag, authoredDurationSeconds, sourceDurationSeconds, mediaStartSeconds, playbackRate } = input;
|
|
3863
|
+
if (authoredDurationSeconds !== null && authoredDurationSeconds > 0) {
|
|
3864
|
+
return { seconds: authoredDurationSeconds, source: "authored" };
|
|
3865
|
+
}
|
|
3866
|
+
if (tag === "img") {
|
|
3867
|
+
return { seconds: DEFAULT_IMAGE_TIMELINE_DURATION_SECONDS, source: "default" };
|
|
3868
|
+
}
|
|
3869
|
+
if (sourceDurationSeconds === null) {
|
|
3870
|
+
return { seconds: null, source: "pending", reason: "source duration not yet probed" };
|
|
3871
|
+
}
|
|
3872
|
+
const seconds = resolveNaturalDurationSeconds(
|
|
3873
|
+
sourceDurationSeconds,
|
|
3874
|
+
mediaStartSeconds,
|
|
3875
|
+
playbackRate
|
|
3876
|
+
);
|
|
3877
|
+
if (seconds === null) {
|
|
3878
|
+
return { seconds: null, source: "pending", reason: "source reported a non-finite duration" };
|
|
3879
|
+
}
|
|
3880
|
+
return { seconds, source: "media" };
|
|
3881
|
+
}
|
|
3882
|
+
var PENDING_MEDIA_DURATION_READERS = [
|
|
3883
|
+
"core/src/runtime/init.ts (visibility end, clock, seek, WebAudio scheduling)",
|
|
3884
|
+
"core/src/compiler/timingCompiler.ts compileTag",
|
|
3885
|
+
"core/src/compiler/htmlCompiler.ts compileHtml",
|
|
3886
|
+
"producer/src/services/htmlCompiler.ts resolveMediaDuration",
|
|
3887
|
+
"engine/src/services/videoFrameExtractor.ts (extraction window)",
|
|
3888
|
+
"engine/src/services/audioMixer.ts",
|
|
3889
|
+
"parsers/src/htmlParser.ts (defaults to 5s, reads only data-media-start)",
|
|
3890
|
+
"studio (timelineDOM.ts, timelineElementHelpers.ts, useTimelineSyncCallbacks.ts)"
|
|
3891
|
+
];
|
|
3892
|
+
|
|
3893
|
+
// src/compositionDuration.ts
|
|
3894
|
+
var isPositiveFinite = (value) => value !== null && Number.isFinite(value) && value > 0;
|
|
3895
|
+
function resolveCompositionDuration(input) {
|
|
3896
|
+
const pendingClips = input.clipEndsSeconds.filter((end) => end === null).length;
|
|
3897
|
+
if (isPositiveFinite(input.authoredDurationSeconds)) {
|
|
3898
|
+
return { seconds: input.authoredDurationSeconds, source: "authored", pendingClips };
|
|
3899
|
+
}
|
|
3900
|
+
const known = input.clipEndsSeconds.filter(isPositiveFinite);
|
|
3901
|
+
if (known.length > 0) {
|
|
3902
|
+
return { seconds: Math.max(...known), source: "derived", pendingClips };
|
|
3903
|
+
}
|
|
3904
|
+
return {
|
|
3905
|
+
seconds: null,
|
|
3906
|
+
source: "unresolved",
|
|
3907
|
+
pendingClips,
|
|
3908
|
+
reason: pendingClips > 0 ? "every clip's length is still pending" : "no authored duration and no clip with a length"
|
|
3909
|
+
};
|
|
3910
|
+
}
|
|
3911
|
+
|
|
3563
3912
|
// src/utils/urlPath.ts
|
|
3564
3913
|
function decodeUrlPathVariants(path) {
|
|
3565
3914
|
const variants = [path];
|
|
@@ -3573,7 +3922,7 @@ function decodeUrlPathVariants(path) {
|
|
|
3573
3922
|
|
|
3574
3923
|
// src/variableUsage.ts
|
|
3575
3924
|
import * as acorn3 from "acorn";
|
|
3576
|
-
import * as
|
|
3925
|
+
import * as acornWalk4 from "acorn-walk";
|
|
3577
3926
|
function isGetVariablesCallee(callee) {
|
|
3578
3927
|
if (callee?.type === "Identifier") return callee.name === "getVariables";
|
|
3579
3928
|
if (callee?.type === "MemberExpression" && !callee.computed) {
|
|
@@ -3650,7 +3999,7 @@ function scanVariableUsage(scriptText) {
|
|
|
3650
3999
|
return { usedIds: [], scanIncomplete: true };
|
|
3651
4000
|
}
|
|
3652
4001
|
const aliases = /* @__PURE__ */ new Set();
|
|
3653
|
-
|
|
4002
|
+
acornWalk4.ancestor(ast, {
|
|
3654
4003
|
CallExpression(node, _, ancestors) {
|
|
3655
4004
|
if (!isGetVariablesCallee(node.callee)) return;
|
|
3656
4005
|
const parent = ancestors[ancestors.length - 2];
|
|
@@ -3659,7 +4008,7 @@ function scanVariableUsage(scriptText) {
|
|
|
3659
4008
|
}
|
|
3660
4009
|
});
|
|
3661
4010
|
if (aliases.size > 0) {
|
|
3662
|
-
|
|
4011
|
+
acornWalk4.ancestor(ast, {
|
|
3663
4012
|
// fallow-ignore-next-line complexity
|
|
3664
4013
|
Identifier(node, _, ancestors) {
|
|
3665
4014
|
if (!aliases.has(String(node.name))) return;
|
|
@@ -3677,7 +4026,7 @@ function scanVariableUsage(scriptText) {
|
|
|
3677
4026
|
|
|
3678
4027
|
// src/mediaSrcMutation.ts
|
|
3679
4028
|
import * as acorn4 from "acorn";
|
|
3680
|
-
import * as
|
|
4029
|
+
import * as acornWalk5 from "acorn-walk";
|
|
3681
4030
|
function parseProgram2(script) {
|
|
3682
4031
|
try {
|
|
3683
4032
|
return acorn4.parse(script, { ecmaVersion: "latest", sourceType: "script" });
|
|
@@ -3712,7 +4061,7 @@ function extractMediaSrcMutations(script) {
|
|
|
3712
4061
|
try {
|
|
3713
4062
|
const ast = parseProgram2(script);
|
|
3714
4063
|
const bindings = /* @__PURE__ */ new Map();
|
|
3715
|
-
|
|
4064
|
+
acornWalk5.simple(ast, {
|
|
3716
4065
|
VariableDeclarator(node) {
|
|
3717
4066
|
if (node.id?.type !== "Identifier") return;
|
|
3718
4067
|
const selector = lookupSelector(node.init, bindings);
|
|
@@ -3720,7 +4069,7 @@ function extractMediaSrcMutations(script) {
|
|
|
3720
4069
|
}
|
|
3721
4070
|
});
|
|
3722
4071
|
const mutations = [];
|
|
3723
|
-
|
|
4072
|
+
acornWalk5.simple(ast, {
|
|
3724
4073
|
AssignmentExpression(node) {
|
|
3725
4074
|
if (node.operator !== "=" || memberName(node.left) !== "src") return;
|
|
3726
4075
|
const selector = lookupSelector(node.left.object, bindings);
|
|
@@ -3864,6 +4213,7 @@ export {
|
|
|
3864
4213
|
ClipTimingWriteError,
|
|
3865
4214
|
CompositionHtmlParseError,
|
|
3866
4215
|
DEFAULT_DURATIONS,
|
|
4216
|
+
DEFAULT_IMAGE_TIMELINE_DURATION_SECONDS,
|
|
3867
4217
|
DERIVED_TIMING_ATTRIBUTES,
|
|
3868
4218
|
EXCLUDED_TAGS,
|
|
3869
4219
|
FONT_ALIAS_KEYS,
|
|
@@ -3871,6 +4221,9 @@ export {
|
|
|
3871
4221
|
HTML_BODY_CSS_HEIGHT_FIRST_RE,
|
|
3872
4222
|
HTML_BODY_CSS_WIDTH_FIRST_RE,
|
|
3873
4223
|
LEGACY_TIMING_ATTRIBUTES,
|
|
4224
|
+
MAX_PLAYBACK_RATE,
|
|
4225
|
+
MIN_PLAYBACK_RATE,
|
|
4226
|
+
PENDING_MEDIA_DURATION_READERS,
|
|
3874
4227
|
PROPERTY_GROUPS,
|
|
3875
4228
|
SPRING_PRESETS,
|
|
3876
4229
|
SUPPORTED_EASES,
|
|
@@ -3881,6 +4234,7 @@ export {
|
|
|
3881
4234
|
addElementToHtml,
|
|
3882
4235
|
checkOutputResolutionCompatibility,
|
|
3883
4236
|
checkSubCompositionUsability,
|
|
4237
|
+
clampPlaybackRate,
|
|
3884
4238
|
classifyPropertyGroup,
|
|
3885
4239
|
classifyTweenPropertyGroup,
|
|
3886
4240
|
decodeUrlPathVariants,
|
|
@@ -3909,14 +4263,22 @@ export {
|
|
|
3909
4263
|
parseNumeric,
|
|
3910
4264
|
parseStartExpression,
|
|
3911
4265
|
queryByAttr,
|
|
4266
|
+
readAuthoredDurationSeconds,
|
|
3912
4267
|
readClipTiming,
|
|
4268
|
+
readDataDurationSeconds,
|
|
4269
|
+
readMediaOffsetSeconds,
|
|
4270
|
+
readPlaybackRate,
|
|
3913
4271
|
removeElementFromHtml,
|
|
3914
4272
|
removeElementWithGsapCascade,
|
|
3915
4273
|
resolveAliasDisplayName,
|
|
4274
|
+
resolveCompositionDuration,
|
|
4275
|
+
resolveMediaDuration,
|
|
4276
|
+
resolveNaturalDurationSeconds,
|
|
3916
4277
|
resolveResolutionFlagPair,
|
|
3917
4278
|
scanVariableUsage,
|
|
3918
4279
|
serializeGsapAnimations,
|
|
3919
4280
|
suggestMatchingPreset,
|
|
4281
|
+
timingMismatches,
|
|
3920
4282
|
topLevelElements,
|
|
3921
4283
|
trackKindOf,
|
|
3922
4284
|
unrollComputedTimeline,
|