Skip to content

Commit

Permalink
refactor: move Worklets JS core from Reanimated (#6973)
Browse files Browse the repository at this point in the history
## Summary

Currently JS core of Worklets sits in Reanimated package. This PR moves
it to Worklets. It's a bit messy but we'll get there eventually.

- Added `createCustomError` that creates `WorkletsError` and
`ReanimatedError` used in respective packages.
- Added ESLint rule `use-worklets-error`, a copypaste of
`use-reanimated-error`.
- Moved `shareables.ts` to `react-native-worklets`.
- Moved `threads.ts` to `react-native-worklets`.
- Moved `initializers.ts` to `react-native-worklets`
- Moved `runtimes.ts` to `react-native-worklets`
- JS version checking in worklets is disabled for the time being, as it
would clutter this PR even more. We'll circle back to it once we set
version checking between Reanimated and Worklets.

## Test plan

CI, run the example app.
  • Loading branch information
tjzel authored Feb 6, 2025
1 parent a343322 commit 455d94a
Show file tree
Hide file tree
Showing 73 changed files with 565 additions and 264 deletions.
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/.yarn/releases/* binary=true
/.yarn/plugins/**/* binary=true
/packages/eslint-plugin-reanimated/index.js linguist-generated=true
/packages/eslint-plugin-reanimated/types linguist-generated=true
/packages/eslint-plugin-reanimated/types/**/* linguist-generated=true
46 changes: 46 additions & 0 deletions packages/eslint-plugin-reanimated/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/eslint-plugin-reanimated/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import type { TSESLint } from '@typescript-eslint/utils';

import noAnimatedStyleToNonAnimatedComponent from './noAnimatedStyleToNonAnimatedComponent';
import useReanimatedError from './useReanimatedError';
import useWorkletsError from './useWorkletsError';
import useWorkletsResolver from './useWorkletsResolver';

export const rules = {
'animated-style-non-animated-component':
noAnimatedStyleToNonAnimatedComponent,
'use-reanimated-error': useReanimatedError,
'use-worklets-error': useWorkletsError,
'use-worklets-resolver': useWorkletsResolver,
} satisfies Record<string, TSESLint.RuleModule<string, Array<unknown>>>;
42 changes: 42 additions & 0 deletions packages/eslint-plugin-reanimated/src/useWorkletsError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
// eslint-disable-next-line import/no-unresolved
import { AST_NODE_TYPES } from '@typescript-eslint/utils';

const rule: TSESLint.RuleModule<'useWorkletsError', []> = {
create: function (context) {
return {
NewExpression(node: TSESTree.NewExpression) {
// Check if the expression is `new Error`
if (
node.callee.type === AST_NODE_TYPES.Identifier &&
node.callee.name === 'Error'
) {
context.report({
node,
messageId: 'useWorkletsError',
fix: function (fixer) {
// Replace `Error` with `WorkletsError`
return fixer.replaceText(node.callee, 'WorkletsError');
},
});
}
},
};
},
meta: {
docs: {
recommended: 'recommended',
description:
'Warns when `new Error` is used instead of `new WorkletsError`.',
},
messages: {
useWorkletsError: 'Use `new WorkletsError` instead of `new Error`.',
},
type: 'suggestion',
schema: [],
fixable: 'code',
},
defaultOptions: [],
};

export default rule;
5 changes: 5 additions & 0 deletions packages/eslint-plugin-reanimated/types/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/eslint-plugin-reanimated/types/useWorkletsError.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ describe('checkCppVersion', () => {
jest.spyOn(console, 'warn').mockImplementation();
delete global._REANIMATED_VERSION_CPP;
checkCppVersion();
expect(console.warn).toBeCalled();
// TODO: Swap the following once version checking is restored.
// expect(console.warn).toBeCalled();
expect(console.warn).not.toBeCalled();
jest.clearAllMocks();
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/react-native-reanimated/plugin/index.js

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions packages/react-native-reanimated/plugin/src/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,12 @@ const notCapturedIdentifiers = [
'HermesInternal',

// Reanimated
'_WORKLET',
'ReanimatedError',
'__reanimatedLoggerConfig',

// Worklets
'_WORKLET',
'WorkletsError',
'__workletsLoggerConfig',
];

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
import { ReanimatedError } from './errors';
import { isFabric } from './PlatformChecker';
import { runOnUI } from './threads';
import { runOnUI } from './WorkletsResolver';

let VIEW_TAGS: number[] = [];

Expand Down
4 changes: 2 additions & 2 deletions packages/react-native-reanimated/src/ConfigHelper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';
import { executeOnUIRuntimeSync, jsiConfigureProps } from './core';
import { ReanimatedError } from './errors';
import type { LoggerConfig } from './logger';
import { updateLoggerConfig } from './logger';
import { shouldBeUseWeb } from './PlatformChecker';
import { PropsAllowlists } from './propsAllowlists';
import type { LoggerConfig } from './WorkletsResolver';
import { updateLoggerConfig } from './WorkletsResolver';

const SHOULD_BE_USE_WEB = shouldBeUseWeb();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
NormalizedSingleCSSAnimationConfig,
NormalizedSingleCSSAnimationSettings,
} from '../css/platform/native';
import { ReanimatedError } from '../errors';
import { ReanimatedError, registerReanimatedError } from '../errors';
import { getShadowNodeWrapperFromRef } from '../fabricUtils';
import { checkCppVersion } from '../platform-specific/checkCppVersion';
import { jsVersion } from '../platform-specific/jsVersion';
Expand All @@ -24,7 +24,7 @@ import type {
ShareableRef,
WorkletFunction,
} from '../WorkletsResolver';
import { WorkletsModule } from '../WorkletsResolver';
import { executeOnUIRuntimeSync, WorkletsModule } from '../WorkletsResolver';
import type {
IReanimatedModule,
ReanimatedModuleProxy,
Expand Down Expand Up @@ -81,6 +81,7 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
checkCppVersion();
}
this.#reanimatedModuleProxy = global.__reanimatedModuleProxy;
executeOnUIRuntimeSync(registerReanimatedError);
}

registerSensor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import type {
NormalizedSingleCSSAnimationSettings,
} from '../../css/platform/native';
import { ReanimatedError } from '../../errors';
import { logger } from '../../logger';
import {
isChromeDebugger,
isJest,
Expand All @@ -24,7 +23,7 @@ import type {
ShareableRef,
WorkletFunction,
} from '../../WorkletsResolver';
import { WorkletsModule } from '../../WorkletsResolver';
import { logger, WorkletsModule } from '../../WorkletsResolver';
import type { IReanimatedModule } from '../reanimatedModuleProxy';
import type { WebSensor } from './WebSensor';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use strict';
import type { AnimatedStyle, StyleProps } from '../../commonTypes';
import { ReanimatedError } from '../../errors';
import { logger } from '../../logger';
import { PropsAllowlists } from '../../propsAllowlists';
import { logger } from '../../WorkletsResolver';
import {
createReactDOMStyle,
createTextShadowValue,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-reanimated/src/UpdateProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { Descriptor } from './hook/commonTypes';
import { isFabric, isJest, shouldBeUseWeb } from './PlatformChecker';
import type { ReanimatedHTMLElement } from './ReanimatedModule/js-reanimated';
import { _updatePropsJS } from './ReanimatedModule/js-reanimated';
import { runOnUIImmediately } from './threads';
import { runOnUIImmediately } from './WorkletsResolver';

let updateProps: (
viewDescriptors: ViewDescriptorsWrapper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,4 @@ try {
}
}

module.exports = {
WorkletsModule: worklets.WorkletsModule,
isWorkletFunction: worklets.isWorkletFunction,
mockedRequestAnimationFrame: worklets.mockedRequestAnimationFrame,
};
module.exports = worklets;
99 changes: 99 additions & 0 deletions packages/react-native-reanimated/src/WorkletsResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,79 @@
// the `WorkletsResolver` module.

import type {
callMicrotasks as callMicrotasksType,
createCustomError as createCustomErrorType,
createWorkletRuntime as createWorkletRuntimeType,
executeOnUIRuntimeSync as executeOnUIRuntimeSyncType,
isWorkletFunction as isWorkletFunctionType,
IWorkletsModule,
logger as loggerType,
LogLevel as LogLevelType,
makeShareable as makeShareableType,
makeShareableCloneOnUIRecursive as makeShareableCloneOnUIRecursiveType,
makeShareableCloneRecursive as makeShareableCloneRecursiveType,
mockedRequestAnimationFrame as mockedRequestAnimationFrameType,
registerCustomError as registerCustomErrorType,
registerLoggerConfig as registerLoggerConfigType,
registerWorkletStackDetails as registerWorkletStackDetailsType,
reportFatalErrorOnJS as reportFatalErrorOnJSType,
runOnJS as runOnJSType,
runOnRuntime as runOnRuntimeType,
runOnUI as runOnUIType,
runOnUIImmediately as runOnUIImmediatelyType,
setupCallGuard as setupCallGuardType,
setupConsole as setupConsoleType,
shareableMappingCache as shareableMappingCacheType,
updateLoggerConfig as updateLoggerConfigType,
} from '../worklets';
import {
// @ts-expect-error - required for resolving the module
callMicrotasks as ResolvedCallMicrotasks,
// @ts-expect-error - required for resolving the module
createCustomError as ResolvedCreateCustomError,
// @ts-expect-error - required for resolving the module
createWorkletRuntime as ResolvedCreateWorkletRuntime,
// @ts-expect-error - required for resolving the module
executeOnUIRuntimeSync as ResolvedExecuteOnUIRuntimeSync,
// @ts-expect-error - required for resolving the module
isWorkletFunction as ResolvedIsWorkletFunction,
// @ts-expect-error - required for resolving the module
logger as ResolvedLogger,
// @ts-expect-error - required for resolving the module
LogLevel as ResolvedLogLevel,
// @ts-expect-error - required for resolving the module
makeShareable as ResolvedMakeShareable,
// @ts-expect-error - required for resolving the module
makeShareableCloneOnUIRecursive as ResolvedMakeShareableCloneOnUIRecursive,
// @ts-expect-error - required for resolving the module
makeShareableCloneRecursive as ResolvedMakeShareableCloneRecursive,
// @ts-expect-error - required for resolving the module
mockedRequestAnimationFrame as ResolvedMockedRequestAnimationFrame,
// @ts-expect-error - required for resolving the module
registerCustomError as ResolvedRegisterCustomError,
// @ts-expect-error - required for resolving the module
registerLoggerConfig as ResolvedRegisterLoggerConfig,
// @ts-expect-error - required for resolving the module
registerWorkletStackDetails as ResolvedRegisterWorkletStackDetails,
// @ts-expect-error - required for resolving the module
reportFatalErrorOnJs as ResolvedReportFatalErrorOnJS,
// @ts-expect-error - required for resolving the module
runOnJS as ResolvedRunOnJS,
// @ts-expect-error - required for resolving the module
runOnRuntime as ResolvedRunOnRuntime,
// @ts-expect-error - required for resolving the module
runOnUI as ResolvedRunOnUI,
// @ts-expect-error - required for resolving the module
runOnUIImmediately as ResolvedRunOnUIImmediately,
// @ts-expect-error - required for resolving the module
setupCallGuard as ResolvedSetupCallGuard,
// @ts-expect-error - required for resolving the module
setupConsole as ResolvedSetupConsole,
// @ts-expect-error - required for resolving the module
shareableMappingCache as ResolvedShareableMappingCache,
// @ts-expect-error - required for resolving the module
updateLoggerConfig as ResolvedUpdateLoggerConfig,
// @ts-expect-error - required for resolving the module
WorkletsModule as ResolvedWorkletsModule,
} from './WorkletsResolver';

Expand All @@ -23,9 +86,45 @@ export const isWorkletFunction =
ResolvedIsWorkletFunction as typeof isWorkletFunctionType;
export const mockedRequestAnimationFrame =
ResolvedMockedRequestAnimationFrame as typeof mockedRequestAnimationFrameType;
export const createCustomError =
ResolvedCreateCustomError as typeof createCustomErrorType;
export const registerCustomError =
ResolvedRegisterCustomError as typeof registerCustomErrorType;
export const reportFatalErrorOnJS =
ResolvedReportFatalErrorOnJS as typeof reportFatalErrorOnJSType;
export const registerWorkletStackDetails =
ResolvedRegisterWorkletStackDetails as typeof registerWorkletStackDetailsType;
export const runOnUI = ResolvedRunOnUI as typeof runOnUIType;
export const runOnJS = ResolvedRunOnJS as typeof runOnJSType;
export const runOnUIImmediately =
ResolvedRunOnUIImmediately as typeof runOnUIImmediatelyType;
export const executeOnUIRuntimeSync =
ResolvedExecuteOnUIRuntimeSync as typeof executeOnUIRuntimeSyncType;
export const makeShareable = ResolvedMakeShareable as typeof makeShareableType;
export const makeShareableCloneRecursive =
ResolvedMakeShareableCloneRecursive as typeof makeShareableCloneRecursiveType;
export const makeShareableCloneOnUIRecursive =
ResolvedMakeShareableCloneOnUIRecursive as typeof makeShareableCloneOnUIRecursiveType;
export const shareableMappingCache =
ResolvedShareableMappingCache as typeof shareableMappingCacheType;
export const callMicrotasks =
ResolvedCallMicrotasks as typeof callMicrotasksType;
export const logger = ResolvedLogger as typeof loggerType;
export const updateLoggerConfig =
ResolvedUpdateLoggerConfig as typeof updateLoggerConfigType;
export const LogLevel = ResolvedLogLevel as typeof LogLevelType;
export const registerLoggerConfig =
ResolvedRegisterLoggerConfig as typeof registerLoggerConfigType;
export const setupCallGuard =
ResolvedSetupCallGuard as typeof setupCallGuardType;
export const setupConsole = ResolvedSetupConsole as typeof setupConsoleType;
export const createWorkletRuntime =
ResolvedCreateWorkletRuntime as typeof createWorkletRuntimeType;
export const runOnRuntime = ResolvedRunOnRuntime as typeof runOnRuntimeType;

export type {
IWorkletsModule,
LoggerConfig,
ShareableRef,
WorkletFunction,
WorkletFunctionDev,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-reanimated/src/animation/clamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
ReduceMotion,
Timestamp,
} from '../commonTypes';
import { logger } from '../logger';
import { logger } from '../WorkletsResolver';
import type { ClampAnimation } from './commonTypes';
import {
defineAnimation,
Expand Down
Loading

0 comments on commit 455d94a

Please sign in to comment.