Use strict equality (===) to prevent type coercion bugs
if (request === 'ReactNativeInternalFeatureFlags') {
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 */7'use strict';89const babel = require('@babel/register');10const {transformSync} = require('@babel/core');11const Module = require('module');12const path = require('path');13const fs = require('fs');14babel({15 plugins: [16 'babel-plugin-syntax-hermes-parser',17 '@babel/plugin-transform-modules-commonjs',18 ],19});2021const yargs = require('yargs');22const argv = yargs23 .parserConfiguration({24 // Important: This option tells yargs to move all other options not25 // specified here into the `_` key. We use this to send all of the26 // Jest options that we don't use through to Jest (like --watch).27 'unknown-options-as-args': true,28 })29 .wrap(yargs.terminalWidth())30 .options({31 csv: {32 alias: 'c',33 describe: 'output cvs.',34 requiresArg: false,35 type: 'boolean',36 default: false,37 },38 diff: {39 alias: 'd',40 describe: 'output diff of two or more flags.',41 requiresArg: false,42 type: 'array',43 choices: [44 'www',45 'www-modern',46 'rn',47 'rn-fb',48 'rn-next',49 'canary',50 'next',51 'experimental',52 null,53 ],54 default: null,55 },56 sort: {57 alias: 's',58 describe: 'sort diff by one or more flags.',59 requiresArg: false,60 type: 'string',61 default: 'flag',62 choices: [63 'flag',64 'www',65 'www-modern',66 'rn',67 'rn-fb',68 'rn-next',69 'canary',70 'next',71 'experimental',72 ],73 },74 cleanup: {75 describe: 'output flags by cleanup category.',76 requiresArg: false,77 type: 'boolean',78 default: false,79 },80 }).argv;8182// Load ReactFeatureFlags with __NEXT_MAJOR__ replaced with 'next'.83// We need to do string replace, since the __NEXT_MAJOR__ is assigned to __EXPERIMENTAL__.84function getReactFeatureFlagsMajor() {85 const virtualName = 'ReactFeatureFlagsMajor.js';86 const file = fs.readFileSync(87 path.join(__dirname, '../../packages/shared/ReactFeatureFlags.js'),88 'utf8'89 );90 const fileContent = transformSync(91 file.replace(92 'const __NEXT_MAJOR__ = __EXPERIMENTAL__;',93 'const __NEXT_MAJOR__ = "next";'94 ),95 {96 plugins: [97 'babel-plugin-syntax-hermes-parser',98 '@babel/plugin-transform-modules-commonjs',99 ],100 }101 ).code;102103 const parent = module.parent;104 const m = new Module(virtualName, parent);105 m.filename = virtualName;106107 m._compile(fileContent, virtualName);108109 return m.exports;110}111112// Load RN ReactFeatureFlags with __NEXT_RN_MAJOR__ replaced with 'next'.113// We need to do string replace, since the __NEXT_RN_MAJOR__ is assigned to false.114function getReactNativeFeatureFlagsMajor() {115 const virtualName = 'ReactNativeFeatureFlagsMajor.js';116 const file = fs.readFileSync(117 path.join(118 __dirname,119 '../../packages/shared/forks/ReactFeatureFlags.native-oss.js'120 ),121 'utf8'122 );123 const fileContent = transformSync(124 file125 .replace(126 'const __NEXT_RN_MAJOR__ = true;',127 'const __NEXT_RN_MAJOR__ = "next";'128 )129 .replace(130 'const __TODO_NEXT_RN_MAJOR__ = false;',131 'const __TODO_NEXT_RN_MAJOR__ = "next-todo";'132 ),133 {134 plugins: [135 'babel-plugin-syntax-hermes-parser',136 '@babel/plugin-transform-modules-commonjs',137 ],138 }139 ).code;140141 const parent = module.parent;142 const m = new Module(virtualName, parent);143 m.filename = virtualName;144145 m._compile(fileContent, virtualName);146147 return m.exports;148}149150// The RN and www Feature flag files import files that don't exist.151// Mock the imports with the dynamic flag values.152function mockDynamicallyFeatureFlags() {153 // Mock the ReactNativeInternalFeatureFlags and ReactFeatureFlags modules154 const DynamicFeatureFlagsWWW = require('../../packages/shared/forks/ReactFeatureFlags.www-dynamic.js');155 const DynamicFeatureFlagsNative = require('../../packages/shared/forks/ReactFeatureFlags.native-fb-dynamic.js');156157 const originalLoad = Module._load;158159 Module._load = function (request, parent) {160 if (request === 'ReactNativeInternalFeatureFlags') {161 return DynamicFeatureFlagsNative;162 } else if (request === 'ReactFeatureFlags') {163 return DynamicFeatureFlagsWWW;164 }165166 return originalLoad.apply(this, arguments);167 };168}169// Set the globals to string values to output them to the table.170global.__VARIANT__ = 'gk';171global.__PROFILE__ = 'profile';172global.__DEV__ = 'dev';173global.__EXPERIMENTAL__ = 'experimental';174175// Load all the feature flag files.176mockDynamicallyFeatureFlags();177const ReactFeatureFlags = require('../../packages/shared/ReactFeatureFlags.js');178const ReactFeatureFlagsWWW = require('../../packages/shared/forks/ReactFeatureFlags.www.js');179const ReactFeatureFlagsNativeFB = require('../../packages/shared/forks/ReactFeatureFlags.native-fb.js');180const ReactFeatureFlagsMajor = getReactFeatureFlagsMajor();181const ReactNativeFeatureFlagsMajor = getReactNativeFeatureFlagsMajor();182183const allFlagsUniqueFlags = Array.from(184 new Set([185 ...Object.keys(ReactFeatureFlags),186 ...Object.keys(ReactFeatureFlagsWWW),187 ...Object.keys(ReactFeatureFlagsNativeFB),188 ])189).sort();190191// These functions are the rules for what each value means in each channel.192function getNextMajorFlagValue(flag) {193 const value = ReactFeatureFlagsMajor[flag];194 if (value === true || value === 'next') {195 return 'โ ';196 } else if (value === false || value === null || value === 'experimental') {197 return 'โ';198 } else if (value === 'profile') {199 return '๐';200 } else if (value === 'dev') {201 return '๐ป';202 } else if (typeof value === 'number' || typeof value === 'string') {203 return value;204 } else {205 throw new Error(`Unexpected OSS Stable value ${value} for flag ${flag}`);206 }207}208209function getOSSCanaryFlagValue(flag) {210 const value = ReactFeatureFlags[flag];211 if (value === true) {212 return 'โ ';213 } else if (214 value === false ||215 value === null ||216 value === 'experimental' ||217 value === 'next'218 ) {219 return 'โ';220 } else if (value === 'profile') {221 return '๐';222 } else if (value === 'dev') {223 return '๐ป';224 } else if (typeof value === 'number' || typeof value === 'string') {225 return value;226 } else {227 throw new Error(`Unexpected OSS Canary value ${value} for flag ${flag}`);228 }229}230231function getOSSExperimentalFlagValue(flag) {232 const value = ReactFeatureFlags[flag];233 if (value === true || value === 'experimental') {234 return 'โ ';235 } else if (value === false || value === null || value === 'next') {236 return 'โ';237 } else if (value === 'profile') {238 return '๐';239 } else if (value === 'dev') {240 return '๐ป';241 } else if (typeof value === 'number' || typeof value === 'string') {242 return value;243 } else {244 throw new Error(245 `Unexpected OSS Experimental value ${value} for flag ${flag}`246 );247 }248}249250function getWWWModernFlagValue(flag) {251 const value = ReactFeatureFlagsWWW[flag];252 if (value === true || value === 'experimental') {253 return 'โ ';254 } else if (value === false || value === null || value === 'next') {255 return 'โ';256 } else if (value === 'profile') {257 return '๐';258 } else if (value === 'dev') {259 return '๐ป';260 } else if (value === 'gk') {261 return '๐งช';262 } else if (typeof value === 'number' || typeof value === 'string') {263 return value;264 } else {265 throw new Error(`Unexpected WWW Modern value ${value} for flag ${flag}`);266 }267}268269function getWWWClassicFlagValue(flag) {270 const value = ReactFeatureFlagsWWW[flag];271 if (value === true) {272 return 'โ ';273 } else if (274 value === false ||275 value === null ||276 value === 'experimental' ||277 value === 'next'278 ) {279 return 'โ';280 } else if (value === 'profile') {281 return '๐';282 } else if (value === 'dev') {283 return '๐ป';284 } else if (value === 'gk') {285 return '๐งช';286 } else if (typeof value === 'number' || typeof value === 'string') {287 return value;288 } else {289 throw new Error(`Unexpected WWW Classic value ${value} for flag ${flag}`);290 }291}292293function getRNNextMajorFlagValue(flag) {294 const value = ReactNativeFeatureFlagsMajor[flag];295 if (value === true || value === 'next') {296 return 'โ ';297 } else if (value === 'next-todo') {298 return '๐';299 } else if (value === false || value === null || value === 'experimental') {300 return 'โ';301 } else if (value === 'profile') {302 return '๐';303 } else if (value === 'dev') {304 return '๐ป';305 } else if (value === 'gk') {306 return '๐งช';307 } else if (typeof value === 'number' || typeof value === 'string') {308 return value;309 } else {310 throw new Error(`Unexpected RN OSS value ${value} for flag ${flag}`);311 }312}313314function getRNOSSFlagValue(flag) {315 const value = ReactNativeFeatureFlagsMajor[flag];316 if (value === true) {317 return 'โ ';318 } else if (319 value === false ||320 value === null ||321 value === 'experimental' ||322 value === 'next' ||323 value === 'next-todo'324 ) {325 return 'โ';326 } else if (value === 'profile') {327 return '๐';328 } else if (value === 'dev') {329 return '๐ป';330 } else if (value === 'gk') {331 return '๐งช';332 } else if (typeof value === 'number' || typeof value === 'string') {333 return value;334 } else {335 throw new Error(`Unexpected RN OSS value ${value} for flag ${flag}`);336 }337}338339function getRNFBFlagValue(flag) {340 const value = ReactFeatureFlagsNativeFB[flag];341 if (value === true) {342 return 'โ ';343 } else if (344 value === false ||345 value === null ||346 value === 'experimental' ||347 value === 'next'348 ) {349 return 'โ';350 } else if (value === 'profile') {351 return '๐';352 } else if (value === 'dev') {353 return '๐ป';354 } else if (value === 'gk') {355 return '๐งช';356 } else if (typeof value === 'number' || typeof value === 'string') {357 return value;358 } else {359 throw new Error(`Unexpected RN FB value ${value} for flag ${flag}`);360 }361}362363function argToHeader(arg) {364 switch (arg) {365 case 'www':366 return 'WWW Classic';367 case 'www-modern':368 return 'WWW Modern';369 case 'rn':370 return 'RN OSS';371 case 'rn-fb':372 return 'RN FB';373 case 'rn-next':374 return 'RN Next Major';375 case 'canary':376 return 'OSS Canary';377 case 'next':378 return 'OSS Next Major';379 case 'experimental':380 return 'OSS Experimental';381 default:382 return arg;383 }384}385386const FLAG_CONFIG = {387 'OSS Next Major': getNextMajorFlagValue,388 'OSS Canary': getOSSCanaryFlagValue,389 'OSS Experimental': getOSSExperimentalFlagValue,390 'WWW Classic': getWWWClassicFlagValue,391 'WWW Modern': getWWWModernFlagValue,392 'RN FB': getRNFBFlagValue,393 'RN OSS': getRNOSSFlagValue,394 'RN Next Major': getRNNextMajorFlagValue,395};396397const FLAG_COLUMNS = Object.keys(FLAG_CONFIG);398399const INTERNAL_VARIANTS = ['WWW Classic', 'WWW Modern', 'RN FB'];400const OSS_VARIANTS = [401 'OSS Next Major',402 'OSS Canary',403 'OSS Experimental',404 'RN OSS',405 'RN Next Major',406];407408// Build the table with the value for each flag.409function buildTable(filterFn) {410 const isDiff = argv.diff != null && argv.diff.length > 1;411 const table = {};412 const filteredFlags = filterFn413 ? allFlagsUniqueFlags.filter(filterFn)414 : allFlagsUniqueFlags;415 // eslint-disable-next-line no-for-of-loops/no-for-of-loops416 for (const flag of filteredFlags) {417 const values = FLAG_COLUMNS.reduce((acc, key) => {418 acc[key] = FLAG_CONFIG[key](flag);419 return acc;420 }, {});421422 if (!isDiff) {423 table[flag] = values;424 continue;425 }426427 const subset = argv.diff.map(argToHeader).reduce((acc, key) => {428 if (key in values) {429 acc[key] = values[key];430 }431 return acc;432 }, {});433434 if (new Set(Object.values(subset)).size !== 1) {435 table[flag] = subset;436 }437 }438439 // Sort the table440 let sorted = table;441 if (isDiff || argv.sort) {442 const sortChannel = argToHeader(isDiff ? argv.diff[0] : argv.sort);443 const sortBy =444 sortChannel === 'flag'445 ? ([flagA], [flagB]) => {446 return flagA.localeCompare(flagB);447 }448 : ([, rowA], [, rowB]) => {449 return rowB[sortChannel]450 .toString()451 .localeCompare(rowA[sortChannel]);452 };453 sorted = Object.fromEntries(Object.entries(table).sort(sortBy));454 }455456 return sorted;457}458459function formatTable(tableData) {460 // left align the flag names.461 const maxLength = Math.max(462 ...Object.keys(tableData).map(item => item.length)463 );464 const padded = {};465 Object.keys(tableData).forEach(key => {466 const newKey = key.padEnd(maxLength, ' ');467 padded[newKey] = tableData[key];468 });469470 return padded;471}472473if (argv.csv) {474 const table = buildTable();475 const csvRows = [476 `Flag name, ${FLAG_COLUMNS.join(', ')}`,477 ...Object.keys(table).map(flag => {478 const row = table[flag];479 return `${flag}, ${FLAG_COLUMNS.map(col => row[col]).join(', ')}`;480 }),481 ];482 fs.writeFile('./flags.csv', csvRows.join('\n'), function (err) {483 if (err) {484 return console.log(err);485 }486 console.log('The file was saved to ./flags.csv');487 });488}489490if (argv.cleanup) {491 const allPassingFlags = [];492 const allFailingFlags = [];493 const needsShippedExperimentFlags = [];494 const earlyExperimentationFlags = [];495 const internalOnlyFlags = [];496497 const diffedFlagColumns =498 argv.diff[0] != null ? argv.diff.map(argToHeader) : FLAG_COLUMNS;499500 // eslint-disable-next-line no-for-of-loops/no-for-of-loops501 for (const flag of allFlagsUniqueFlags) {502 const values = diffedFlagColumns.reduce((acc, key) => {503 acc[key] = FLAG_CONFIG[key](flag);504 return acc;505 }, {});506507 const uniqueValues = new Set(Object.values(values));508509 if (510 uniqueValues.size === 1 &&511 (uniqueValues.has('โ ') ||512 typeof uniqueValues.values().next().value === 'number')513 ) {514 allPassingFlags.push(flag);515 }516517 if (uniqueValues.size === 1 && uniqueValues.has('โ')) {518 allFailingFlags.push(flag);519 }520521 const internalVariantValues = INTERNAL_VARIANTS.filter(value =>522 diffedFlagColumns.includes(value)523 ).map(v => values[v]);524 const ossVariantValues = OSS_VARIANTS.filter(value =>525 diffedFlagColumns.includes(value)526 ).map(v => values[v]);527528 if (529 internalVariantValues.some(v => v === 'โ ') &&530 ossVariantValues.every(v => v === 'โ')531 ) {532 internalOnlyFlags.push(flag);533 }534535 if (536 internalVariantValues.some(v => v === '๐งช') &&537 (ossVariantValues.every(v => v === 'โ') ||538 (ossVariantValues.some(v => v === 'โ') &&539 values['OSS Experimental'] === 'โ '))540 ) {541 earlyExperimentationFlags.push(flag);542 }543544 if (545 internalVariantValues.some(v => v === '๐งช' || v === 'โ') &&546 ossVariantValues.every(v => v === 'โ ')547 ) {548 needsShippedExperimentFlags.push(flag);549 }550 }551552 if (allPassingFlags.length > 0) {553 console.log('ALL VARIANTS PASS (โ )');554 console.table(555 formatTable(buildTable(flag => allPassingFlags.includes(flag)))556 );557 }558559 if (allFailingFlags.length > 0) {560 console.log('ALL VARIANTS FAIL (โ)');561 console.table(562 formatTable(buildTable(flag => allFailingFlags.includes(flag)))563 );564 }565566 if (internalOnlyFlags.length > 0) {567 console.log('INTERNAL ONLY (โ )');568 console.table(569 formatTable(buildTable(flag => internalOnlyFlags.includes(flag)))570 );571 }572573 if (earlyExperimentationFlags.length > 0) {574 console.log('WAITING ON RESULTS (๐งช)');575 console.table(576 formatTable(buildTable(flag => earlyExperimentationFlags.includes(flag)))577 );578 }579580 if (needsShippedExperimentFlags.length > 0) {581 console.log('WAITING ON ROLLOUT (๐งช)');582 console.table(583 formatTable(584 buildTable(flag => needsShippedExperimentFlags.includes(flag))585 )586 );587 }588} else {589 console.table(formatTable(buildTable()));590}591592console.log(`593Legend:594 โ On595 โ Off596 ๐ป DEV597 ๐ TODO598 ๐ Profiling599 ๐งช Experiment600`);
Same data, no extra tab โ call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.