@samitouri / QOS-React-1 / commits / fabd6d3a64

Better NEXT_MAJOR support for RN flags (#28583)

The `__NEXT_MAJOR__` value in the RN flags doesn't make sense because: a) The flags are for the next RN major, since it only impacts the renderers b) The flags are off, so they're not currently in the next major, they need enabled c) the flag script didn't support it This PR adds two aliases to the RN file: - `__TODO_NEXT_RN_MAJOR__`: flags that need enabled before the next RN major. - `__NEXT_RN_MAJOR__`: flags that have been enabled since the last RN major. These values will need to be manually kept up to date when we cut a RN version, but once RN switches to the canary build and aligns all the flags, this entire file can be deleted. ## Script screen Notably, I added a TODO value and a legend that prints at the end of the script: <img width="1078" alt="Screenshot 2024-03-18 at 8 11 27 PM" src="https://github.com/facebook/react/assets/2440089/14da9066-f77d-437f-8188-830a31a843c5">

Ricky committed Mar 22, 2024 at 13:02 UTC fabd6d3a64aa20aca940142a29a14a82d1cefd31
2 files changed +169 -76
packages/shared/forks/ReactFeatureFlags.native-oss.js
+62 -51
@@ -10,98 +10,109 @@
10 import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
11 import typeof * as ExportsType from './ReactFeatureFlags.native-oss';
12
13 +// TODO: Align these flags with canary and delete this file once RN ships from Canary.
14 +
15 // -----------------------------------------------------------------------------
14 -// Ready for next major.
16 +// TODO for next React Native major.
17 //
16 -// Alias __NEXT_MAJOR__ to false for easier skimming.
18 +// Alias __TODO_NEXT_RN_MAJOR__ to false for easier skimming.
19 // -----------------------------------------------------------------------------
18 -const __NEXT_MAJOR__ = false;
20 +const __TODO_NEXT_RN_MAJOR__ = false;
21 +export const enableRefAsProp = __TODO_NEXT_RN_MAJOR__;
22 +export const disableStringRefs = __TODO_NEXT_RN_MAJOR__;
23 +export const disableLegacyMode = __TODO_NEXT_RN_MAJOR__;
24 +export const enableBigIntSupport = __TODO_NEXT_RN_MAJOR__;
25 +export const useModernStrictMode = __TODO_NEXT_RN_MAJOR__;
26 +export const enableReactTestRendererWarning = __TODO_NEXT_RN_MAJOR__;
27 +export const enableAsyncActions = __TODO_NEXT_RN_MAJOR__;
28 +export const consoleManagedByDevToolsDuringStrictMode = __TODO_NEXT_RN_MAJOR__;
29 +export const enableDeferRootSchedulingToMicrotask = __TODO_NEXT_RN_MAJOR__;
30 +export const alwaysThrottleDisappearingFallbacks = __TODO_NEXT_RN_MAJOR__;
31 +export const alwaysThrottleRetries = __TODO_NEXT_RN_MAJOR__;
32 +export const enableInfiniteRenderLoopDetection = __TODO_NEXT_RN_MAJOR__;
33 +export const enableComponentStackLocations = __TODO_NEXT_RN_MAJOR__;
34 +export const disableModulePatternComponents = __TODO_NEXT_RN_MAJOR__;
35
36 +// -----------------------------------------------------------------------------
37 +// These are ready to flip after the next React npm release (or RN switches to
38 +// Canary, but can't flip before then because of react/renderer mismatches.
39 +// -----------------------------------------------------------------------------
40 +export const enableCache = __TODO_NEXT_RN_MAJOR__;
41 +export const enableRenderableContext = __TODO_NEXT_RN_MAJOR__;
42 +
43 +// -----------------------------------------------------------------------------
44 +// Already enabled for next React Native major.
45 +// Hardcode these to true after the next RN major.
46 +//
47 +// Alias __NEXT_RN_MAJOR__ to true for easier skimming.
48 +// -----------------------------------------------------------------------------
49 +const __NEXT_RN_MAJOR__ = true;
50 +export const disableClientCache = __NEXT_RN_MAJOR__;
51 +export const disableLegacyContext = __NEXT_RN_MAJOR__;
52 +export const enableCacheElement = __NEXT_RN_MAJOR__;
53 +export const enableTaint = __NEXT_RN_MAJOR__;
54 +export const enableUnifiedSyncLane = __NEXT_RN_MAJOR__;
55 +export const enableFizzExternalRuntime = __NEXT_RN_MAJOR__; // DOM-only
56 +export const disableJavaScriptURLs = __NEXT_RN_MAJOR__; // DOM-only
57 +export const enableFormActions = __NEXT_RN_MAJOR__; // DOM-only
58 +export const enableBinaryFlight = __NEXT_RN_MAJOR__; // DOM-only
59 +export const enableCustomElementPropertySupport = __NEXT_RN_MAJOR__; // DOM-only
60 +export const enableServerComponentKeys = __NEXT_RN_MAJOR__;
61 +export const enableServerComponentLogs = __NEXT_RN_MAJOR__;
62 +
63 +// DEV-only but enabled in the next RN Major.
64 +// Not supported by flag script to avoid the special case.
65 export const debugRenderPhaseSideEffectsForStrictMode = __DEV__;
66 +
67 +// TODO: decide on React 19
68 +export const enableUseMemoCacheHook = false;
69 +export const enableUseDeferredValueInitialArg = __EXPERIMENTAL__;
70 +
71 +// -----------------------------------------------------------------------------
72 +// All other flags
73 +// -----------------------------------------------------------------------------
74 +export const enableCPUSuspense = false;
75 export const enableDebugTracing = false;
76 export const enableAsyncDebugInfo = false;
77 export const enableSchedulingProfiler = false;
24 -export const enableProfilerTimer = __PROFILE__;
25 -export const enableProfilerCommitHooks = __PROFILE__;
26 -export const enableProfilerNestedUpdatePhase = __PROFILE__;
27 -export const enableUpdaterTracking = __PROFILE__;
28 -export const enableCache = __NEXT_MAJOR__;
78 export const enableLegacyCache = false;
30 -export const enableCacheElement = true;
79 export const enableFetchInstrumentation = false;
32 -export const enableFormActions = true; // Doesn't affect Native
33 -export const enableBinaryFlight = true;
34 -export const enableTaint = true;
80 export const enablePostpone = false;
36 -export const disableJavaScriptURLs = true;
81 export const disableCommentsAsDOMContainers = true;
82 export const disableInputAttributeSyncing = false;
83 export const disableIEWorkarounds = true;
84 export const enableScopeAPI = false;
85 export const enableCreateEventHandleAPI = false;
86 export const enableSuspenseCallback = false;
43 -export const disableLegacyContext = true;
87 export const enableTrustedTypesIntegration = false;
88 export const disableTextareaChildren = false;
46 -export const disableModulePatternComponents = false;
89 export const enableSuspenseAvoidThisFallback = false;
90 export const enableSuspenseAvoidThisFallbackFizz = false;
49 -export const enableCPUSuspense = false;
50 -export const enableUseMemoCacheHook = false;
91 export const enableUseEffectEventHook = false;
92 export const enableClientRenderFallbackOnTextMismatch = true;
53 -export const enableComponentStackLocations = false;
93 export const enableLegacyFBSupport = false;
94 export const enableFilterEmptyStringAttributesDOM = true;
95 export const enableGetInspectorDataForInstanceInProduction = false;
57 -export const enableRenderableContext = false;
58 -
96 export const enableRetryLaneExpiration = false;
97 export const retryLaneExpirationMs = 5000;
98 export const syncLaneExpirationMs = 250;
99 export const transitionLaneExpirationMs = 5000;
63 -
100 export const enableUseRefAccessWarning = false;
65 -
101 export const disableSchedulerTimeoutInWorkLoop = false;
102 export const enableLazyContextPropagation = false;
103 export const enableLegacyHidden = false;
104 export const forceConcurrentByDefaultForTesting = false;
70 -export const enableUnifiedSyncLane = true;
105 export const allowConcurrentByDefault = false;
72 -export const enableCustomElementPropertySupport = true;
106 export const enableNewBooleanProps = true;
74 -
75 -export const consoleManagedByDevToolsDuringStrictMode = false;
76 -
107 export const enableTransitionTracing = false;
78 -
79 -export const useModernStrictMode = false;
108 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
81 -export const enableFizzExternalRuntime = true;
82 -export const enableDeferRootSchedulingToMicrotask = false;
83 -export const enableInfiniteRenderLoopDetection = false;
84 -
85 -export const enableAsyncActions = false;
86 -
87 -export const alwaysThrottleDisappearingFallbacks = false;
88 -export const alwaysThrottleRetries = false;
89 -
109 export const passChildrenWhenCloningPersistedNodes = false;
91 -export const enableUseDeferredValueInitialArg = __EXPERIMENTAL__;
92 -export const disableClientCache = true;
93 -
94 -export const enableServerComponentKeys = true;
95 -export const enableServerComponentLogs = true;
96 -
97 -// TODO: Should turn this on in next "major" RN release.
98 -export const enableRefAsProp = false;
99 -export const disableStringRefs = false;
100 -
101 -export const enableReactTestRendererWarning = false;
110
103 -export const enableBigIntSupport = false;
104 -export const disableLegacyMode = false;
111 +// Profiling Only
112 +export const enableProfilerTimer = __PROFILE__;
113 +export const enableProfilerCommitHooks = __PROFILE__;
114 +export const enableProfilerNestedUpdatePhase = __PROFILE__;
115 +export const enableUpdaterTracking = __PROFILE__;
116
117 // Flow magic to verify the exports of this file match the original version.
118 ((((null: any): ExportsType): FeatureFlagsType): ExportsType);
scripts/flags/flags.js
+107 -25
@@ -36,6 +36,7 @@ const argv = yargs
36 'www-modern',
37 'rn',
38 'rn-fb',
39 + 'rn-next',
40 'canary',
41 'next',
42 'experimental',
@@ -55,6 +56,7 @@ const argv = yargs
56 'www-modern',
57 'rn',
58 'rn-fb',
59 + 'rn-next',
60 'canary',
61 'next',
62 'experimental',
@@ -62,10 +64,10 @@ const argv = yargs
64 },
65 }).argv;
66
65 -// Load ReactNativeFeatureFlags with __NEXT_MAJOR__ replace with 'next'.
67 +// Load ReactFeatureFlags with __NEXT_MAJOR__ replaced with 'next'.
68 // We need to do string replace, since the __NEXT_MAJOR__ is assigned to __EXPERIMENTAL__.
67 -function getReactNativeFeatureFlagsMajor() {
68 - const virtualName = 'ReactNativeFeatureFlagsMajor.js';
69 +function getReactFeatureFlagsMajor() {
70 + const virtualName = 'ReactFeatureFlagsMajor.js';
71 const file = fs.readFileSync(
72 path.join(__dirname, '../../packages/shared/ReactFeatureFlags.js'),
73 'utf8'
@@ -89,6 +91,41 @@ function getReactNativeFeatureFlagsMajor() {
91 return m.exports;
92 }
93
94 +// Load RN ReactFeatureFlags with __NEXT_RN_MAJOR__ replaced with 'next'.
95 +// We need to do string replace, since the __NEXT_RN_MAJOR__ is assigned to false.
96 +function getReactNativeFeatureFlagsMajor() {
97 + const virtualName = 'ReactNativeFeatureFlagsMajor.js';
98 + const file = fs.readFileSync(
99 + path.join(
100 + __dirname,
101 + '../../packages/shared/forks/ReactFeatureFlags.native-oss.js'
102 + ),
103 + 'utf8'
104 + );
105 + const fileContent = transformSync(
106 + file
107 + .replace(
108 + 'const __NEXT_RN_MAJOR__ = true;',
109 + 'const __NEXT_RN_MAJOR__ = "next";'
110 + )
111 + .replace(
112 + 'const __TODO_NEXT_RN_MAJOR__ = false;',
113 + 'const __TODO_NEXT_RN_MAJOR__ = "next-todo";'
114 + ),
115 + {
116 + plugins: ['@babel/plugin-transform-modules-commonjs'],
117 + }
118 + ).code;
119 +
120 + const parent = module.parent;
121 + const m = new Module(virtualName, parent);
122 + m.filename = virtualName;
123 +
124 + m._compile(fileContent, virtualName);
125 +
126 + return m.exports;
127 +}
128 +
129 // The RN and www Feature flag files import files that don't exist.
130 // Mock the imports with the dynamic flag values.
131 function mockDynamicallyFeatureFlags() {
@@ -119,15 +156,14 @@ mockDynamicallyFeatureFlags();
156 const ReactFeatureFlags = require('../../packages/shared/ReactFeatureFlags.js');
157 const ReactFeatureFlagsWWW = require('../../packages/shared/forks/ReactFeatureFlags.www.js');
158 const ReactFeatureFlagsNativeFB = require('../../packages/shared/forks/ReactFeatureFlags.native-fb.js');
122 -const ReactFeatureFlagsNativeOSS = require('../../packages/shared/forks/ReactFeatureFlags.native-oss.js');
123 -const ReactFeatureFlagsMajor = getReactNativeFeatureFlagsMajor();
159 +const ReactFeatureFlagsMajor = getReactFeatureFlagsMajor();
160 +const ReactNativeFeatureFlagsMajor = getReactNativeFeatureFlagsMajor();
161
162 const allFlagsUniqueFlags = Array.from(
163 new Set([
164 ...Object.keys(ReactFeatureFlags),
165 ...Object.keys(ReactFeatureFlagsWWW),
166 ...Object.keys(ReactFeatureFlagsNativeFB),
130 - ...Object.keys(ReactFeatureFlagsNativeOSS),
167 ])
168 ).sort();
169
@@ -223,11 +259,37 @@ function getWWWClassicFlagValue(flag) {
259 }
260 }
261
262 +function getRNNextMajorFlagValue(flag) {
263 + const value = ReactNativeFeatureFlagsMajor[flag];
264 + if (value === true || value === 'next') {
265 + return '✅';
266 + } else if (value === 'next-todo') {
267 + return '📋';
268 + } else if (value === false || value === 'experimental') {
269 + return '❌';
270 + } else if (value === 'profile') {
271 + return '📊';
272 + } else if (value === 'dev') {
273 + return '💻';
274 + } else if (value === 'gk') {
275 + return '🧪';
276 + } else if (typeof value === 'number') {
277 + return value;
278 + } else {
279 + throw new Error(`Unexpected RN OSS value ${value} for flag ${flag}`);
280 + }
281 +}
282 +
283 function getRNOSSFlagValue(flag) {
227 - const value = ReactFeatureFlagsNativeOSS[flag];
284 + const value = ReactNativeFeatureFlagsMajor[flag];
285 if (value === true) {
286 return '✅';
230 - } else if (value === false || value === 'experimental' || value === 'next') {
287 + } else if (
288 + value === false ||
289 + value === 'experimental' ||
290 + value === 'next' ||
291 + value === 'next-todo'
292 + ) {
293 return '❌';
294 } else if (value === 'profile') {
295 return '📊';
@@ -271,6 +333,8 @@ function argToHeader(arg) {
333 return 'RN OSS';
334 case 'rn-fb':
335 return 'RN FB';
336 + case 'rn-next':
337 + return 'RN Next Major';
338 case 'canary':
339 return 'OSS Canary';
340 case 'next':
@@ -282,20 +346,28 @@ function argToHeader(arg) {
346 }
347 }
348
349 +const FLAG_CONFIG = {
350 + 'OSS Next Major': getNextMajorFlagValue,
351 + 'OSS Canary': getOSSCanaryFlagValue,
352 + 'OSS Experimental': getOSSExperimentalFlagValue,
353 + 'WWW Classic': getWWWClassicFlagValue,
354 + 'WWW Modern': getWWWModernFlagValue,
355 + 'RN FB': getRNFBFlagValue,
356 + 'RN OSS': getRNOSSFlagValue,
357 + 'RN Next Major': getRNNextMajorFlagValue,
358 +};
359 +
360 +const FLAG_COLUMNS = Object.keys(FLAG_CONFIG);
361 +
362 // Build the table with the value for each flag.
363 const isDiff = argv.diff != null && argv.diff.length > 1;
364 const table = {};
365 // eslint-disable-next-line no-for-of-loops/no-for-of-loops
366 for (const flag of allFlagsUniqueFlags) {
290 - const values = {
291 - 'OSS Next Major': getNextMajorFlagValue(flag),
292 - 'OSS Canary': getOSSCanaryFlagValue(flag),
293 - 'OSS Experimental': getOSSExperimentalFlagValue(flag),
294 - 'WWW Classic': getWWWClassicFlagValue(flag),
295 - 'WWW Modern': getWWWModernFlagValue(flag),
296 - 'RN FB': getRNFBFlagValue(flag),
297 - 'RN OSS': getRNOSSFlagValue(flag),
298 - };
367 + const values = FLAG_COLUMNS.reduce((acc, key) => {
368 + acc[key] = FLAG_CONFIG[key](flag);
369 + return acc;
370 + }, {});
371
372 if (!isDiff) {
373 table[flag] = values;
@@ -330,17 +402,18 @@ if (isDiff || argv.sort) {
402 }
403
404 if (argv.csv) {
333 - const csvHeader =
334 - 'Flag name, WWW Classic, RN FB, OSS Canary, OSS Experimental, WWW Modern, RN OSS\n';
335 - const csvRows = Object.keys(sorted).map(flag => {
336 - const row = sorted[flag];
337 - return `${flag}, ${row['WWW Classic']}, ${row['RN FB']}, ${row['OSS Canary']}, ${row['OSS Experimental']}, ${row['WWW Modern']}, ${row['RN OSS']}`;
338 - });
339 - fs.writeFile('./flags.csv', csvHeader + csvRows.join('\n'), function (err) {
405 + const csvRows = [
406 + `Flag name, ${FLAG_COLUMNS.join(', ')}`,
407 + ...Object.keys(table).map(flag => {
408 + const row = sorted[flag];
409 + return `${flag}, ${FLAG_COLUMNS.map(col => row[col]).join(', ')}`;
410 + }),
411 + ];
412 + fs.writeFile('./flags.csv', csvRows.join('\n'), function (err) {
413 if (err) {
414 return console.log(err);
415 }
343 - console.log('The file was saved!');
416 + console.log('The file was saved to ./flags.csv');
417 });
418 }
419
@@ -354,3 +427,12 @@ Object.keys(sorted).forEach(key => {
427
428 // print table with formatting
429 console.table(padded);
430 +console.log(`
431 +Legend:
432 + ✅ On
433 + ❌ Off
434 + 💻 DEV
435 + 📋 TODO
436 + 📊 Profiling
437 + 🧪 Experiment
438 +`);