Skip to content

Commit fabd6d3

Browse files
authored
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">
1 parent fa0efa1 commit fabd6d3

File tree

2 files changed

+169
-76
lines changed

2 files changed

+169
-76
lines changed

packages/shared/forks/ReactFeatureFlags.native-oss.js

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,98 +10,109 @@
1010
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
1111
import typeof * as ExportsType from './ReactFeatureFlags.native-oss';
1212

13+
// TODO: Align these flags with canary and delete this file once RN ships from Canary.
14+
1315
// -----------------------------------------------------------------------------
14-
// Ready for next major.
16+
// TODO for next React Native major.
1517
//
16-
// Alias __NEXT_MAJOR__ to false for easier skimming.
18+
// Alias __TODO_NEXT_RN_MAJOR__ to false for easier skimming.
1719
// -----------------------------------------------------------------------------
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__;
1935

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.
2065
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;
2175
export const enableDebugTracing = false;
2276
export const enableAsyncDebugInfo = false;
2377
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__;
2978
export const enableLegacyCache = false;
30-
export const enableCacheElement = true;
3179
export const enableFetchInstrumentation = false;
32-
export const enableFormActions = true; // Doesn't affect Native
33-
export const enableBinaryFlight = true;
34-
export const enableTaint = true;
3580
export const enablePostpone = false;
36-
export const disableJavaScriptURLs = true;
3781
export const disableCommentsAsDOMContainers = true;
3882
export const disableInputAttributeSyncing = false;
3983
export const disableIEWorkarounds = true;
4084
export const enableScopeAPI = false;
4185
export const enableCreateEventHandleAPI = false;
4286
export const enableSuspenseCallback = false;
43-
export const disableLegacyContext = true;
4487
export const enableTrustedTypesIntegration = false;
4588
export const disableTextareaChildren = false;
46-
export const disableModulePatternComponents = false;
4789
export const enableSuspenseAvoidThisFallback = false;
4890
export const enableSuspenseAvoidThisFallbackFizz = false;
49-
export const enableCPUSuspense = false;
50-
export const enableUseMemoCacheHook = false;
5191
export const enableUseEffectEventHook = false;
5292
export const enableClientRenderFallbackOnTextMismatch = true;
53-
export const enableComponentStackLocations = false;
5493
export const enableLegacyFBSupport = false;
5594
export const enableFilterEmptyStringAttributesDOM = true;
5695
export const enableGetInspectorDataForInstanceInProduction = false;
57-
export const enableRenderableContext = false;
58-
5996
export const enableRetryLaneExpiration = false;
6097
export const retryLaneExpirationMs = 5000;
6198
export const syncLaneExpirationMs = 250;
6299
export const transitionLaneExpirationMs = 5000;
63-
64100
export const enableUseRefAccessWarning = false;
65-
66101
export const disableSchedulerTimeoutInWorkLoop = false;
67102
export const enableLazyContextPropagation = false;
68103
export const enableLegacyHidden = false;
69104
export const forceConcurrentByDefaultForTesting = false;
70-
export const enableUnifiedSyncLane = true;
71105
export const allowConcurrentByDefault = false;
72-
export const enableCustomElementPropertySupport = true;
73106
export const enableNewBooleanProps = true;
74-
75-
export const consoleManagedByDevToolsDuringStrictMode = false;
76-
77107
export const enableTransitionTracing = false;
78-
79-
export const useModernStrictMode = false;
80108
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-
90109
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;
102110

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__;
105116

106117
// Flow magic to verify the exports of this file match the original version.
107118
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

scripts/flags/flags.js

Lines changed: 107 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const argv = yargs
3636
'www-modern',
3737
'rn',
3838
'rn-fb',
39+
'rn-next',
3940
'canary',
4041
'next',
4142
'experimental',
@@ -55,17 +56,18 @@ const argv = yargs
5556
'www-modern',
5657
'rn',
5758
'rn-fb',
59+
'rn-next',
5860
'canary',
5961
'next',
6062
'experimental',
6163
],
6264
},
6365
}).argv;
6466

65-
// Load ReactNativeFeatureFlags with __NEXT_MAJOR__ replace with 'next'.
67+
// Load ReactFeatureFlags with __NEXT_MAJOR__ replaced with 'next'.
6668
// 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';
6971
const file = fs.readFileSync(
7072
path.join(__dirname, '../../packages/shared/ReactFeatureFlags.js'),
7173
'utf8'
@@ -89,6 +91,41 @@ function getReactNativeFeatureFlagsMajor() {
8991
return m.exports;
9092
}
9193

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+
92129
// The RN and www Feature flag files import files that don't exist.
93130
// Mock the imports with the dynamic flag values.
94131
function mockDynamicallyFeatureFlags() {
@@ -119,15 +156,14 @@ mockDynamicallyFeatureFlags();
119156
const ReactFeatureFlags = require('../../packages/shared/ReactFeatureFlags.js');
120157
const ReactFeatureFlagsWWW = require('../../packages/shared/forks/ReactFeatureFlags.www.js');
121158
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();
124161

125162
const allFlagsUniqueFlags = Array.from(
126163
new Set([
127164
...Object.keys(ReactFeatureFlags),
128165
...Object.keys(ReactFeatureFlagsWWW),
129166
...Object.keys(ReactFeatureFlagsNativeFB),
130-
...Object.keys(ReactFeatureFlagsNativeOSS),
131167
])
132168
).sort();
133169

@@ -223,11 +259,37 @@ function getWWWClassicFlagValue(flag) {
223259
}
224260
}
225261

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+
226283
function getRNOSSFlagValue(flag) {
227-
const value = ReactFeatureFlagsNativeOSS[flag];
284+
const value = ReactNativeFeatureFlagsMajor[flag];
228285
if (value === true) {
229286
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+
) {
231293
return '❌';
232294
} else if (value === 'profile') {
233295
return '📊';
@@ -271,6 +333,8 @@ function argToHeader(arg) {
271333
return 'RN OSS';
272334
case 'rn-fb':
273335
return 'RN FB';
336+
case 'rn-next':
337+
return 'RN Next Major';
274338
case 'canary':
275339
return 'OSS Canary';
276340
case 'next':
@@ -282,20 +346,28 @@ function argToHeader(arg) {
282346
}
283347
}
284348

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+
285362
// Build the table with the value for each flag.
286363
const isDiff = argv.diff != null && argv.diff.length > 1;
287364
const table = {};
288365
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
289366
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+
}, {});
299371

300372
if (!isDiff) {
301373
table[flag] = values;
@@ -330,17 +402,18 @@ if (isDiff || argv.sort) {
330402
}
331403

332404
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) {
340413
if (err) {
341414
return console.log(err);
342415
}
343-
console.log('The file was saved!');
416+
console.log('The file was saved to ./flags.csv');
344417
});
345418
}
346419

@@ -354,3 +427,12 @@ Object.keys(sorted).forEach(key => {
354427

355428
// print table with formatting
356429
console.table(padded);
430+
console.log(`
431+
Legend:
432+
✅ On
433+
❌ Off
434+
💻 DEV
435+
📋 TODO
436+
📊 Profiling
437+
🧪 Experiment
438+
`);

0 commit comments

Comments
 (0)