diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 315c6bb082e9f..8e992b805f4d9 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -10,98 +10,109 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags'; import typeof * as ExportsType from './ReactFeatureFlags.native-oss'; +// TODO: Align these flags with canary and delete this file once RN ships from Canary. + // ----------------------------------------------------------------------------- -// Ready for next major. +// TODO for next React Native major. // -// Alias __NEXT_MAJOR__ to false for easier skimming. +// Alias __TODO_NEXT_RN_MAJOR__ to false for easier skimming. // ----------------------------------------------------------------------------- -const __NEXT_MAJOR__ = false; +const __TODO_NEXT_RN_MAJOR__ = false; +export const enableRefAsProp = __TODO_NEXT_RN_MAJOR__; +export const disableStringRefs = __TODO_NEXT_RN_MAJOR__; +export const disableLegacyMode = __TODO_NEXT_RN_MAJOR__; +export const enableBigIntSupport = __TODO_NEXT_RN_MAJOR__; +export const useModernStrictMode = __TODO_NEXT_RN_MAJOR__; +export const enableReactTestRendererWarning = __TODO_NEXT_RN_MAJOR__; +export const enableAsyncActions = __TODO_NEXT_RN_MAJOR__; +export const consoleManagedByDevToolsDuringStrictMode = __TODO_NEXT_RN_MAJOR__; +export const enableDeferRootSchedulingToMicrotask = __TODO_NEXT_RN_MAJOR__; +export const alwaysThrottleDisappearingFallbacks = __TODO_NEXT_RN_MAJOR__; +export const alwaysThrottleRetries = __TODO_NEXT_RN_MAJOR__; +export const enableInfiniteRenderLoopDetection = __TODO_NEXT_RN_MAJOR__; +export const enableComponentStackLocations = __TODO_NEXT_RN_MAJOR__; +export const disableModulePatternComponents = __TODO_NEXT_RN_MAJOR__; +// ----------------------------------------------------------------------------- +// These are ready to flip after the next React npm release (or RN switches to +// Canary, but can't flip before then because of react/renderer mismatches. +// ----------------------------------------------------------------------------- +export const enableCache = __TODO_NEXT_RN_MAJOR__; +export const enableRenderableContext = __TODO_NEXT_RN_MAJOR__; + +// ----------------------------------------------------------------------------- +// Already enabled for next React Native major. +// Hardcode these to true after the next RN major. +// +// Alias __NEXT_RN_MAJOR__ to true for easier skimming. +// ----------------------------------------------------------------------------- +const __NEXT_RN_MAJOR__ = true; +export const disableClientCache = __NEXT_RN_MAJOR__; +export const disableLegacyContext = __NEXT_RN_MAJOR__; +export const enableCacheElement = __NEXT_RN_MAJOR__; +export const enableTaint = __NEXT_RN_MAJOR__; +export const enableUnifiedSyncLane = __NEXT_RN_MAJOR__; +export const enableFizzExternalRuntime = __NEXT_RN_MAJOR__; // DOM-only +export const disableJavaScriptURLs = __NEXT_RN_MAJOR__; // DOM-only +export const enableFormActions = __NEXT_RN_MAJOR__; // DOM-only +export const enableBinaryFlight = __NEXT_RN_MAJOR__; // DOM-only +export const enableCustomElementPropertySupport = __NEXT_RN_MAJOR__; // DOM-only +export const enableServerComponentKeys = __NEXT_RN_MAJOR__; +export const enableServerComponentLogs = __NEXT_RN_MAJOR__; + +// DEV-only but enabled in the next RN Major. +// Not supported by flag script to avoid the special case. export const debugRenderPhaseSideEffectsForStrictMode = __DEV__; + +// TODO: decide on React 19 +export const enableUseMemoCacheHook = false; +export const enableUseDeferredValueInitialArg = __EXPERIMENTAL__; + +// ----------------------------------------------------------------------------- +// All other flags +// ----------------------------------------------------------------------------- +export const enableCPUSuspense = false; export const enableDebugTracing = false; export const enableAsyncDebugInfo = false; export const enableSchedulingProfiler = false; -export const enableProfilerTimer = __PROFILE__; -export const enableProfilerCommitHooks = __PROFILE__; -export const enableProfilerNestedUpdatePhase = __PROFILE__; -export const enableUpdaterTracking = __PROFILE__; -export const enableCache = __NEXT_MAJOR__; export const enableLegacyCache = false; -export const enableCacheElement = true; export const enableFetchInstrumentation = false; -export const enableFormActions = true; // Doesn't affect Native -export const enableBinaryFlight = true; -export const enableTaint = true; export const enablePostpone = false; -export const disableJavaScriptURLs = true; export const disableCommentsAsDOMContainers = true; export const disableInputAttributeSyncing = false; export const disableIEWorkarounds = true; export const enableScopeAPI = false; export const enableCreateEventHandleAPI = false; export const enableSuspenseCallback = false; -export const disableLegacyContext = true; export const enableTrustedTypesIntegration = false; export const disableTextareaChildren = false; -export const disableModulePatternComponents = false; export const enableSuspenseAvoidThisFallback = false; export const enableSuspenseAvoidThisFallbackFizz = false; -export const enableCPUSuspense = false; -export const enableUseMemoCacheHook = false; export const enableUseEffectEventHook = false; export const enableClientRenderFallbackOnTextMismatch = true; -export const enableComponentStackLocations = false; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = true; export const enableGetInspectorDataForInstanceInProduction = false; -export const enableRenderableContext = false; - export const enableRetryLaneExpiration = false; export const retryLaneExpirationMs = 5000; export const syncLaneExpirationMs = 250; export const transitionLaneExpirationMs = 5000; - export const enableUseRefAccessWarning = false; - export const disableSchedulerTimeoutInWorkLoop = false; export const enableLazyContextPropagation = false; export const enableLegacyHidden = false; export const forceConcurrentByDefaultForTesting = false; -export const enableUnifiedSyncLane = true; export const allowConcurrentByDefault = false; -export const enableCustomElementPropertySupport = true; export const enableNewBooleanProps = true; - -export const consoleManagedByDevToolsDuringStrictMode = false; - export const enableTransitionTracing = false; - -export const useModernStrictMode = false; export const enableDO_NOT_USE_disableStrictPassiveEffect = false; -export const enableFizzExternalRuntime = true; -export const enableDeferRootSchedulingToMicrotask = false; -export const enableInfiniteRenderLoopDetection = false; - -export const enableAsyncActions = false; - -export const alwaysThrottleDisappearingFallbacks = false; -export const alwaysThrottleRetries = false; - export const passChildrenWhenCloningPersistedNodes = false; -export const enableUseDeferredValueInitialArg = __EXPERIMENTAL__; -export const disableClientCache = true; - -export const enableServerComponentKeys = true; -export const enableServerComponentLogs = true; - -// TODO: Should turn this on in next "major" RN release. -export const enableRefAsProp = false; -export const disableStringRefs = false; - -export const enableReactTestRendererWarning = false; -export const enableBigIntSupport = false; -export const disableLegacyMode = false; +// Profiling Only +export const enableProfilerTimer = __PROFILE__; +export const enableProfilerCommitHooks = __PROFILE__; +export const enableProfilerNestedUpdatePhase = __PROFILE__; +export const enableUpdaterTracking = __PROFILE__; // Flow magic to verify the exports of this file match the original version. ((((null: any): ExportsType): FeatureFlagsType): ExportsType); diff --git a/scripts/flags/flags.js b/scripts/flags/flags.js index 2b4fced638ff6..50d263cd498ef 100644 --- a/scripts/flags/flags.js +++ b/scripts/flags/flags.js @@ -36,6 +36,7 @@ const argv = yargs 'www-modern', 'rn', 'rn-fb', + 'rn-next', 'canary', 'next', 'experimental', @@ -55,6 +56,7 @@ const argv = yargs 'www-modern', 'rn', 'rn-fb', + 'rn-next', 'canary', 'next', 'experimental', @@ -62,10 +64,10 @@ const argv = yargs }, }).argv; -// Load ReactNativeFeatureFlags with __NEXT_MAJOR__ replace with 'next'. +// Load ReactFeatureFlags with __NEXT_MAJOR__ replaced with 'next'. // We need to do string replace, since the __NEXT_MAJOR__ is assigned to __EXPERIMENTAL__. -function getReactNativeFeatureFlagsMajor() { - const virtualName = 'ReactNativeFeatureFlagsMajor.js'; +function getReactFeatureFlagsMajor() { + const virtualName = 'ReactFeatureFlagsMajor.js'; const file = fs.readFileSync( path.join(__dirname, '../../packages/shared/ReactFeatureFlags.js'), 'utf8' @@ -89,6 +91,41 @@ function getReactNativeFeatureFlagsMajor() { return m.exports; } +// Load RN ReactFeatureFlags with __NEXT_RN_MAJOR__ replaced with 'next'. +// We need to do string replace, since the __NEXT_RN_MAJOR__ is assigned to false. +function getReactNativeFeatureFlagsMajor() { + const virtualName = 'ReactNativeFeatureFlagsMajor.js'; + const file = fs.readFileSync( + path.join( + __dirname, + '../../packages/shared/forks/ReactFeatureFlags.native-oss.js' + ), + 'utf8' + ); + const fileContent = transformSync( + file + .replace( + 'const __NEXT_RN_MAJOR__ = true;', + 'const __NEXT_RN_MAJOR__ = "next";' + ) + .replace( + 'const __TODO_NEXT_RN_MAJOR__ = false;', + 'const __TODO_NEXT_RN_MAJOR__ = "next-todo";' + ), + { + plugins: ['@babel/plugin-transform-modules-commonjs'], + } + ).code; + + const parent = module.parent; + const m = new Module(virtualName, parent); + m.filename = virtualName; + + m._compile(fileContent, virtualName); + + return m.exports; +} + // The RN and www Feature flag files import files that don't exist. // Mock the imports with the dynamic flag values. function mockDynamicallyFeatureFlags() { @@ -119,15 +156,14 @@ mockDynamicallyFeatureFlags(); const ReactFeatureFlags = require('../../packages/shared/ReactFeatureFlags.js'); const ReactFeatureFlagsWWW = require('../../packages/shared/forks/ReactFeatureFlags.www.js'); const ReactFeatureFlagsNativeFB = require('../../packages/shared/forks/ReactFeatureFlags.native-fb.js'); -const ReactFeatureFlagsNativeOSS = require('../../packages/shared/forks/ReactFeatureFlags.native-oss.js'); -const ReactFeatureFlagsMajor = getReactNativeFeatureFlagsMajor(); +const ReactFeatureFlagsMajor = getReactFeatureFlagsMajor(); +const ReactNativeFeatureFlagsMajor = getReactNativeFeatureFlagsMajor(); const allFlagsUniqueFlags = Array.from( new Set([ ...Object.keys(ReactFeatureFlags), ...Object.keys(ReactFeatureFlagsWWW), ...Object.keys(ReactFeatureFlagsNativeFB), - ...Object.keys(ReactFeatureFlagsNativeOSS), ]) ).sort(); @@ -223,11 +259,37 @@ function getWWWClassicFlagValue(flag) { } } +function getRNNextMajorFlagValue(flag) { + const value = ReactNativeFeatureFlagsMajor[flag]; + if (value === true || value === 'next') { + return 'โœ…'; + } else if (value === 'next-todo') { + return '๐Ÿ“‹'; + } else if (value === false || value === 'experimental') { + return 'โŒ'; + } else if (value === 'profile') { + return '๐Ÿ“Š'; + } else if (value === 'dev') { + return '๐Ÿ’ป'; + } else if (value === 'gk') { + return '๐Ÿงช'; + } else if (typeof value === 'number') { + return value; + } else { + throw new Error(`Unexpected RN OSS value ${value} for flag ${flag}`); + } +} + function getRNOSSFlagValue(flag) { - const value = ReactFeatureFlagsNativeOSS[flag]; + const value = ReactNativeFeatureFlagsMajor[flag]; if (value === true) { return 'โœ…'; - } else if (value === false || value === 'experimental' || value === 'next') { + } else if ( + value === false || + value === 'experimental' || + value === 'next' || + value === 'next-todo' + ) { return 'โŒ'; } else if (value === 'profile') { return '๐Ÿ“Š'; @@ -271,6 +333,8 @@ function argToHeader(arg) { return 'RN OSS'; case 'rn-fb': return 'RN FB'; + case 'rn-next': + return 'RN Next Major'; case 'canary': return 'OSS Canary'; case 'next': @@ -282,20 +346,28 @@ function argToHeader(arg) { } } +const FLAG_CONFIG = { + 'OSS Next Major': getNextMajorFlagValue, + 'OSS Canary': getOSSCanaryFlagValue, + 'OSS Experimental': getOSSExperimentalFlagValue, + 'WWW Classic': getWWWClassicFlagValue, + 'WWW Modern': getWWWModernFlagValue, + 'RN FB': getRNFBFlagValue, + 'RN OSS': getRNOSSFlagValue, + 'RN Next Major': getRNNextMajorFlagValue, +}; + +const FLAG_COLUMNS = Object.keys(FLAG_CONFIG); + // Build the table with the value for each flag. const isDiff = argv.diff != null && argv.diff.length > 1; const table = {}; // eslint-disable-next-line no-for-of-loops/no-for-of-loops for (const flag of allFlagsUniqueFlags) { - const values = { - 'OSS Next Major': getNextMajorFlagValue(flag), - 'OSS Canary': getOSSCanaryFlagValue(flag), - 'OSS Experimental': getOSSExperimentalFlagValue(flag), - 'WWW Classic': getWWWClassicFlagValue(flag), - 'WWW Modern': getWWWModernFlagValue(flag), - 'RN FB': getRNFBFlagValue(flag), - 'RN OSS': getRNOSSFlagValue(flag), - }; + const values = FLAG_COLUMNS.reduce((acc, key) => { + acc[key] = FLAG_CONFIG[key](flag); + return acc; + }, {}); if (!isDiff) { table[flag] = values; @@ -330,17 +402,18 @@ if (isDiff || argv.sort) { } if (argv.csv) { - const csvHeader = - 'Flag name, WWW Classic, RN FB, OSS Canary, OSS Experimental, WWW Modern, RN OSS\n'; - const csvRows = Object.keys(sorted).map(flag => { - const row = sorted[flag]; - return `${flag}, ${row['WWW Classic']}, ${row['RN FB']}, ${row['OSS Canary']}, ${row['OSS Experimental']}, ${row['WWW Modern']}, ${row['RN OSS']}`; - }); - fs.writeFile('./flags.csv', csvHeader + csvRows.join('\n'), function (err) { + const csvRows = [ + `Flag name, ${FLAG_COLUMNS.join(', ')}`, + ...Object.keys(table).map(flag => { + const row = sorted[flag]; + return `${flag}, ${FLAG_COLUMNS.map(col => row[col]).join(', ')}`; + }), + ]; + fs.writeFile('./flags.csv', csvRows.join('\n'), function (err) { if (err) { return console.log(err); } - console.log('The file was saved!'); + console.log('The file was saved to ./flags.csv'); }); } @@ -354,3 +427,12 @@ Object.keys(sorted).forEach(key => { // print table with formatting console.table(padded); +console.log(` +Legend: + โœ… On + โŒ Off + ๐Ÿ’ป DEV + ๐Ÿ“‹ TODO + ๐Ÿ“Š Profiling + ๐Ÿงช Experiment +`);