react-native-worklets
Version:
The React Native multithreading library
198 lines (188 loc) • 7.34 kB
JavaScript
;
import { setupCallGuard } from '../callGuard';
import { registerReportFatalRemoteError } from '../debug/errors';
import { registerWorkletsError, WorkletsError } from '../debug/WorkletsError';
import { bundleValueUnpacker } from '../memory/bundleUnpacker';
import { __installUnpacker as installCustomSerializableUnpacker } from '../memory/customSerializableUnpacker';
import { __installUnpacker as installSynchronizableUnpacker } from '../memory/synchronizableUnpacker';
import { setupSetImmediate } from '../runLoop/common/setImmediatePolyfill';
import { setupSetInterval } from '../runLoop/common/setIntervalPolyfill';
import { setupRequestAnimationFrame } from '../runLoop/uiRuntime/requestAnimationFrame';
import { setupSetTimeout } from '../runLoop/uiRuntime/setTimeoutPolyfill';
import { RuntimeKind } from '../runtimeKind';
import { runOnUISync, scheduleOnRN, setupMicrotasks } from '../threads';
import { isWorkletFunction } from '../workletFunction';
import { WorkletsModule } from '../WorkletsModule/NativeWorklets';
if (globalThis.__RUNTIME_KIND === undefined) {
// The only runtime that doesn't have `__RUNTIME_KIND` preconfigured
// is the RN Runtime. We must set it as soon as possible.
globalThis.__RUNTIME_KIND = RuntimeKind.ReactNative;
}
let capturableConsole;
/**
* Currently there seems to be a bug in the JSI layer which causes a crash when
* we try to copy some of the console methods, i.e. `clear` or `dirxml`.
*
* The crash happens only in React Native 0.75. It's not reproducible in neither
* 0.76 nor 0.74. It also happens only in the configuration of a debug app and
* production bundle.
*
* I haven't yet discovered what exactly causes the crash. It's tied to the
* console methods sometimes being `HostFunction`s. Therefore, as a workaround
* we don't copy the methods as they are in the original console object, we copy
* JavaScript wrappers instead.
*/
export function getMemorySafeCapturableConsole() {
if (capturableConsole) {
return capturableConsole;
}
const consoleCopy = Object.fromEntries(Object.entries(console).map(([methodName, method]) => {
const methodWrapper = function methodWrapper(...args) {
return method(...args);
};
if (method.name) {
/**
* Set the original method name as the wrapper name if available.
*
* It might be unnecessary but if we want to fully mimic the console
* object we should take into the account the fact some code might rely
* on the method name.
*/
Object.defineProperty(methodWrapper, 'name', {
value: method.name,
writable: false
});
}
return [methodName, methodWrapper];
}));
capturableConsole = consoleCopy;
return consoleCopy;
}
export function setupConsole(boundCapturableConsole) {
'worklet';
// @ts-ignore TypeScript doesn't like that there are missing methods in console object, but we don't provide all the methods for the UI runtime console version
globalThis.console = {
assert: (...args) => scheduleOnRN(boundCapturableConsole.assert, ...args),
debug: (...args) => scheduleOnRN(boundCapturableConsole.debug, ...args),
log: (...args) => scheduleOnRN(boundCapturableConsole.log, ...args),
warn: (...args) => scheduleOnRN(boundCapturableConsole.warn, ...args),
error: (...args) => scheduleOnRN(boundCapturableConsole.error, ...args),
info: (...args) => scheduleOnRN(boundCapturableConsole.info, ...args)
};
}
let initialized = false;
export function init() {
if (initialized) {
return;
}
initialized = true;
initializeRuntime();
if (globalThis.__RUNTIME_KIND !== RuntimeKind.ReactNative) {
initializeWorkletRuntime();
} else {
initializeRNRuntime();
installRNBindingsOnUIRuntime();
}
}
/** A function that should be run on any kind of runtime. */
function initializeRuntime() {
if (globalThis._WORKLETS_BUNDLE_MODE) {
globalThis.__valueUnpacker = bundleValueUnpacker;
}
installSynchronizableUnpacker();
installCustomSerializableUnpacker();
}
/** A function that should be run only on React Native runtime. */
function initializeRNRuntime() {
if (__DEV__) {
const testWorklet = () => {
'worklet';
};
if (!isWorkletFunction(testWorklet)) {
throw new WorkletsError(`Failed to create a worklet. See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#failed-to-create-a-worklet for more details.`);
}
}
registerReportFatalRemoteError();
}
/** A function that should be run only on Worklet runtimes. */
function initializeWorkletRuntime() {
if (globalThis._WORKLETS_BUNDLE_MODE) {
setupCallGuard();
if (__DEV__) {
/*
* Temporary workaround for Metro bundler. We must implement a dummy
* Refresh module to prevent Metro from throwing irrelevant errors.
*/
const Refresh = new Proxy({}, {
get() {
return () => {};
}
});
globalThis.__r.Refresh = Refresh;
/* Gracefully handle unwanted imports from React Native. */
const modules = require.getModules();
const ReactNativeModuleId = require.resolveWeak('react-native');
const factory = function (_global, _require, _importDefault, _importAll, module, _exports, _dependencyMap) {
module.exports = new Proxy({}, {
get: function get(_target, prop) {
globalThis.console.warn(`You tried to import '${String(prop)}' from 'react-native' module on a Worklet Runtime. Using 'react-native' module on a Worklet Runtime is not allowed.`);
return {
get() {
return undefined;
}
};
}
});
};
const mod = {
dependencyMap: [],
factory,
hasError: false,
importedAll: {},
importedDefault: {},
isInitialized: false,
publicModule: {
exports: {}
}
};
modules.set(ReactNativeModuleId, mod);
}
}
}
/**
* A function that should be run on the RN Runtime to configure the UI Runtime
* with callback bindings.
*/
function installRNBindingsOnUIRuntime() {
if (!WorkletsModule) {
throw new WorkletsError('Worklets are trying to initialize the UI runtime without a valid WorkletsModule');
}
const runtimeBoundCapturableConsole = getMemorySafeCapturableConsole();
if (!globalThis._WORKLETS_BUNDLE_MODE) {
/** In bundle mode Runtimes setup their callGuard themselves. */
runOnUISync(setupCallGuard);
/**
* Register WorkletsError in the UI runtime global scope. (we are using
* `executeOnUIRuntimeSync` here to make sure that the changes are applied
* before any async operations are executed on the UI runtime).
*
* There's no need to register the error in bundle mode.
*/
runOnUISync(registerWorkletsError);
}
runOnUISync(() => {
'worklet';
setupConsole(runtimeBoundCapturableConsole);
/**
* TODO: Move `setupMicrotasks` and `setupRequestAnimationFrame` to a
* separate function once we have a better way to distinguish between
* Worklet Runtimes.
*/
setupMicrotasks();
setupRequestAnimationFrame();
setupSetTimeout();
setupSetImmediate();
setupSetInterval();
});
}
//# sourceMappingURL=initializers.native.js.map