Skip to content

Commit

Permalink
feat: Use new logger and ReanimatedError for lib warnings and errors (#…
Browse files Browse the repository at this point in the history
…6387)

## Summary

This PR replaces all `console.warn`, `console.error` and `throw new
Error` statements with custom logger calls (`logger.warn`,
`logger.error` and `logger.newError` respectively).

It declares the logger object as a global variable for easier usage and
to prevent circular imports (as it imports `runOnJS`, which was the
cause of circular dependencies).

## Test plan

- open the `EmptyExample` in the example app,
- change the `SHOW_EXAMPLE` constant to see the respective
error/warning,
- see better error logs with code frame that was the culprit (if
possible - sometimes we cannot get pretty error stack trace, especially
when functions are called asynchronously)

---------

Co-authored-by: Tomasz Żelawski <[email protected]>
  • Loading branch information
MatiPl01 and tjzel committed Aug 28, 2024
1 parent adede31 commit 96b9ed4
Show file tree
Hide file tree
Showing 46 changed files with 260 additions and 243 deletions.
2 changes: 1 addition & 1 deletion apps/next-example/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
alias: {
'react-native': '../../node_modules/react-native-web',
// Uncomment this if you want fast-refresh to work with reanimated:
// 'react-native-reanimated': '../src/index',
// 'react-native-reanimated': '../../packages/react-native-reanimated/src',
},
},
],
Expand Down
5 changes: 4 additions & 1 deletion packages/react-native-reanimated/src/Bezier.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
'use strict';

import { ReanimatedError } from './errors';

/**
* https://github.com/gre/bezier-easing
* BezierEasing - use bezier curve for transition easing function
Expand Down Expand Up @@ -98,7 +101,7 @@ export function Bezier(
}

if (!(mX1 >= 0 && mX1 <= 1 && mX2 >= 0 && mX2 <= 1)) {
throw new Error('[Reanimated] Bezier x values must be in [0, 1] range.');
throw new ReanimatedError('Bezier x values must be in [0, 1] range.');
}

if (mX1 === mY1 && mX2 === mY2) {
Expand Down
6 changes: 4 additions & 2 deletions packages/react-native-reanimated/src/ConfigHelper.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
'use strict';
import { PropsAllowlists } from './propsAllowlists';
import { jsiConfigureProps } from './core';
import { ReanimatedError } from './errors';

function assertNoOverlapInLists() {
for (const key in PropsAllowlists.NATIVE_THREAD_PROPS_WHITELIST) {
if (key in PropsAllowlists.UI_THREAD_PROPS_WHITELIST) {
throw new Error(
`[Reanimated] Property \`${key}\` was whitelisted both as UI and native prop. Please remove it from one of the lists.`
throw new ReanimatedError(
`Property \`${key}\` was whitelisted both as UI and native prop. Please remove it from one of the lists.`
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type React from 'react';
import { getShadowNodeWrapperFromRef } from '../fabricUtils';
import type { LayoutAnimationBatchItem } from '../layoutReanimation/animationBuilder/commonTypes';
import ReanimatedModule from '../specs/NativeReanimatedModule';
import { ReanimatedError } from '../errors';

// this is the type of `__reanimatedModuleProxy` which is injected using JSI
export interface NativeReanimatedModule {
Expand Down Expand Up @@ -68,8 +69,8 @@ function assertSingleReanimatedInstance() {
global._REANIMATED_VERSION_JS !== undefined &&
global._REANIMATED_VERSION_JS !== jsVersion
) {
throw new Error(
`[Reanimated] Another instance of Reanimated was detected.
throw new ReanimatedError(
`Another instance of Reanimated was detected.
See \`https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#another-instance-of-reanimated-was-detected\` for more details. Previous: ${global._REANIMATED_VERSION_JS}, current: ${jsVersion}.`
);
}
Expand All @@ -89,8 +90,8 @@ export class NativeReanimated {
ReanimatedModule?.installTurboModule(valueUnpackerCode);
}
if (global.__reanimatedModuleProxy === undefined) {
throw new Error(
`[Reanimated] Native part of Reanimated doesn't seem to be initialized.
throw new ReanimatedError(
`Native part of Reanimated doesn't seem to be initialized.
See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#native-part-of-reanimated-doesnt-seem-to-be-initialized for more details.`
);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/react-native-reanimated/src/PropsRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
import { ReanimatedError } from './errors';
import { isFabric } from './PlatformChecker';
import { runOnUI } from './threads';

Expand All @@ -13,7 +14,7 @@ export function removeFromPropsRegistry(viewTag: number) {

function flush() {
if (__DEV__ && !isFabric()) {
throw new Error('[Reanimated] PropsRegistry is only available on Fabric.');
throw new ReanimatedError('PropsRegistry is only available on Fabric.');
}
runOnUI(removeFromPropsRegistryOnUI)(VIEW_TAGS);
VIEW_TAGS = [];
Expand Down
5 changes: 3 additions & 2 deletions packages/react-native-reanimated/src/UpdateProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { ReanimatedHTMLElement } from './js-reanimated';
import { _updatePropsJS } from './js-reanimated';
import { isFabric, isJest, shouldBeUseWeb } from './PlatformChecker';
import { runOnUIImmediately } from './threads';
import { ReanimatedError } from './errors';

let updateProps: (
viewDescriptor: SharedValue<Descriptor[]>,
Expand Down Expand Up @@ -120,8 +121,8 @@ if (shouldBeUseWeb()) {
// Jest attempts to access a property of this object to check if it is a Jest mock
// so we can't throw an error in the getter.
if (!isJest()) {
throw new Error(
'[Reanimated] `UpdatePropsManager` is not available on non-native platform.'
throw new ReanimatedError(
'`UpdatePropsManager` is not available on non-native platform.'
);
}
};
Expand Down
9 changes: 5 additions & 4 deletions packages/react-native-reanimated/src/animation/clamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
ReduceMotion,
} from '../commonTypes';
import type { ClampAnimation } from './commonTypes';
import { logger } from '../logger';

type withClampType = <T extends number | string>(
config: {
Expand Down Expand Up @@ -52,8 +53,8 @@ export const withClamp = function <T extends number | string>(
const finished = animationToClamp.onFrame(animationToClamp, now);

if (animationToClamp.current === undefined) {
console.warn(
"[Reanimated] Error inside 'withClamp' animation, the inner animation has invalid current value"
logger.warn(
"Error inside 'withClamp' animation, the inner animation has invalid current value"
);
return true;
} else {
Expand Down Expand Up @@ -96,8 +97,8 @@ export const withClamp = function <T extends number | string>(
config.min !== undefined &&
config.max < config.min
) {
console.warn(
'[Reanimated] Wrong config was provided to withClamp. Min value is bigger than max'
logger.warn(
'Wrong config was provided to withClamp. Min value is bigger than max'
);
}

Expand Down
17 changes: 9 additions & 8 deletions packages/react-native-reanimated/src/animation/decay/decay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
InnerDecayAnimation,
} from './utils';
import { rigidDecay } from './rigidDecay';
import { ReanimatedError } from '../../errors';

export type WithDecayConfig = DecayConfig;

Expand All @@ -27,26 +28,26 @@ function validateConfig(config: DefaultDecayConfig): void {
'worklet';
if (config.clamp) {
if (!Array.isArray(config.clamp)) {
throw new Error(
`[Reanimated] \`config.clamp\` must be an array but is ${typeof config.clamp}.`
throw new ReanimatedError(
`\`config.clamp\` must be an array but is ${typeof config.clamp}.`
);
}
if (config.clamp.length !== 2) {
throw new Error(
`[Reanimated] \`clamp array\` must contain 2 items but is given ${
throw new ReanimatedError(
`\`clamp array\` must contain 2 items but is given ${
config.clamp.length as number
}.`
);
}
}
if (config.velocityFactor <= 0) {
throw new Error(
`[Reanimated] \`config.velocityFactor\` must be greather then 0 but is ${config.velocityFactor}.`
throw new ReanimatedError(
`\`config.velocityFactor\` must be greater then 0 but is ${config.velocityFactor}.`
);
}
if (config.rubberBandEffect && !config.clamp) {
throw new Error(
'[Reanimated] You need to set `clamp` property when using `rubberBandEffect`.'
throw new ReanimatedError(
'You need to set `clamp` property when using `rubberBandEffect`.'
);
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/react-native-reanimated/src/animation/sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
ReduceMotion,
Timestamp,
} from '../commonTypes';
import { logger } from '../logger';

/**
* Lets you run animations in a sequence.
Expand Down Expand Up @@ -44,7 +45,7 @@ export function withSequence(
}

if (_animations.length === 0) {
console.warn('[Reanimated] No animation was provided for the sequence');
logger.warn('No animation was provided for the sequence');

return defineAnimation<SequenceAnimation>(0, () => {
'worklet';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
Timestamp,
ReduceMotion,
} from '../commonTypes';
import { logger } from '../logger';

/**
* Spring animation configuration.
Expand Down Expand Up @@ -108,7 +109,7 @@ export function checkIfConfigIsValid(config: DefaultSpringConfig): boolean {
}

if (errorMessage !== '') {
console.warn('[Reanimated] Invalid spring config' + errorMessage);
logger.warn('Invalid spring config' + errorMessage);
}

return errorMessage === '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
import type { StyleLayoutAnimation } from './commonTypes';
import { withTiming } from './timing';
import { ColorProperties, processColor } from '../Colors';
import { logger } from '../logger';

// resolves path to value for nested objects
// if path cannot be resolved returns undefined
Expand Down Expand Up @@ -185,7 +186,7 @@ export function withStyleAnimation(
prevVal = (prevAnimation as any).current;
}
if (prevVal === undefined) {
console.warn(
logger.warn(
`Initial values for animation are missing for property ${currentEntry.path.join(
'.'
)}`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
'use strict';

import { ReanimatedError } from '../../errors';

type FixedLengthArray<
T,
L extends number,
Expand Down Expand Up @@ -255,8 +258,8 @@ function transposeMatrix(matrix: AffineMatrix): AffineMatrix {
function assertVectorsHaveEqualLengths(a: number[], b: number[]) {
'worklet';
if (__DEV__ && a.length !== b.length) {
throw new Error(
`[Reanimated] Cannot calculate inner product of two vectors of different lengths. Length of ${a.toString()} is ${
throw new ReanimatedError(
`Cannot calculate inner product of two vectors of different lengths. Length of ${a.toString()} is ${
a.length
} and length of ${b.toString()} is ${b.length}.`
);
Expand Down Expand Up @@ -348,7 +351,7 @@ export function decomposeMatrix(

// normalize matrix
if (matrix[15] === 0) {
throw new Error('[Reanimated] Invalid transform matrix.');
throw new ReanimatedError('Invalid transform matrix.');
}
matrix.forEach((_, i) => (matrix[i] /= matrix[15]));

Expand Down
12 changes: 7 additions & 5 deletions packages/react-native-reanimated/src/animation/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ import {
import { shouldBeUseWeb } from '../PlatformChecker';
import type { EasingFunctionFactory } from '../Easing';
import { ReducedMotionManager } from '../ReducedMotion';
import { logger } from '../logger';
import { ReanimatedError } from '../errors';

let IN_STYLE_UPDATER = false;
const SHOULD_BE_USE_WEB = shouldBeUseWeb();

if (__DEV__ && ReducedMotionManager.jsValue) {
console.warn(
`[Reanimated] Reduced motion setting is enabled on this device. This warning is visible only in the development mode. Some animations will be disabled by default. You can override the behavior for individual animations, see https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#reduced-motion-setting-is-enabled-on-this-device.`
logger.warn(
`Reduced motion setting is enabled on this device. This warning is visible only in the development mode. Some animations will be disabled by default. You can override the behavior for individual animations, see https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#reduced-motion-setting-is-enabled-on-this-device.`
);
}

Expand All @@ -65,8 +67,8 @@ export function assertEasingIsWorklet(
}

if (!isWorkletFunction(easing)) {
throw new Error(
'[Reanimated] The easing function is not a worklet. Please make sure you import `Easing` from react-native-reanimated.'
throw new ReanimatedError(
'The easing function is not a worklet. Please make sure you import `Easing` from react-native-reanimated.'
);
}
}
Expand All @@ -93,7 +95,7 @@ export function recognizePrefixSuffix(
/([A-Za-z]*)(-?\d*\.?\d*)([eE][-+]?[0-9]+)?([A-Za-z%]*)/
);
if (!match) {
throw new Error("[Reanimated] Couldn't parse animation value.");
throw new ReanimatedError("Couldn't parse animation value.");
}
const prefix = match[1];
const suffix = match[4];
Expand Down
5 changes: 3 additions & 2 deletions packages/react-native-reanimated/src/animationBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
} from './layoutReanimation';
import type { StyleProps } from './commonTypes';
import type { NestedArray } from './createAnimatedComponent/commonTypes';
import { logger } from './logger';

const mockTargetValues: LayoutAnimationsValues = {
targetOriginX: 0,
Expand Down Expand Up @@ -61,8 +62,8 @@ function maybeReportOverwrittenProperties(
const commonProperties = getCommonProperties(layoutAnimationStyle, style);

if (commonProperties.length > 0) {
console.warn(
`[Reanimated] ${
logger.warn(
`${
commonProperties.length === 1 ? 'Property' : 'Properties'
} "${commonProperties.join(
', '
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ReducedMotionManager,
isReducedMotionEnabledInSystem,
} from '../ReducedMotion';
import { logger } from '../logger';

/**
* A component that lets you overwrite default reduce motion behavior globally in your application.
Expand All @@ -17,9 +18,7 @@ export function ReducedMotionConfig({ mode }: { mode: ReduceMotion }) {
if (!__DEV__) {
return;
}
console.warn(
`[Reanimated] Reduced motion setting is overwritten with mode '${mode}'.`
);
logger.warn(`Reduced motion setting is overwritten with mode '${mode}'.`);
}, []);

useEffect(() => {
Expand Down
5 changes: 3 additions & 2 deletions packages/react-native-reanimated/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { makeShareableCloneRecursive } from './shareables';
import { initializeUIRuntime } from './initializers';
import type { LayoutAnimationBatchItem } from './layoutReanimation/animationBuilder/commonTypes';
import { SensorContainer } from './SensorContainer';
import { ReanimatedError } from './errors';

export { startMapper, stopMapper } from './mappers';
export { runOnJS, runOnUI, executeOnUIRuntimeSync } from './threads';
Expand Down Expand Up @@ -52,8 +53,8 @@ export function getViewProp<T>(
component?: React.Component // required on Fabric
): Promise<T> {
if (isFabric() && !component) {
throw new Error(
'[Reanimated] Function `getViewProp` requires a component to be passed as an argument on Fabric.'
throw new ReanimatedError(
'Function `getViewProp` requires a component to be passed as an argument on Fabric.'
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { addHTMLMutationObserver } from '../layoutReanimation/web/domUtils';
import { getViewInfo } from './getViewInfo';
import { NativeEventsManager } from './NativeEventsManager';
import type { ReanimatedHTMLElement } from '../js-reanimated';
import { ReanimatedError } from '../errors';

const IS_WEB = isWeb();
const IS_JEST = isJest();
Expand Down Expand Up @@ -301,8 +302,8 @@ export function createAnimatedComponent(
// hostInstance can be null for a component that doesn't render anything (render function returns null). Example: svg Stop: https://github.com/react-native-svg/react-native-svg/blob/develop/src/elements/Stop.tsx
const hostInstance = RNRenderer.findHostInstance_DEPRECATED(component);
if (!hostInstance) {
throw new Error(
'[Reanimated] Cannot find host instance for this component. Maybe it renders nothing?'
throw new ReanimatedError(
'Cannot find host instance for this component. Maybe it renders nothing?'
);
}

Expand Down
7 changes: 5 additions & 2 deletions packages/react-native-reanimated/src/fabricUtils.web.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
'use strict';

import { ReanimatedError } from './errors';

export function getShadowNodeWrapperFromRef() {
throw new Error(
'[Reanimated] Trying to call `getShadowNodeWrapperFromRef` on web.'
throw new ReanimatedError(
'Trying to call `getShadowNodeWrapperFromRef` on web.'
);
}
Loading

0 comments on commit 96b9ed4

Please sign in to comment.