( script: string, animationId: string, percentage: number, properties: Record<string, number | string>, ease?: string, backfillDefaults?: Record<string, number | string>, )
| 1005 | } |
| 1006 | |
| 1007 | export function addKeyframeToScript( |
| 1008 | script: string, |
| 1009 | animationId: string, |
| 1010 | percentage: number, |
| 1011 | properties: Record<string, number | string>, |
| 1012 | ease?: string, |
| 1013 | backfillDefaults?: Record<string, number | string>, |
| 1014 | ): string { |
| 1015 | const located = ensureKeyframesNode(script, animationId); |
| 1016 | if (!located) return script; |
| 1017 | const { script: src, kfNode } = located; |
| 1018 | |
| 1019 | const existing = findKfPropByPct(kfNode, percentage); |
| 1020 | |
| 1021 | // Final record for the target keyframe (merge if it already exists). |
| 1022 | const targetRecord = buildTargetRecord(existing, src, properties, ease); |
| 1023 | // `_auto` endpoint syncs fire only on new inserts; a merge landing ON an |
| 1024 | // endpoint already preserves `_auto` via buildTargetRecord. |
| 1025 | const endpointOverwrites = existing |
| 1026 | ? new Map<any, Record<string, number | string>>() |
| 1027 | : autoEndpointOverwrites(kfNode, src, percentage, properties); |
| 1028 | // Backfilled siblings (each node changes at most once). |
| 1029 | const backfillOverwrites = collectBackfillOverwrites(kfNode, src, properties, backfillDefaults, { |
| 1030 | existingProp: existing?.prop, |
| 1031 | endpoints: endpointOverwrites, |
| 1032 | }); |
| 1033 | |
| 1034 | // Emit exactly one overwrite per changed node, plus one insert for a new key. |
| 1035 | const ms = new MagicString(src); |
| 1036 | if (existing) { |
| 1037 | // Merge into the existing keyframe at this percentage, preserving sibling |
| 1038 | // properties — overwrite only the given keys. (A whole-value overwrite here |
| 1039 | // would silently drop other properties already keyframed at this percent.) |
| 1040 | if (existing.prop.value?.type === "ObjectExpression") { |
| 1041 | for (const [k, v] of Object.entries(properties)) { |
| 1042 | upsertProp(ms, existing.prop.value, k, v); |
| 1043 | } |
| 1044 | if (ease !== undefined) upsertProp(ms, existing.prop.value, "ease", ease); |
| 1045 | } else { |
| 1046 | ms.overwrite(existing.prop.value.start, existing.prop.value.end, recordToCode(targetRecord)); |
| 1047 | } |
| 1048 | } else { |
| 1049 | insertNewKeyframe(ms, kfNode, percentage, `${percentage}%`, recordToCode(targetRecord)); |
| 1050 | } |
| 1051 | for (const [prop, rec] of [...endpointOverwrites, ...backfillOverwrites]) { |
| 1052 | ms.overwrite(prop.value.start, prop.value.end, recordToCode(rec)); |
| 1053 | } |
| 1054 | |
| 1055 | return ms.toString(); |
| 1056 | } |
| 1057 | |
| 1058 | /** Insert a brand-new `"pct%": {...}` property in sorted order. */ |
| 1059 | function insertNewKeyframe( |
nothing calls this directly
no test coverage detected