diff --git a/apps/next-example/babel.config.js b/apps/next-example/babel.config.js index 94838f17f775..9075b7dc683f 100644 --- a/apps/next-example/babel.config.js +++ b/apps/next-example/babel.config.js @@ -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', }, }, ], diff --git a/packages/react-native-reanimated/src/Bezier.ts b/packages/react-native-reanimated/src/Bezier.ts index c745dba8c4a7..3968f3a689c8 100644 --- a/packages/react-native-reanimated/src/Bezier.ts +++ b/packages/react-native-reanimated/src/Bezier.ts @@ -1,4 +1,7 @@ 'use strict'; + +import { ReanimatedError } from './errors'; + /** * https://github.com/gre/bezier-easing * BezierEasing - use bezier curve for transition easing function @@ -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) { diff --git a/packages/react-native-reanimated/src/ConfigHelper.ts b/packages/react-native-reanimated/src/ConfigHelper.ts index 9026cc8b9602..b93993bb0574 100644 --- a/packages/react-native-reanimated/src/ConfigHelper.ts +++ b/packages/react-native-reanimated/src/ConfigHelper.ts @@ -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.` ); } } diff --git a/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts b/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts index 4119474c5afc..4c2e833dfe25 100644 --- a/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts +++ b/packages/react-native-reanimated/src/NativeReanimated/NativeReanimated.ts @@ -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 { @@ -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}.` ); } @@ -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.` ); } diff --git a/packages/react-native-reanimated/src/PropsRegistry.ts b/packages/react-native-reanimated/src/PropsRegistry.ts index 5b005618ef11..16ca3922cec8 100644 --- a/packages/react-native-reanimated/src/PropsRegistry.ts +++ b/packages/react-native-reanimated/src/PropsRegistry.ts @@ -1,4 +1,5 @@ 'use strict'; +import { ReanimatedError } from './errors'; import { isFabric } from './PlatformChecker'; import { runOnUI } from './threads'; @@ -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 = []; diff --git a/packages/react-native-reanimated/src/UpdateProps.ts b/packages/react-native-reanimated/src/UpdateProps.ts index f4ad235ed762..69b15716d14f 100644 --- a/packages/react-native-reanimated/src/UpdateProps.ts +++ b/packages/react-native-reanimated/src/UpdateProps.ts @@ -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, @@ -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.' ); } }; diff --git a/packages/react-native-reanimated/src/animation/clamp.ts b/packages/react-native-reanimated/src/animation/clamp.ts index 3e71ab2fb995..7af1653f4821 100644 --- a/packages/react-native-reanimated/src/animation/clamp.ts +++ b/packages/react-native-reanimated/src/animation/clamp.ts @@ -12,6 +12,7 @@ import type { ReduceMotion, } from '../commonTypes'; import type { ClampAnimation } from './commonTypes'; +import { logger } from '../logger'; type withClampType = ( config: { @@ -52,8 +53,8 @@ export const withClamp = function ( 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 { @@ -96,8 +97,8 @@ export const withClamp = function ( 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' ); } diff --git a/packages/react-native-reanimated/src/animation/decay/decay.ts b/packages/react-native-reanimated/src/animation/decay/decay.ts index 33eefd7d8be6..104bda4b0854 100644 --- a/packages/react-native-reanimated/src/animation/decay/decay.ts +++ b/packages/react-native-reanimated/src/animation/decay/decay.ts @@ -14,6 +14,7 @@ import type { InnerDecayAnimation, } from './utils'; import { rigidDecay } from './rigidDecay'; +import { ReanimatedError } from '../../errors'; export type WithDecayConfig = DecayConfig; @@ -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`.' ); } } diff --git a/packages/react-native-reanimated/src/animation/sequence.ts b/packages/react-native-reanimated/src/animation/sequence.ts index 95617168621c..05077ca40399 100644 --- a/packages/react-native-reanimated/src/animation/sequence.ts +++ b/packages/react-native-reanimated/src/animation/sequence.ts @@ -8,6 +8,7 @@ import type { ReduceMotion, Timestamp, } from '../commonTypes'; +import { logger } from '../logger'; /** * Lets you run animations in a sequence. @@ -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(0, () => { 'worklet'; diff --git a/packages/react-native-reanimated/src/animation/springUtils.ts b/packages/react-native-reanimated/src/animation/springUtils.ts index 466440d3eddc..fb8d29f72c8f 100644 --- a/packages/react-native-reanimated/src/animation/springUtils.ts +++ b/packages/react-native-reanimated/src/animation/springUtils.ts @@ -5,6 +5,7 @@ import type { Timestamp, ReduceMotion, } from '../commonTypes'; +import { logger } from '../logger'; /** * Spring animation configuration. @@ -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 === ''; diff --git a/packages/react-native-reanimated/src/animation/styleAnimation.ts b/packages/react-native-reanimated/src/animation/styleAnimation.ts index c8ef61005613..235f201d5ffa 100644 --- a/packages/react-native-reanimated/src/animation/styleAnimation.ts +++ b/packages/react-native-reanimated/src/animation/styleAnimation.ts @@ -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 @@ -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( '.' )}` diff --git a/packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx b/packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx index bb8d30c080c5..5f107066fe48 100644 --- a/packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx +++ b/packages/react-native-reanimated/src/animation/transformationMatrix/matrixUtils.tsx @@ -1,4 +1,7 @@ 'use strict'; + +import { ReanimatedError } from '../../errors'; + type FixedLengthArray< T, L extends number, @@ -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}.` ); @@ -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])); diff --git a/packages/react-native-reanimated/src/animation/util.ts b/packages/react-native-reanimated/src/animation/util.ts index bdece36e854a..8211e818c219 100644 --- a/packages/react-native-reanimated/src/animation/util.ts +++ b/packages/react-native-reanimated/src/animation/util.ts @@ -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.` ); } @@ -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.' ); } } @@ -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]; diff --git a/packages/react-native-reanimated/src/animationBuilder.tsx b/packages/react-native-reanimated/src/animationBuilder.tsx index a5067d88576c..02f3892b7b7b 100644 --- a/packages/react-native-reanimated/src/animationBuilder.tsx +++ b/packages/react-native-reanimated/src/animationBuilder.tsx @@ -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, @@ -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( ', ' diff --git a/packages/react-native-reanimated/src/component/ReducedMotionConfig.tsx b/packages/react-native-reanimated/src/component/ReducedMotionConfig.tsx index 9a2eb5983258..ddc04fc1092d 100644 --- a/packages/react-native-reanimated/src/component/ReducedMotionConfig.tsx +++ b/packages/react-native-reanimated/src/component/ReducedMotionConfig.tsx @@ -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. @@ -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(() => { diff --git a/packages/react-native-reanimated/src/core.ts b/packages/react-native-reanimated/src/core.ts index 0cc50a6ef8c8..af085641a194 100644 --- a/packages/react-native-reanimated/src/core.ts +++ b/packages/react-native-reanimated/src/core.ts @@ -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'; @@ -52,8 +53,8 @@ export function getViewProp( component?: React.Component // required on Fabric ): Promise { 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.' ); } diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx b/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx index cd010b247282..9f5fd59102dd 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx +++ b/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx @@ -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(); @@ -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?' ); } diff --git a/packages/react-native-reanimated/src/fabricUtils.web.ts b/packages/react-native-reanimated/src/fabricUtils.web.ts index 220245daa429..e8c389f1280e 100644 --- a/packages/react-native-reanimated/src/fabricUtils.web.ts +++ b/packages/react-native-reanimated/src/fabricUtils.web.ts @@ -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.' ); } diff --git a/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts b/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts index 160775c8f4eb..8c71d599e684 100644 --- a/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts +++ b/packages/react-native-reanimated/src/hook/useAnimatedStyle.ts @@ -34,6 +34,7 @@ import type { AnimatedStyle, } from '../commonTypes'; import { isWorkletFunction } from '../commonTypes'; +import { ReanimatedError } from '../errors'; const SHOULD_BE_USE_WEB = shouldBeUseWeb(); @@ -379,9 +380,9 @@ function checkSharedValueUsage( prop !== null && prop.value !== undefined ) { - // if shared value is passed insted of its value, throw an error - throw new Error( - `[Reanimated] Invalid value passed to \`${currentKey}\`, maybe you forgot to use \`.value\`?` + // if shared value is passed instead of its value, throw an error + throw new ReanimatedError( + `Invalid value passed to \`${currentKey}\`, maybe you forgot to use \`.value\`?` ); } } @@ -422,8 +423,8 @@ export function useAnimatedStyle