Skip to content

Commit c880229

Browse files
committed
flag script
1 parent 76f14eb commit c880229

File tree

2 files changed

+308
-1
lines changed

2 files changed

+308
-1
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@
130130
"download-build-for-head": "node ./scripts/release/download-experimental-build.js --commit=$(git rev-parse HEAD)",
131131
"download-build-in-codesandbox-ci": "cd scripts/release && yarn install && cd ../../ && yarn download-build-for-head || yarn build --type=node react/index react-dom/index react-dom/src/server react-dom/test-utils scheduler/index react/jsx-runtime react/jsx-dev-runtime",
132132
"check-release-dependencies": "node ./scripts/release/check-release-dependencies",
133-
"generate-inline-fizz-runtime": "node ./scripts/rollup/generate-inline-fizz-runtime.js"
133+
"generate-inline-fizz-runtime": "node ./scripts/rollup/generate-inline-fizz-runtime.js",
134+
"flags": "node ./scripts/flags/flags.js"
134135
},
135136
"resolutions": {
136137
"react-is": "npm:react-is"

scripts/flags/flags.js

+306
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
'use strict';
2+
3+
const babel = require('@babel/register');
4+
5+
babel({
6+
plugins: ['@babel/plugin-transform-modules-commonjs'],
7+
});
8+
9+
// Set the globals to string values to output them to the table.
10+
global.__VARIANT__ = 'gk';
11+
global.__EXPERIMENTAL__ = 'experimental';
12+
global.__NEXT_MAJOR__ = 'next';
13+
global.__PROFILE__ = 'profile';
14+
global.__DEV__ = 'dev';
15+
16+
// Mock the ReactNativeInternalFeatureFlags and ReactFeatureFlags modules
17+
const Module = require('module');
18+
const DynamicFeatureFlagsWWW = require('../../packages/shared/forks/ReactFeatureFlags.www-dynamic.js');
19+
const DynamicFeatureFlagsNative = require('../../packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js');
20+
21+
const originalLoad = Module._load;
22+
23+
Module._load = function (request, parent) {
24+
if (request === 'ReactNativeInternalFeatureFlags') {
25+
return DynamicFeatureFlagsNative;
26+
} else if (request === 'ReactFeatureFlags') {
27+
return DynamicFeatureFlagsWWW;
28+
}
29+
30+
return originalLoad.apply(this, arguments);
31+
};
32+
33+
const yargs = require('yargs');
34+
const argv = yargs
35+
.parserConfiguration({
36+
// Important: This option tells yargs to move all other options not
37+
// specified here into the `_` key. We use this to send all of the
38+
// Jest options that we don't use through to Jest (like --watch).
39+
'unknown-options-as-args': true,
40+
})
41+
.wrap(yargs.terminalWidth())
42+
.options({
43+
csv: {
44+
alias: 'c',
45+
describe: 'output cvs.',
46+
requiresArg: false,
47+
type: 'boolean',
48+
default: false,
49+
},
50+
diff: {
51+
alias: 'd',
52+
describe: 'output diff of two or more flags.',
53+
requiresArg: false,
54+
type: 'array',
55+
choices: [
56+
'www',
57+
'www-modern',
58+
'rn',
59+
'rn-fb',
60+
'canary',
61+
'next',
62+
'experimental',
63+
null,
64+
],
65+
default: null,
66+
},
67+
sort: {
68+
alias: 's',
69+
describe: 'sort diff by one or more flags.',
70+
requiresArg: false,
71+
type: 'array',
72+
default: 'next',
73+
choices: [
74+
'www',
75+
'www-modern',
76+
'rn',
77+
'rn-fb',
78+
'canary',
79+
'next',
80+
'experimental',
81+
],
82+
},
83+
}).argv;
84+
85+
const ReactFeatureFlags = require('../../packages/shared/ReactFeatureFlags.js');
86+
const ReactFeatureFlagsWWW = require('../../packages/shared/forks/ReactFeatureFlags.www.js');
87+
const ReactFeatureFlagsNativeFB = require('../../packages/shared/forks/ReactFeatureFlags.native-fb.js');
88+
const ReactFeatureFlagsNativeOSS = require('../../packages/shared/forks/ReactFeatureFlags.native-oss.js');
89+
90+
const allFlagsUniqueFlags = new Set([
91+
...Object.keys(ReactFeatureFlags),
92+
...Object.keys(ReactFeatureFlagsWWW),
93+
...Object.keys(ReactFeatureFlagsNativeFB),
94+
...Object.keys(ReactFeatureFlagsNativeOSS),
95+
]);
96+
97+
function getNextMajorFlagValue(flag) {
98+
const value = ReactFeatureFlags[flag];
99+
if (value === true || value === 'next') {
100+
return '✅';
101+
} else if (value === false || value === 'experimental') {
102+
return '❌';
103+
} else if (value === 'profile') {
104+
return '⏱️';
105+
} else if (value === 'dev') {
106+
return '💻';
107+
} else if (typeof value === 'number') {
108+
return value;
109+
} else {
110+
throw new Error(`Unexpected OSS Stable value ${value} for flag ${flag}`);
111+
}
112+
}
113+
114+
function getOSSCanaryFlagValue(flag) {
115+
const value = ReactFeatureFlags[flag];
116+
if (value === true) {
117+
return '✅';
118+
} else if (value === false || value === 'experimental' || value === 'next') {
119+
return '❌';
120+
} else if (value === 'profile') {
121+
return '⏱️';
122+
} else if (value === 'dev') {
123+
return '💻';
124+
} else if (typeof value === 'number') {
125+
return value;
126+
} else {
127+
throw new Error(`Unexpected OSS Canary value ${value} for flag ${flag}`);
128+
}
129+
}
130+
131+
function getOSSExperimentalFlagValue(flag) {
132+
const value = ReactFeatureFlags[flag];
133+
if (value === true || value === 'experimental') {
134+
return '✅';
135+
} else if (value === false || value === 'next') {
136+
return '❌';
137+
} else if (value === 'profile') {
138+
return '⏱️';
139+
} else if (value === 'dev') {
140+
return '💻';
141+
} else if (typeof value === 'number') {
142+
return value;
143+
} else {
144+
throw new Error(
145+
`Unexpected OSS Experimental value ${value} for flag ${flag}`
146+
);
147+
}
148+
}
149+
150+
function getWWWModernFlagValue(flag) {
151+
const value = ReactFeatureFlagsWWW[flag];
152+
if (value === true || value === 'experimental') {
153+
return '✅';
154+
} else if (value === false || value === 'next') {
155+
return '❌';
156+
} else if (value === 'profile') {
157+
return '⏱️';
158+
} else if (value === 'dev') {
159+
return '💻';
160+
} else if (value === 'gk') {
161+
return '🧪';
162+
} else if (typeof value === 'number') {
163+
return value;
164+
} else {
165+
throw new Error(`Unexpected WWW Modern value ${value} for flag ${flag}`);
166+
}
167+
}
168+
169+
function getWWWClassicFlagValue(flag) {
170+
const value = ReactFeatureFlagsWWW[flag];
171+
if (value === true) {
172+
return '✅';
173+
} else if (value === false || value === 'experimental' || value === 'next') {
174+
return '❌';
175+
} else if (value === 'profile') {
176+
return '⏱️';
177+
} else if (value === 'dev') {
178+
return '💻';
179+
} else if (value === 'gk') {
180+
return '🧪';
181+
} else if (typeof value === 'number') {
182+
return value;
183+
} else {
184+
throw new Error(`Unexpected WWW Classic value ${value} for flag ${flag}`);
185+
}
186+
}
187+
188+
function getRNOSSFlagValue(flag) {
189+
const value = ReactFeatureFlagsNativeOSS[flag];
190+
if (value === true) {
191+
return '✅';
192+
} else if (value === false || value === 'experimental' || value === 'next') {
193+
return '❌';
194+
} else if (value === 'profile') {
195+
return '⏱️';
196+
} else if (value === 'dev') {
197+
return '💻';
198+
} else if (value === 'gk') {
199+
return '🧪';
200+
} else if (typeof value === 'number') {
201+
return value;
202+
} else {
203+
throw new Error(`Unexpected RN OSS value ${value} for flag ${flag}`);
204+
}
205+
}
206+
207+
function getRNFBFlagValue(flag) {
208+
const value = ReactFeatureFlagsNativeFB[flag];
209+
if (value === true) {
210+
return '✅';
211+
} else if (value === false || value === 'experimental' || value === 'next') {
212+
return '❌';
213+
} else if (value === 'profile') {
214+
return '⏱️';
215+
} else if (value === 'dev') {
216+
return '💻';
217+
} else if (value === 'gk') {
218+
return '🧪';
219+
} else if (typeof value === 'number') {
220+
return value;
221+
} else {
222+
throw new Error(`Unexpected RN FB value ${value} for flag ${flag}`);
223+
}
224+
}
225+
226+
function argToHeader(arg) {
227+
switch (arg) {
228+
case 'www':
229+
return 'WWW Classic';
230+
case 'www-modern':
231+
return 'WWW Modern';
232+
case 'rn':
233+
return 'RN OSS';
234+
case 'rn-fb':
235+
return 'RN FB';
236+
case 'canary':
237+
return 'OSS Canary';
238+
case 'next':
239+
return 'OSS Next Major';
240+
case 'experimental':
241+
return 'OSS Experimental';
242+
default:
243+
return arg;
244+
}
245+
}
246+
const isDiff = argv.diff != null && argv.diff.length > 1;
247+
248+
const table = {};
249+
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
250+
for (const flag of allFlagsUniqueFlags) {
251+
const values = {
252+
'OSS Next Major': getNextMajorFlagValue(flag),
253+
'OSS Canary': getOSSCanaryFlagValue(flag),
254+
'OSS Experimental': getOSSExperimentalFlagValue(flag),
255+
'WWW Classic': getWWWClassicFlagValue(flag),
256+
'WWW Modern': getWWWModernFlagValue(flag),
257+
'RN FB': getRNFBFlagValue(flag),
258+
'RN OSS': getRNOSSFlagValue(flag),
259+
};
260+
261+
if (!isDiff) {
262+
table[flag] = values;
263+
continue;
264+
}
265+
266+
const subset = argv.diff.map(argToHeader).reduce((acc, key) => {
267+
if (key in values) {
268+
acc[key] = values[key];
269+
}
270+
return acc;
271+
}, {});
272+
273+
if (new Set(Object.values(subset)).size !== 1) {
274+
table[flag] = subset;
275+
}
276+
}
277+
278+
// Sort the table
279+
const sortBy = isDiff ? argv.diff.map(argToHeader) : argv.sort.map(argToHeader);
280+
let sorted;
281+
sortBy.forEach(sort => {
282+
sorted = Object.fromEntries(
283+
Object.entries(table).sort(([, rowA], [, rowB]) =>
284+
rowB[sort].toString().localeCompare(rowA[sort])
285+
)
286+
);
287+
});
288+
289+
if (argv.csv) {
290+
const fs = require('fs');
291+
const csvHeader =
292+
'Flag name, WWW Classic, RN FB, OSS Canary, OSS Experimental, WWW Modern, RN OSS\n';
293+
const csvRows = Object.keys(sorted).map(flag => {
294+
const row = sorted[flag];
295+
return `${flag}, ${row['WWW Classic']}, ${row['RN FB']}, ${row['OSS Canary']}, ${row['OSS Experimental']}, ${row['WWW Modern']}, ${row['RN OSS']}`;
296+
});
297+
fs.writeFile('./flags.csv', csvHeader + csvRows.join('\n'), function (err) {
298+
if (err) {
299+
return console.log(err);
300+
}
301+
console.log('The file was saved!');
302+
});
303+
}
304+
305+
// print table with formatting
306+
console.table(sorted);

0 commit comments

Comments
 (0)