Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export default function RuntimeTestsExample() {
importTest: () => {
require('./tests/advancedAPI/useFrameCallback.test');
require('./tests/advancedAPI/measure.test');
require('./tests/advancedAPI/staticFeatureFlags.test');
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getStaticFeatureFlag as getStaticFeatureFlagReanimated } from 'react-native-reanimated';
import { getStaticFeatureFlag as getStaticFeatureFlagWorklets } from 'react-native-worklets';

import { describe, expect, test } from '../../ReJest/RuntimeTestsApi';

/**
* This flag is `false` by default - this test checks if setting it
* to `true` in `package.json` is picked up on the native side.
*/
const RUNTIME_TEST_FLAG = 'RUNTIME_TEST_FLAG';

describe('Test getting static feature flags', () => {
test('from Reanimated', () => {
expect(getStaticFeatureFlagReanimated(RUNTIME_TEST_FLAG)).toBe(true);
});
test('from Worklets', () => {
expect(getStaticFeatureFlagWorklets(RUNTIME_TEST_FLAG)).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe('Test *****useFrameCallback*****', () => {
await render(<InvalidFrameCallback />);
await wait(200);
}).toThrow(
'Error: [Worklets] Tried to synchronously call a non-worklet function `imNotAWorklet` on the UI thread.\nSee https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-worklet-function-on-the-ui-thread for more details., js engine: Worklets',
'Error: [Worklets] Tried to synchronously call a non-worklet function `imNotAWorklet` on the UI thread.\nSee https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-worklet-function-on-the-ui-thread for more details.',
);
});
});
Expand Down
4 changes: 2 additions & 2 deletions apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3078,10 +3078,10 @@ SPEC CHECKSUMS:
RNCClipboard: 4b58c780f63676367640f23c8e114e9bd0cf86ac
RNCMaskedView: 5ef8c95cbab95334a32763b72896a7b7d07e6299
RNGestureHandler: 3a73f098d74712952870e948b3d9cf7b6cae9961
RNReanimated: bedbdc2587402b124d504cd243b6eed208dc1a5c
RNReanimated: e8938e49be3ffc8f3712521871f9f883c0ecc293
RNScreens: 6ced6ae8a526512a6eef6e28c2286e1fc2d378c3
RNSVG: 6f39605a4c4d200b11435c35bd077553c6b5963a
RNWorklets: f928b6ff86463bbdcfbc62043a6cf411d6cf9e70
RNWorklets: 2c4d82749bbe199a1e6d9fcd696955bd905545b5
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: 00013dd9cde63a2d98e8002fcc4f5ddb66c10782

Expand Down
10 changes: 10 additions & 0 deletions apps/fabric-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,15 @@
},
"installConfig": {
"selfReferences": false
},
"reanimated": {
"staticFeatureFlags": {
"RUNTIME_TEST_FLAG": true
}
},
"worklets": {
"staticFeatureFlags": {
"RUNTIME_TEST_FLAG": true
}
}
}
10 changes: 10 additions & 0 deletions apps/macos-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,15 @@
"installConfig": {
"hoistingLimits": "workspaces",
"selfReferences": false
},
"reanimated": {
"staticFeatureFlags": {
"RUNTIME_TEST_FLAG": true
}
},
"worklets": {
"staticFeatureFlags": {
"RUNTIME_TEST_FLAG": true
}
}
}
4 changes: 2 additions & 2 deletions apps/tvos-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2104,8 +2104,8 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 29fb2827ec6c6c8ee9ee9108e838b4e0e4e1b730
ReactCodegen: 3a01f76123e04b8b945d43e5ffccae6f90f4a26e
ReactCommon: 59e7bd3cf331ba77a96a75e6b603abf05883b102
RNReanimated: 4e7fb9acca641e2bed32e300c9dddbd350f5210d
RNWorklets: 9d4d5a0ef70d5e6385cbd28bfa4e91dd6e18c97e
RNReanimated: 72d815b95ba0f8703c4b47acc58877c7c35ee0dd
RNWorklets: 7f0681f2328f76943ca7f185d71a698eaceb6ce8
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: 770a077e3a222f162c2e0c8a95e7e997b7682a8e

Expand Down
10 changes: 10 additions & 0 deletions apps/tvos-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,15 @@
"installConfig": {
"hoistingLimits": "workspaces",
"selfReferences": false
},
"reanimated": {
"staticFeatureFlags": {
"RUNTIME_TEST_FLAG": true
}
},
"worklets": {
"staticFeatureFlags": {
"RUNTIME_TEST_FLAG": true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,18 @@ jsi::Value ReanimatedModuleProxy::getViewProp(
return jsi::Value::undefined();
}

jsi::Value ReanimatedModuleProxy::getStaticFeatureFlag(
jsi::Runtime &rt,
const jsi::Value &name) {
return reanimated::StaticFeatureFlags::getFlag(name.asString(rt).utf8(rt));
}

jsi::Value ReanimatedModuleProxy::setDynamicFeatureFlag(
jsi::Runtime &rt,
const jsi::Value &name,
const jsi::Value &value) {
DynamicFeatureFlags::setFlag(name.asString(rt).utf8(rt), value.asBool());
reanimated::DynamicFeatureFlags::setFlag(
name.asString(rt).utf8(rt), value.asBool());
return jsi::Value::undefined();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,13 @@ class ReanimatedModuleProxy
const jsi::Value &propName,
const jsi::Value &callback) override;

jsi::Value getStaticFeatureFlag(jsi::Runtime &rt, const jsi::Value &name)
override;
jsi::Value setDynamicFeatureFlag(
jsi::Runtime &rt,
const jsi::Value &name,
const jsi::Value &value) override;

jsi::Value configureLayoutAnimationBatch(
jsi::Runtime &rt,
const jsi::Value &layoutAnimationsBatch) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ static jsi::Value REANIMATED_SPEC_PREFIX(unregisterSensor)(
return jsi::Value::undefined();
}

static jsi::Value REANIMATED_SPEC_PREFIX(getStaticFeatureFlag)(
jsi::Runtime &rt,
TurboModule &turboModule,
const jsi::Value *args,
size_t) {
return static_cast<ReanimatedModuleProxySpec *>(&turboModule)
->getStaticFeatureFlag(rt, std::move(args[0]));
}

static jsi::Value REANIMATED_SPEC_PREFIX(setDynamicFeatureFlag)(
jsi::Runtime &rt,
TurboModule &turboModule,
Expand Down Expand Up @@ -224,6 +233,8 @@ ReanimatedModuleProxySpec::ReanimatedModuleProxySpec(
MethodMetadata{3, REANIMATED_SPEC_PREFIX(getViewProp)};
methodMap_["registerSensor"] =
MethodMetadata{4, REANIMATED_SPEC_PREFIX(registerSensor)};
methodMap_["getStaticFeatureFlag"] =
MethodMetadata{1, REANIMATED_SPEC_PREFIX(getStaticFeatureFlag)};
methodMap_["unregisterSensor"] =
MethodMetadata{1, REANIMATED_SPEC_PREFIX(unregisterSensor)};
methodMap_["setDynamicFeatureFlag"] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ class JSI_EXPORT ReanimatedModuleProxySpec : public TurboModule {
jsi::Runtime &rt,
const jsi::Value &listenerId) = 0;

// other
// feature flags
virtual jsi::Value getStaticFeatureFlag(
jsi::Runtime &rt,
const jsi::Value &name) = 0;

virtual jsi::Value setDynamicFeatureFlag(
jsi::Runtime &rt,
const jsi::Value &name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class StaticFeatureFlags {
// itself instead of the flag value
#define TOSTRING(x) XTOSTRING(x)

static consteval bool getFlag(const std::string_view &name) {
static constexpr bool getFlag(const std::string_view &name) {
std::string nameStr = name.data();
std::string featureFlags = TOSTRING(REANIMATED_FEATURE_FLAGS);
if (featureFlags.find("[" + nameStr + ":") == std::string::npos) {
Expand All @@ -25,7 +25,7 @@ class StaticFeatureFlags {

#else

static consteval bool getFlag(const std::string_view &) {
static constexpr bool getFlag(const std::string_view &) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { getStaticFeatureFlag } from '../..';

function GetStaticFeatureFlagTest() {
// ok - this flag exists
getStaticFeatureFlag('RUNTIME_TEST_FLAG');

// @ts-expect-error - this flag does not exist
getStaticFeatureFlag('NON_EXISTENT_FLAG');
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
);
}

getStaticFeatureFlag(name: string): boolean {
return this.#reanimatedModuleProxy.getStaticFeatureFlag(name);
}

setDynamicFeatureFlag(name: string, value: boolean) {
this.#reanimatedModuleProxy.setDynamicFeatureFlag(name, value);
}
Expand Down Expand Up @@ -248,6 +252,9 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
class DummyReanimatedModuleProxy implements ReanimatedModuleProxy {
configureLayoutAnimationBatch(): void {}
setShouldAnimateExitingForTag(): void {}
getStaticFeatureFlag(): boolean {
return false;
}
setDynamicFeatureFlag(): void {}
subscribeForKeyboardEvents(): number {
return -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,12 @@ class JSReanimated implements IReanimatedModule {
throw new ReanimatedError('getViewProp is not available in JSReanimated.');
}

setDynamicFeatureFlag(_name: string, _value: boolean): void {
getStaticFeatureFlag(): boolean {
// mock implementation
return false;
}

setDynamicFeatureFlag(): void {
// noop
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export interface ReanimatedModuleProxy {

unregisterSensor(sensorId: number): void;

getStaticFeatureFlag(name: string): boolean;

setDynamicFeatureFlag(name: string, value: boolean): void;

subscribeForKeyboardEvents(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';
import { logger } from '../common';
import { ReanimatedModule } from '../ReanimatedModule';
import type StaticFeatureFlagsJSON from './staticFlags.json';

type DynamicFlagsType = {
EXPERIMENTAL_MUTABLE_OPTIMIZATION: boolean;
Expand All @@ -9,7 +10,6 @@ type DynamicFlagsType = {
};
type DynamicFlagName = keyof Omit<Omit<DynamicFlagsType, 'setFlag'>, 'init'>;

/** @knipIgnore */
export const DynamicFlags: DynamicFlagsType = {
EXPERIMENTAL_MUTABLE_OPTIMIZATION: false,

Expand Down Expand Up @@ -43,3 +43,33 @@ export function setDynamicFeatureFlag(
): void {
DynamicFlags.setFlag(name, value);
}

/**
* This constant is needed for typechecking and preserving static typechecks in
* generated .d.ts files. Without it, the static flags resolve to an object
* without specific keys.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const DefaultStaticFeatureFlags = {
DISABLE_COMMIT_PAUSING_MECHANISM: false,
ANDROID_SYNCHRONOUSLY_UPDATE_UI_PROPS: false,
UNSTABLE_CSS_ANIMATIONS_FOR_SVG_COMPONENTS: false,
RUNTIME_TEST_FLAG: false,
} as const satisfies typeof StaticFeatureFlagsJSON;

type StaticFeatureFlagsSchema = {
-readonly [K in keyof typeof DefaultStaticFeatureFlags]: boolean;
};

const staticFeatureFlags: Partial<StaticFeatureFlagsSchema> = {};

export function getStaticFeatureFlag(
name: keyof StaticFeatureFlagsSchema
): boolean {
if (name in staticFeatureFlags) {
return staticFeatureFlags[name]!;
}
const featureFlagValue = ReanimatedModule.getStaticFeatureFlag(name);
staticFeatureFlags[name] = featureFlagValue;
return featureFlagValue;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"DISABLE_COMMIT_PAUSING_MECHANISM": false,
"ANDROID_SYNCHRONOUSLY_UPDATE_UI_PROPS": false,
"UNSTABLE_CSS_ANIMATIONS_FOR_SVG_COMPONENTS": false
"UNSTABLE_CSS_ANIMATIONS_FOR_SVG_COMPONENTS": false,
"RUNTIME_TEST_FLAG": false
}
2 changes: 1 addition & 1 deletion packages/react-native-reanimated/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export {
export * from './css';
export type { EasingFunctionFactory } from './Easing';
export { Easing } from './Easing';
export { setDynamicFeatureFlag } from './featureFlags/dynamicFlags';
export { getStaticFeatureFlag, setDynamicFeatureFlag } from './featureFlags';
export type { FrameInfo } from './frameCallback';
export type { AnimatedProps, EntryOrExitLayoutType } from './helperTypes';
export type {
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-reanimated/src/mutables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {

import { IS_JEST, logger, ReanimatedError, SHOULD_BE_USE_WEB } from './common';
import type { Mutable } from './commonTypes';
import { DynamicFlags } from './featureFlags/dynamicFlags';
import { DynamicFlags } from './featureFlags';
import { isFirstReactRender, isReactRendering } from './reactUtils';
import { valueSetter } from './valueSetter';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ std::vector<jsi::PropNameID> JSIWorkletsModuleProxy::getPropertyNames(
jsi::PropNameID::forAscii(rt, "scheduleOnRuntime"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "reportFatalErrorOnJS"));

propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "getStaticFeatureFlag"));
propertyNames.emplace_back(
jsi::PropNameID::forAscii(rt, "setDynamicFeatureFlag"));

Expand Down Expand Up @@ -669,6 +672,20 @@ jsi::Value JSIWorkletsModuleProxy::get(
}
#endif // WORKLETS_BUNDLE_MODE

if (name == "getStaticFeatureFlag") {
return jsi::Function::createFromHostFunction(
rt,
propName,
2,
[](jsi::Runtime &rt,
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
return worklets::StaticFeatureFlags::getFlag(
/* name */ args[0].asString(rt).utf8(rt));
});
}

if (name == "setDynamicFeatureFlag") {
return jsi::Function::createFromHostFunction(
rt,
Expand All @@ -678,7 +695,7 @@ jsi::Value JSIWorkletsModuleProxy::get(
const jsi::Value &thisValue,
const jsi::Value *args,
size_t count) {
DynamicFeatureFlags::setFlag(
worklets::DynamicFeatureFlags::setFlag(
/* name */ args[0].asString(rt).utf8(rt),
/* value */ args[1].asBool());
return jsi::Value::undefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class StaticFeatureFlags {
// itself instead of the flag value
#define TOSTRING(x) XTOSTRING(x)

static consteval bool getFlag(const std::string_view &name) {
static constexpr bool getFlag(const std::string_view &name) {
std::string nameStr = name.data();
std::string featureFlags = TOSTRING(WORKLETS_FEATURE_FLAGS);
if (featureFlags.find("[" + nameStr + ":") == std::string::npos) {
Expand All @@ -25,7 +25,7 @@ class StaticFeatureFlags {

#else

static consteval bool getFlag(const std::string_view &) {
static constexpr bool getFlag(const std::string_view &) {
return false;
}

Expand Down
10 changes: 10 additions & 0 deletions packages/react-native-worklets/__typetests__/FeatureFlagTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { getStaticFeatureFlag } from '..';

function GetStaticFeatureFlagTest() {
// ok - this flag exists
getStaticFeatureFlag('RUNTIME_TEST_FLAG');

// @ts-expect-error - this flag does not exist
getStaticFeatureFlag('NON_EXISTENT_FLAG');
}
Loading
Loading