( script: string, animationId: string, percentage: number, properties: Record<string, number | string>, ease?: string, backfillDefaults?: Record<string, number | string>, )
| 2136 | * object. If the percentage already exists, its value is replaced. |
| 2137 | */ |
| 2138 | export function addKeyframeToScript( |
| 2139 | script: string, |
| 2140 | animationId: string, |
| 2141 | percentage: number, |
| 2142 | properties: Record<string, number | string>, |
| 2143 | ease?: string, |
| 2144 | backfillDefaults?: Record<string, number | string>, |
| 2145 | ): string { |
| 2146 | let loc = locateAnimationWithFallback(script, animationId); |
| 2147 | if (!loc) return script; |
| 2148 | let kfNode = findKeyframesObjectNode(loc.target.call.varsArg); |
| 2149 | |
| 2150 | // Array-form keyframes can't host an arbitrary new percentage — normalize to |
| 2151 | // object form in place first. (convertToKeyframesInScript below only converts |
| 2152 | // FLAT tweens; it early-returns when keyframes already exist.) |
| 2153 | if (!kfNode) kfNode = convertArrayKeyframesToObjectNode(loc.target.call.varsArg); |
| 2154 | |
| 2155 | if (!kfNode) { |
| 2156 | script = convertToKeyframesInScript(script, animationId); |
| 2157 | loc = locateAnimationWithFallback(script, animationId); |
| 2158 | if (!loc) return script; |
| 2159 | kfNode = findKeyframesObjectNode(loc.target.call.varsArg); |
| 2160 | if (!kfNode) return script; |
| 2161 | } |
| 2162 | const pctKey = `${percentage}%`; |
| 2163 | |
| 2164 | const newValueNode = buildKeyframeValueNode(properties, ease); |
| 2165 | |
| 2166 | // Merge into existing keyframe at this percentage, or insert new |
| 2167 | const existing = findKeyframePropByPct(kfNode, percentage); |
| 2168 | if (existing) { |
| 2169 | if (existing.prop.value?.type === "ObjectExpression") { |
| 2170 | const existingRecord = objectExpressionToRecord(existing.prop.value, loc.parsed.scope); |
| 2171 | const merged = { ...existingRecord }; |
| 2172 | for (const [k, v] of Object.entries(properties)) merged[k] = v; |
| 2173 | existing.prop.value = buildKeyframeValueNode( |
| 2174 | merged as Record<string, number | string>, |
| 2175 | ease ?? (typeof existingRecord.ease === "string" ? existingRecord.ease : undefined), |
| 2176 | ); |
| 2177 | } else { |
| 2178 | existing.prop.value = newValueNode; |
| 2179 | } |
| 2180 | } else { |
| 2181 | // Build the new property node with a quoted percentage key |
| 2182 | const newProp = parseExpr(`{ ${JSON.stringify(pctKey)}: {} }`).properties[0]; |
| 2183 | newProp.value = newValueNode; |
| 2184 | |
| 2185 | // Insert in sorted order by percentage |
| 2186 | let insertIdx = kfNode.properties.length; |
| 2187 | for (let i = 0; i < kfNode.properties.length; i++) { |
| 2188 | const key = isObjectProperty(kfNode.properties[i]) |
| 2189 | ? propKeyName(kfNode.properties[i]) |
| 2190 | : undefined; |
| 2191 | if (typeof key === "string" && percentageFromKey(key) > percentage) { |
| 2192 | insertIdx = i; |
| 2193 | break; |
| 2194 | } |
| 2195 | } |
no test coverage detected