Skip to content

Commit

Permalink
Add support for feature flags without native implementation (facebook…
Browse files Browse the repository at this point in the history
…#47059)

Summary:

Changelog: [internal]

This adds a new configuration for feature flags to preserve their definition only in JavaScript and skip their native API and implementations. This is useful to preserve the API in JavaScript when JavaScript changes progress faster than native changes.

Reviewed By: sammy-SC

Differential Revision: D64464779
  • Loading branch information
rubennorte authored and facebook-github-bot committed Oct 17, 2024
1 parent 01e210f commit 953407a
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<81047b339ef89dfdfa10b58e22d9407d>>
* @generated SignedSource<<61c80a52ec4c2cf0904bb17c8f479f9d>>
*/

/**
Expand Down Expand Up @@ -42,6 +42,13 @@ bool NativeReactNativeFeatureFlags::commonTestFlag(
return ReactNativeFeatureFlags::commonTestFlag();
}

bool NativeReactNativeFeatureFlags::commonTestFlagWithoutNativeImplementation(
jsi::Runtime& /*runtime*/) {
// This flag is configured with `skipNativeAPI: true`.
// TODO(T204838867): Implement support for optional methods in C++ TM codegen and remove the method definition altogether.
return false;
}

bool NativeReactNativeFeatureFlags::allowRecursiveCommitsWithSynchronousMountOnAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::allowRecursiveCommitsWithSynchronousMountOnAndroid();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<8260155b1822cf4906660d3ad9b298a4>>
* @generated SignedSource<<68423ab013e1357ec176da0fbef4f7e8>>
*/

/**
Expand Down Expand Up @@ -37,6 +37,8 @@ class NativeReactNativeFeatureFlags

bool commonTestFlag(jsi::Runtime& runtime);

bool commonTestFlagWithoutNativeImplementation(jsi::Runtime& runtime);

bool allowRecursiveCommitsWithSynchronousMountOnAndroid(jsi::Runtime& runtime);

bool batchRenderingUpdatesInEventLoop(jsi::Runtime& runtime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ const testDefinitions: FeatureFlagDefinitions = {
purpose: 'operational',
},
},
commonTestFlagWithoutNativeImplementation: {
defaultValue: false,
metadata: {
description:
'Common flag for testing (without native implementation). Do NOT modify.',
purpose: 'operational',
},
skipNativeAPI: true,
},
},
jsOnly: {
jsOnlyTestFlag: {
Expand Down
26 changes: 23 additions & 3 deletions packages/react-native/scripts/featureflags/generateFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,31 @@ export default function generateFiles(

const jsModules = generateJavaScriptModules(generatorConfig);

const commonCxxModules = generateCommonCxxModules(generatorConfig);
const generatorConfigWithDefinitionsForNative = {
...generatorConfig,
featureFlagDefinitions: {
...generatorConfig.featureFlagDefinitions,
common: Object.fromEntries(
Object.entries(generatorConfig.featureFlagDefinitions.common).filter(
([_, definition]) => !definition.skipNativeAPI,
),
),
},
};

const androidModules = generateAndroidModules(generatorConfig);
const commonCxxModules = generateCommonCxxModules(
generatorConfigWithDefinitionsForNative,
);

const generatedFiles = {...jsModules, ...commonCxxModules, ...androidModules};
const androidModules = generateAndroidModules(
generatorConfigWithDefinitionsForNative,
);

const generatedFiles = {
...jsModules,
...commonCxxModules,
...androidModules,
};

if (generatorOptions.verifyUnchanged) {
const existingModules: {[string]: string} = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,19 @@ NativeReactNativeFeatureFlags::NativeReactNativeFeatureFlags(
: NativeReactNativeFeatureFlagsCxxSpec(std::move(jsInvoker)) {}
${Object.entries(definitions.common)
.map(
([flagName, flagConfig]) =>
`${getCxxTypeFromDefaultValue(
flagConfig.defaultValue,
)} NativeReactNativeFeatureFlags::${flagName}(
.map(([flagName, flagConfig]) =>
flagConfig.skipNativeAPI
? `${getCxxTypeFromDefaultValue(
flagConfig.defaultValue,
)} NativeReactNativeFeatureFlags::${flagName}(
jsi::Runtime& /*runtime*/) {
// This flag is configured with \`skipNativeAPI: true\`.
// TODO(T204838867): Implement support for optional methods in C++ TM codegen and remove the method definition altogether.
return ${JSON.stringify(flagConfig.defaultValue)};
}`
: `${getCxxTypeFromDefaultValue(
flagConfig.defaultValue,
)} NativeReactNativeFeatureFlags::${flagName}(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::${flagName}();
}`,
Expand Down
46 changes: 29 additions & 17 deletions packages/react-native/scripts/featureflags/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,42 @@
export type FeatureFlagValue = boolean | number | string;

export type FeatureFlagDefinitions = {
common: FeatureFlagList,
jsOnly: FeatureFlagList,
common: CommonFeatureFlagList,
jsOnly: JsOnlyFeatureFlagList,
};

type FeatureFlagList = {
type CommonFeatureFlagList = {
[flagName: string]: {
defaultValue: FeatureFlagValue,
metadata:
| {
purpose: 'experimentation',
/**
* Aproximate date when the flag was added.
* Used to help prioritize feature flags that need to be cleaned up.
*/
dateAdded: string,
description: string,
}
| {
purpose: 'operational' | 'release',
description: string,
},
metadata: FeatureFlagMetadata,
// Indicates if this API should only be defined in JavaScript, only to
// preserve backwards compatibility with existing native code temporarily.
skipNativeAPI?: true,
},
};

type JsOnlyFeatureFlagList = {
[flagName: string]: {
defaultValue: FeatureFlagValue,
metadata: FeatureFlagMetadata,
},
};

type FeatureFlagMetadata =
| {
purpose: 'experimentation',
/**
* Aproximate date when the flag was added.
* Used to help prioritize feature flags that need to be cleaned up.
*/
dateAdded: string,
description: string,
}
| {
purpose: 'operational' | 'release',
description: string,
};

export type GeneratorConfig = {
featureFlagDefinitions: FeatureFlagDefinitions,
jsPath: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<5f8a62b797980987ce7f415fa00c0965>>
* @generated SignedSource<<b86924d0e998d2f9656f91ac7459bc3c>>
* @flow strict
*/

Expand Down Expand Up @@ -51,6 +51,7 @@ export type ReactNativeFeatureFlagsJsOnlyOverrides = OverridesFor<ReactNativeFea
export type ReactNativeFeatureFlags = {
...ReactNativeFeatureFlagsJsOnly,
commonTestFlag: Getter<boolean>,
commonTestFlagWithoutNativeImplementation: Getter<boolean>,
allowRecursiveCommitsWithSynchronousMountOnAndroid: Getter<boolean>,
batchRenderingUpdatesInEventLoop: Getter<boolean>,
completeReactInstanceCreationOnBgThreadOnAndroid: Getter<boolean>,
Expand Down Expand Up @@ -188,6 +189,10 @@ export const useRefsForTextInputState: Getter<boolean> = createJavaScriptFlagGet
* Common flag for testing. Do NOT modify.
*/
export const commonTestFlag: Getter<boolean> = createNativeFlagGetter('commonTestFlag', false);
/**
* Common flag for testing (without native implementation). Do NOT modify.
*/
export const commonTestFlagWithoutNativeImplementation: Getter<boolean> = createNativeFlagGetter('commonTestFlagWithoutNativeImplementation', false);
/**
* Adds support for recursively processing commits that mount synchronously (Android only).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2dea79097b1952f97d9e5dda86a38e58>>
* @generated SignedSource<<ab8cd7856e973a389fc400ed35e21dd8>>
* @flow strict
*/

Expand All @@ -24,6 +24,7 @@ import * as TurboModuleRegistry from '../../../../Libraries/TurboModule/TurboMod

export interface Spec extends TurboModule {
+commonTestFlag?: () => boolean;
+commonTestFlagWithoutNativeImplementation?: () => boolean;
+allowRecursiveCommitsWithSynchronousMountOnAndroid?: () => boolean;
+batchRenderingUpdatesInEventLoop?: () => boolean;
+completeReactInstanceCreationOnBgThreadOnAndroid?: () => boolean;
Expand Down

0 comments on commit 953407a

Please sign in to comment.