Skip to content

Commit 0dc88f2

Browse files
authored
chore: More detailed invalid CSS keyframes object error message (#8618)
## Summary This PR adds more details to the error thrown when CSS animation keyframes passed from JS aren't valid. It adds the property path to the error message, as well as the value that is not valid. This should make debugging simpler if problems like this occur in the future. ## Examples | Before | After | |-|-| | <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 Pro - 2025-11-18 at 16 38 50" src="https://github.com/user-attachments/assets/c84c09ba-e68b-44d0-afbc-419884fe637f" /> | <img width="1206" height="2622" alt="Simulator Screenshot - iPhone 17 Pro - 2025-11-18 at 16 46 57" src="https://github.com/user-attachments/assets/cfca507f-6792-4f55-9468-e5be0658cd4b" /> | I have to check what happened to the `transformOrigin` property and why it crashes now 😅
1 parent 2e1c71c commit 0dc88f2

File tree

9 files changed

+66
-68
lines changed

9 files changed

+66
-68
lines changed

packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/values/CSSValueVariant.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include <reanimated/CSS/svg/values/SVGLength.h>
1212
#include <reanimated/CSS/svg/values/SVGStrokeDashArray.h>
1313

14+
#include <worklets/Tools/JSISerializer.h>
15+
1416
#include <string>
1517
#include <utility>
1618
#include <vector>
@@ -37,7 +39,7 @@ CSSValueVariant<AllowedTypes...>::CSSValueVariant(jsi::Runtime &rt, const jsi::V
3739
// Try constructing with each allowed type until one succeeds
3840
if (!(tryOne.template operator()<AllowedTypes>() || ...)) {
3941
throw std::runtime_error(
40-
"[Reanimated] No compatible type found for construction from: " + stringifyJSIValue(rt, jsiValue));
42+
"[Reanimated] No compatible type found for construction from: " + worklets::stringifyJSIValue(rt, jsiValue));
4143
}
4244
}
4345

packages/react-native-reanimated/Common/cpp/reanimated/CSS/common/values/CSSValueVariant.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#pragma once
22

33
#include <reanimated/CSS/common/values/CSSValue.h>
4-
#include <worklets/Tools/JSISerializer.h>
54

65
#include <folly/json.h>
76

@@ -13,8 +12,6 @@
1312

1413
namespace reanimated::css {
1514

16-
using namespace worklets;
17-
1815
/**
1916
* Macro to check if two lambda parameters have the same reference-removed type.
2017
*

packages/react-native-reanimated/Common/cpp/reanimated/CSS/interpolation/PropertyInterpolator.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
22

3+
#include <worklets/Tools/JSISerializer.h>
4+
35
#include <memory>
46
#include <utility>
57

@@ -14,4 +16,61 @@ bool PropertyInterpolatorFactory::isDiscreteProperty() const {
1416
return false;
1517
}
1618

19+
std::string PropertyInterpolator::getPropertyPathString() const {
20+
if (propertyPath_.empty()) {
21+
return "";
22+
}
23+
24+
std::string result = propertyPath_[0];
25+
for (size_t i = 1; i < propertyPath_.size(); ++i) {
26+
result += "." + propertyPath_[i];
27+
}
28+
return result;
29+
}
30+
31+
std::vector<std::pair<double, jsi::Value>> PropertyInterpolator::parseJSIKeyframes(
32+
jsi::Runtime &rt,
33+
const jsi::Value &keyframes) const {
34+
if (!keyframes.isObject() || !keyframes.asObject(rt).isArray(rt)) {
35+
throw std::invalid_argument(
36+
"[Reanimated] Received invalid keyframes object for property: " + getPropertyPathString() +
37+
".\n\nExpected an array of objects with 'offset' and 'value' properties, got: " +
38+
worklets::stringifyJSIValue(rt, keyframes));
39+
}
40+
41+
const auto keyframeArray = keyframes.asObject(rt).asArray(rt);
42+
const auto keyframesCount = keyframeArray.size(rt);
43+
44+
const auto getKeyframeAtIndexOffset = [&](size_t index) -> double {
45+
return keyframeArray.getValueAtIndex(rt, index).asObject(rt).getProperty(rt, "offset").asNumber();
46+
};
47+
48+
const bool hasOffset0 = getKeyframeAtIndexOffset(0) == 0;
49+
const bool hasOffset1 = getKeyframeAtIndexOffset(keyframesCount - 1) == 1;
50+
51+
std::vector<std::pair<double, jsi::Value>> result;
52+
result.reserve(keyframesCount + (hasOffset0 ? 0 : 1) + (hasOffset1 ? 0 : 1));
53+
54+
// Insert the keyframe without value at offset 0 if it is not present
55+
if (!hasOffset0) {
56+
result.emplace_back(0.0, jsi::Value::undefined());
57+
}
58+
59+
// Insert all provided keyframes
60+
for (size_t i = 0; i < keyframesCount; ++i) {
61+
jsi::Object keyframeObject = keyframeArray.getValueAtIndex(rt, i).asObject(rt);
62+
double offset = keyframeObject.getProperty(rt, "offset").asNumber();
63+
jsi::Value value = keyframeObject.getProperty(rt, "value");
64+
65+
result.emplace_back(offset, std::move(value));
66+
}
67+
68+
// Insert the keyframe without value at offset 1 if it is not present
69+
if (!hasOffset1) {
70+
result.emplace_back(1.0, jsi::Value::undefined());
71+
}
72+
73+
return result;
74+
}
75+
1776
} // namespace reanimated::css

packages/react-native-reanimated/Common/cpp/reanimated/CSS/interpolation/PropertyInterpolator.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <memory>
99
#include <string>
1010
#include <unordered_map>
11+
#include <utility>
1112
#include <vector>
1213

1314
namespace reanimated::css {
@@ -38,6 +39,9 @@ class PropertyInterpolator {
3839
protected:
3940
const PropertyPath propertyPath_;
4041
const std::shared_ptr<ViewStylesRepository> viewStylesRepository_;
42+
43+
std::string getPropertyPathString() const;
44+
std::vector<std::pair<double, jsi::Value>> parseJSIKeyframes(jsi::Runtime &rt, const jsi::Value &keyframes) const;
4145
};
4246

4347
class PropertyInterpolatorFactory {

packages/react-native-reanimated/Common/cpp/reanimated/CSS/interpolation/filters/FilterStyleInterpolator.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
44
#include <reanimated/CSS/interpolation/filters/FilterOperationInterpolator.h>
5-
#include <reanimated/CSS/utils/keyframes.h>
65

76
#include <memory>
87
#include <tuple>

packages/react-native-reanimated/Common/cpp/reanimated/CSS/interpolation/transforms/TransformsStyleInterpolator.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
44
#include <reanimated/CSS/interpolation/transforms/TransformOperationInterpolator.h>
55
#include <reanimated/CSS/interpolation/transforms/operations/matrix.h>
6-
#include <reanimated/CSS/utils/keyframes.h>
76

87
#include <memory>
98
#include <unordered_map>

packages/react-native-reanimated/Common/cpp/reanimated/CSS/interpolation/values/ValueInterpolator.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include <reanimated/CSS/common/values/CSSValueVariant.h>
44
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
5-
#include <reanimated/CSS/utils/keyframes.h>
65

76
#include <memory>
87
#include <optional>

packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/keyframes.cpp

Lines changed: 0 additions & 48 deletions
This file was deleted.

packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/keyframes.h

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)