1*753557f6STomasz Sapeta/** 2*753557f6STomasz Sapeta * Copyright (c) Meta Platforms, Inc. and affiliates. 3*753557f6STomasz Sapeta * 4*753557f6STomasz Sapeta * This source code is licensed under the MIT license found in the 5*753557f6STomasz Sapeta * LICENSE file in the root directory of this source tree. 6*753557f6STomasz Sapeta * 7*753557f6STomasz Sapeta * @format 8*753557f6STomasz Sapeta */ 9*753557f6STomasz Sapeta 10*753557f6STomasz Sapeta'use strict'; 11*753557f6STomasz Sapeta 12*753557f6STomasz Sapeta/** 13*753557f6STomasz Sapeta * This script crawls through a React Native application's dependencies and invokes the codegen 14*753557f6STomasz Sapeta * for any libraries that require it. 15*753557f6STomasz Sapeta * To enable codegen support, the library should include a config in the codegenConfigKey key 16*753557f6STomasz Sapeta * in a codegenConfigFilename file. 17*753557f6STomasz Sapeta */ 18*753557f6STomasz Sapeta 19*753557f6STomasz Sapetaconst {execSync} = require('child_process'); 20*753557f6STomasz Sapetaconst fs = require('fs'); 21*753557f6STomasz Sapetaconst os = require('os'); 22*753557f6STomasz Sapetaconst path = require('path'); 23*753557f6STomasz Sapeta 24*753557f6STomasz Sapetaconst RN_ROOT = path.join(__dirname, '../..'); 25*753557f6STomasz Sapeta 26*753557f6STomasz Sapetaconst CODEGEN_REPO_PATH = `${RN_ROOT}/packages/react-native-codegen`; 27*753557f6STomasz Sapetaconst CODEGEN_NPM_PATH = `${RN_ROOT}/../react-native-codegen`; 28*753557f6STomasz Sapetaconst CORE_LIBRARIES = new Set(['rncore', 'FBReactNativeSpec']); 29*753557f6STomasz Sapetaconst REACT_NATIVE_DEPENDENCY_NAME = 'react-native'; 30*753557f6STomasz Sapeta 31*753557f6STomasz Sapeta// HELPERS 32*753557f6STomasz Sapeta 33*753557f6STomasz Sapetafunction isReactNativeCoreLibrary(libraryName) { 34*753557f6STomasz Sapeta return CORE_LIBRARIES.has(libraryName); 35*753557f6STomasz Sapeta} 36*753557f6STomasz Sapeta 37*753557f6STomasz Sapetafunction executeNodeScript(node, script) { 38*753557f6STomasz Sapeta execSync(`${node} ${script}`); 39*753557f6STomasz Sapeta} 40*753557f6STomasz Sapeta 41*753557f6STomasz Sapetafunction isAppRootValid(appRootDir) { 42*753557f6STomasz Sapeta if (appRootDir == null) { 43*753557f6STomasz Sapeta console.error('Missing path to React Native application'); 44*753557f6STomasz Sapeta process.exitCode = 1; 45*753557f6STomasz Sapeta return false; 46*753557f6STomasz Sapeta } 47*753557f6STomasz Sapeta return true; 48*753557f6STomasz Sapeta} 49*753557f6STomasz Sapeta 50*753557f6STomasz Sapetafunction readPackageJSON(appRootDir) { 51*753557f6STomasz Sapeta return JSON.parse(fs.readFileSync(path.join(appRootDir, 'package.json'))); 52*753557f6STomasz Sapeta} 53*753557f6STomasz Sapeta 54*753557f6STomasz Sapetafunction printDeprecationWarningIfNeeded(dependency) { 55*753557f6STomasz Sapeta if (dependency === REACT_NATIVE_DEPENDENCY_NAME) { 56*753557f6STomasz Sapeta return; 57*753557f6STomasz Sapeta } 58*753557f6STomasz Sapeta console.log(`[Codegen] CodegenConfig Deprecated Setup for ${dependency}. 59*753557f6STomasz Sapeta The configuration file still contains the codegen in the libraries array. 60*753557f6STomasz Sapeta If possible, replace it with a single object. 61*753557f6STomasz Sapeta `); 62*753557f6STomasz Sapeta console.debug(`BEFORE: 63*753557f6STomasz Sapeta { 64*753557f6STomasz Sapeta // ... 65*753557f6STomasz Sapeta "codegenConfig": { 66*753557f6STomasz Sapeta "libraries": [ 67*753557f6STomasz Sapeta { 68*753557f6STomasz Sapeta "name": "libName1", 69*753557f6STomasz Sapeta "type": "all|components|modules", 70*753557f6STomasz Sapeta "jsSrcsRoot": "libName1/js" 71*753557f6STomasz Sapeta }, 72*753557f6STomasz Sapeta { 73*753557f6STomasz Sapeta "name": "libName2", 74*753557f6STomasz Sapeta "type": "all|components|modules", 75*753557f6STomasz Sapeta "jsSrcsRoot": "libName2/src" 76*753557f6STomasz Sapeta } 77*753557f6STomasz Sapeta ] 78*753557f6STomasz Sapeta } 79*753557f6STomasz Sapeta } 80*753557f6STomasz Sapeta 81*753557f6STomasz Sapeta AFTER: 82*753557f6STomasz Sapeta { 83*753557f6STomasz Sapeta "codegenConfig": { 84*753557f6STomasz Sapeta "name": "libraries", 85*753557f6STomasz Sapeta "type": "all", 86*753557f6STomasz Sapeta "jsSrcsRoot": "." 87*753557f6STomasz Sapeta } 88*753557f6STomasz Sapeta } 89*753557f6STomasz Sapeta `); 90*753557f6STomasz Sapeta} 91*753557f6STomasz Sapeta 92*753557f6STomasz Sapeta// Reading Libraries 93*753557f6STomasz Sapetafunction extractLibrariesFromConfigurationArray( 94*753557f6STomasz Sapeta configFile, 95*753557f6STomasz Sapeta codegenConfigKey, 96*753557f6STomasz Sapeta libraries, 97*753557f6STomasz Sapeta dependency, 98*753557f6STomasz Sapeta dependencyPath, 99*753557f6STomasz Sapeta) { 100*753557f6STomasz Sapeta console.log(`[Codegen] Found ${dependency}`); 101*753557f6STomasz Sapeta configFile[codegenConfigKey].libraries.forEach(config => { 102*753557f6STomasz Sapeta const libraryConfig = { 103*753557f6STomasz Sapeta library: dependency, 104*753557f6STomasz Sapeta config, 105*753557f6STomasz Sapeta libraryPath: dependencyPath, 106*753557f6STomasz Sapeta }; 107*753557f6STomasz Sapeta libraries.push(libraryConfig); 108*753557f6STomasz Sapeta }); 109*753557f6STomasz Sapeta} 110*753557f6STomasz Sapeta 111*753557f6STomasz Sapetafunction extractLibrariesFromJSON( 112*753557f6STomasz Sapeta configFile, 113*753557f6STomasz Sapeta libraries, 114*753557f6STomasz Sapeta codegenConfigKey, 115*753557f6STomasz Sapeta dependency, 116*753557f6STomasz Sapeta dependencyPath, 117*753557f6STomasz Sapeta) { 118*753557f6STomasz Sapeta var isBlocking = false; 119*753557f6STomasz Sapeta if (dependency == null) { 120*753557f6STomasz Sapeta dependency = REACT_NATIVE_DEPENDENCY_NAME; 121*753557f6STomasz Sapeta dependencyPath = RN_ROOT; 122*753557f6STomasz Sapeta // If we are exploring the ReactNative libraries, we want to raise an error 123*753557f6STomasz Sapeta // if the codegen is not properly configured. 124*753557f6STomasz Sapeta isBlocking = true; 125*753557f6STomasz Sapeta } 126*753557f6STomasz Sapeta 127*753557f6STomasz Sapeta if (configFile[codegenConfigKey] == null) { 128*753557f6STomasz Sapeta if (isBlocking) { 129*753557f6STomasz Sapeta throw `[Codegen] Error: Could not find codegen config for ${dependency} .`; 130*753557f6STomasz Sapeta } 131*753557f6STomasz Sapeta return; 132*753557f6STomasz Sapeta } 133*753557f6STomasz Sapeta 134*753557f6STomasz Sapeta if (configFile[codegenConfigKey].libraries == null) { 135*753557f6STomasz Sapeta console.log(`[Codegen] Found ${dependency}`); 136*753557f6STomasz Sapeta var config = configFile[codegenConfigKey]; 137*753557f6STomasz Sapeta libraries.push({ 138*753557f6STomasz Sapeta library: dependency, 139*753557f6STomasz Sapeta config, 140*753557f6STomasz Sapeta libraryPath: dependencyPath, 141*753557f6STomasz Sapeta }); 142*753557f6STomasz Sapeta } else { 143*753557f6STomasz Sapeta printDeprecationWarningIfNeeded(dependency); 144*753557f6STomasz Sapeta extractLibrariesFromConfigurationArray( 145*753557f6STomasz Sapeta configFile, 146*753557f6STomasz Sapeta codegenConfigKey, 147*753557f6STomasz Sapeta libraries, 148*753557f6STomasz Sapeta dependency, 149*753557f6STomasz Sapeta dependencyPath, 150*753557f6STomasz Sapeta ); 151*753557f6STomasz Sapeta } 152*753557f6STomasz Sapeta} 153*753557f6STomasz Sapeta 154*753557f6STomasz Sapetafunction handleReactNativeCodeLibraries( 155*753557f6STomasz Sapeta libraries, 156*753557f6STomasz Sapeta codegenConfigFilename, 157*753557f6STomasz Sapeta codegenConfigKey, 158*753557f6STomasz Sapeta) { 159*753557f6STomasz Sapeta // Handle react-native core libraries. 160*753557f6STomasz Sapeta // This is required when react-native is outside of node_modules. 161*753557f6STomasz Sapeta console.log('[Codegen] Processing react-native core libraries'); 162*753557f6STomasz Sapeta const reactNativePkgJson = path.join(RN_ROOT, codegenConfigFilename); 163*753557f6STomasz Sapeta if (!fs.existsSync(reactNativePkgJson)) { 164*753557f6STomasz Sapeta throw '[Codegen] Error: Could not find config file for react-native.'; 165*753557f6STomasz Sapeta } 166*753557f6STomasz Sapeta const reactNativeConfigFile = JSON.parse(fs.readFileSync(reactNativePkgJson)); 167*753557f6STomasz Sapeta extractLibrariesFromJSON(reactNativeConfigFile, libraries, codegenConfigKey); 168*753557f6STomasz Sapeta} 169*753557f6STomasz Sapeta 170*753557f6STomasz Sapetafunction handleThirdPartyLibraries( 171*753557f6STomasz Sapeta libraries, 172*753557f6STomasz Sapeta baseCodegenConfigFileDir, 173*753557f6STomasz Sapeta dependencies, 174*753557f6STomasz Sapeta codegenConfigFilename, 175*753557f6STomasz Sapeta codegenConfigKey, 176*753557f6STomasz Sapeta) { 177*753557f6STomasz Sapeta // Determine which of these are codegen-enabled libraries 178*753557f6STomasz Sapeta const configDir = baseCodegenConfigFileDir || path.join(RN_ROOT, '..'); 179*753557f6STomasz Sapeta console.log( 180*753557f6STomasz Sapeta `\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in ${configDir}`, 181*753557f6STomasz Sapeta ); 182*753557f6STomasz Sapeta 183*753557f6STomasz Sapeta // Handle third-party libraries 184*753557f6STomasz Sapeta Object.keys(dependencies).forEach(dependency => { 185*753557f6STomasz Sapeta if (dependency === REACT_NATIVE_DEPENDENCY_NAME) { 186*753557f6STomasz Sapeta // react-native should already be added. 187*753557f6STomasz Sapeta return; 188*753557f6STomasz Sapeta } 189*753557f6STomasz Sapeta const codegenConfigFileDir = path.join(configDir, dependency); 190*753557f6STomasz Sapeta const configFilePath = path.join( 191*753557f6STomasz Sapeta codegenConfigFileDir, 192*753557f6STomasz Sapeta codegenConfigFilename, 193*753557f6STomasz Sapeta ); 194*753557f6STomasz Sapeta if (fs.existsSync(configFilePath)) { 195*753557f6STomasz Sapeta const configFile = JSON.parse(fs.readFileSync(configFilePath)); 196*753557f6STomasz Sapeta extractLibrariesFromJSON( 197*753557f6STomasz Sapeta configFile, 198*753557f6STomasz Sapeta libraries, 199*753557f6STomasz Sapeta codegenConfigKey, 200*753557f6STomasz Sapeta dependency, 201*753557f6STomasz Sapeta codegenConfigFileDir, 202*753557f6STomasz Sapeta ); 203*753557f6STomasz Sapeta } 204*753557f6STomasz Sapeta }); 205*753557f6STomasz Sapeta} 206*753557f6STomasz Sapeta 207*753557f6STomasz Sapetafunction handleLibrariesFromReactNativeConfig( 208*753557f6STomasz Sapeta libraries, 209*753557f6STomasz Sapeta codegenConfigKey, 210*753557f6STomasz Sapeta codegenConfigFilename, 211*753557f6STomasz Sapeta appRootDir, 212*753557f6STomasz Sapeta) { 213*753557f6STomasz Sapeta const rnConfigFileName = 'react-native.config.js'; 214*753557f6STomasz Sapeta 215*753557f6STomasz Sapeta console.log( 216*753557f6STomasz Sapeta `\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in ${rnConfigFileName}`, 217*753557f6STomasz Sapeta ); 218*753557f6STomasz Sapeta 219*753557f6STomasz Sapeta const rnConfigFilePath = path.join(appRootDir, rnConfigFileName); 220*753557f6STomasz Sapeta 221*753557f6STomasz Sapeta if (fs.existsSync(rnConfigFilePath)) { 222*753557f6STomasz Sapeta const rnConfig = require(rnConfigFilePath); 223*753557f6STomasz Sapeta 224*753557f6STomasz Sapeta if (rnConfig.dependencies != null) { 225*753557f6STomasz Sapeta Object.keys(rnConfig.dependencies).forEach(name => { 226*753557f6STomasz Sapeta const dependencyConfig = rnConfig.dependencies[name]; 227*753557f6STomasz Sapeta 228*753557f6STomasz Sapeta if (dependencyConfig.root) { 229*753557f6STomasz Sapeta const codegenConfigFileDir = path.resolve( 230*753557f6STomasz Sapeta appRootDir, 231*753557f6STomasz Sapeta dependencyConfig.root, 232*753557f6STomasz Sapeta ); 233*753557f6STomasz Sapeta const configFilePath = path.join( 234*753557f6STomasz Sapeta codegenConfigFileDir, 235*753557f6STomasz Sapeta codegenConfigFilename, 236*753557f6STomasz Sapeta ); 237*753557f6STomasz Sapeta const pkgJsonPath = path.join(codegenConfigFileDir, 'package.json'); 238*753557f6STomasz Sapeta 239*753557f6STomasz Sapeta if (fs.existsSync(configFilePath)) { 240*753557f6STomasz Sapeta const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath)); 241*753557f6STomasz Sapeta const configFile = JSON.parse(fs.readFileSync(configFilePath)); 242*753557f6STomasz Sapeta extractLibrariesFromJSON( 243*753557f6STomasz Sapeta configFile, 244*753557f6STomasz Sapeta libraries, 245*753557f6STomasz Sapeta codegenConfigKey, 246*753557f6STomasz Sapeta pkgJson.name, 247*753557f6STomasz Sapeta codegenConfigFileDir, 248*753557f6STomasz Sapeta ); 249*753557f6STomasz Sapeta } 250*753557f6STomasz Sapeta } 251*753557f6STomasz Sapeta }); 252*753557f6STomasz Sapeta } 253*753557f6STomasz Sapeta } 254*753557f6STomasz Sapeta} 255*753557f6STomasz Sapeta 256*753557f6STomasz Sapetafunction handleInAppLibraries( 257*753557f6STomasz Sapeta libraries, 258*753557f6STomasz Sapeta pkgJson, 259*753557f6STomasz Sapeta codegenConfigKey, 260*753557f6STomasz Sapeta appRootDir, 261*753557f6STomasz Sapeta) { 262*753557f6STomasz Sapeta console.log( 263*753557f6STomasz Sapeta '\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in the app', 264*753557f6STomasz Sapeta ); 265*753557f6STomasz Sapeta 266*753557f6STomasz Sapeta extractLibrariesFromJSON( 267*753557f6STomasz Sapeta pkgJson, 268*753557f6STomasz Sapeta libraries, 269*753557f6STomasz Sapeta codegenConfigKey, 270*753557f6STomasz Sapeta pkgJson.name, 271*753557f6STomasz Sapeta appRootDir, 272*753557f6STomasz Sapeta ); 273*753557f6STomasz Sapeta} 274*753557f6STomasz Sapeta 275*753557f6STomasz Sapeta// CodeGen 276*753557f6STomasz Sapeta 277*753557f6STomasz Sapetafunction getCodeGenCliPath() { 278*753557f6STomasz Sapeta let codegenCliPath; 279*753557f6STomasz Sapeta if (fs.existsSync(CODEGEN_REPO_PATH)) { 280*753557f6STomasz Sapeta codegenCliPath = CODEGEN_REPO_PATH; 281*753557f6STomasz Sapeta 282*753557f6STomasz Sapeta if (!fs.existsSync(path.join(CODEGEN_REPO_PATH, 'lib'))) { 283*753557f6STomasz Sapeta console.log('\n\n[Codegen] >>>>> Building react-native-codegen package'); 284*753557f6STomasz Sapeta execSync('yarn install', { 285*753557f6STomasz Sapeta cwd: codegenCliPath, 286*753557f6STomasz Sapeta stdio: 'inherit', 287*753557f6STomasz Sapeta }); 288*753557f6STomasz Sapeta execSync('yarn build', { 289*753557f6STomasz Sapeta cwd: codegenCliPath, 290*753557f6STomasz Sapeta stdio: 'inherit', 291*753557f6STomasz Sapeta }); 292*753557f6STomasz Sapeta } 293*753557f6STomasz Sapeta } else if (fs.existsSync(CODEGEN_NPM_PATH)) { 294*753557f6STomasz Sapeta codegenCliPath = CODEGEN_NPM_PATH; 295*753557f6STomasz Sapeta } else { 296*753557f6STomasz Sapeta throw "error: Could not determine react-native-codegen location. Try running 'yarn install' or 'npm install' in your project root."; 297*753557f6STomasz Sapeta } 298*753557f6STomasz Sapeta return codegenCliPath; 299*753557f6STomasz Sapeta} 300*753557f6STomasz Sapeta 301*753557f6STomasz Sapetafunction computeIOSOutputDir(outputPath, appRootDir) { 302*753557f6STomasz Sapeta return path.join(outputPath ? outputPath : appRootDir, 'build/generated/ios'); 303*753557f6STomasz Sapeta} 304*753557f6STomasz Sapeta 305*753557f6STomasz Sapetafunction generateSchema(tmpDir, library, node, codegenCliPath) { 306*753557f6STomasz Sapeta const pathToSchema = path.join(tmpDir, 'schema.json'); 307*753557f6STomasz Sapeta const pathToJavaScriptSources = path.join( 308*753557f6STomasz Sapeta library.libraryPath, 309*753557f6STomasz Sapeta library.config.jsSrcsDir, 310*753557f6STomasz Sapeta ); 311*753557f6STomasz Sapeta 312*753557f6STomasz Sapeta console.log(`\n\n[Codegen] >>>>> Processing ${library.config.name}`); 313*753557f6STomasz Sapeta // Generate one schema for the entire library... 314*753557f6STomasz Sapeta executeNodeScript( 315*753557f6STomasz Sapeta node, 316*753557f6STomasz Sapeta `${path.join( 317*753557f6STomasz Sapeta codegenCliPath, 318*753557f6STomasz Sapeta 'lib', 319*753557f6STomasz Sapeta 'cli', 320*753557f6STomasz Sapeta 'combine', 321*753557f6STomasz Sapeta 'combine-js-to-schema-cli.js', 322*753557f6STomasz Sapeta )} ${pathToSchema} ${pathToJavaScriptSources}`, 323*753557f6STomasz Sapeta ); 324*753557f6STomasz Sapeta console.log(`[Codegen] Generated schema: ${pathToSchema}`); 325*753557f6STomasz Sapeta return pathToSchema; 326*753557f6STomasz Sapeta} 327*753557f6STomasz Sapeta 328*753557f6STomasz Sapetafunction generateCode(iosOutputDir, library, tmpDir, node, pathToSchema) { 329*753557f6STomasz Sapeta // ...then generate native code artifacts. 330*753557f6STomasz Sapeta const libraryTypeArg = library.config.type 331*753557f6STomasz Sapeta ? `--libraryType ${library.config.type}` 332*753557f6STomasz Sapeta : ''; 333*753557f6STomasz Sapeta 334*753557f6STomasz Sapeta const tmpOutputDir = path.join(tmpDir, 'out'); 335*753557f6STomasz Sapeta fs.mkdirSync(tmpOutputDir, {recursive: true}); 336*753557f6STomasz Sapeta 337*753557f6STomasz Sapeta executeNodeScript( 338*753557f6STomasz Sapeta node, 339*753557f6STomasz Sapeta `${path.join(RN_ROOT, 'scripts', 'generate-specs-cli.js')} \ 340*753557f6STomasz Sapeta --platform ios \ 341*753557f6STomasz Sapeta --schemaPath ${pathToSchema} \ 342*753557f6STomasz Sapeta --outputDir ${tmpOutputDir} \ 343*753557f6STomasz Sapeta --libraryName ${library.config.name} \ 344*753557f6STomasz Sapeta ${libraryTypeArg}`, 345*753557f6STomasz Sapeta ); 346*753557f6STomasz Sapeta 347*753557f6STomasz Sapeta // Finally, copy artifacts to the final output directory. 348*753557f6STomasz Sapeta fs.mkdirSync(iosOutputDir, {recursive: true}); 349*753557f6STomasz Sapeta execSync(`cp -R ${tmpOutputDir}/* ${iosOutputDir}`); 350*753557f6STomasz Sapeta console.log(`[Codegen] Generated artifacts: ${iosOutputDir}`); 351*753557f6STomasz Sapeta} 352*753557f6STomasz Sapeta 353*753557f6STomasz Sapetafunction generateNativeCodegenFiles( 354*753557f6STomasz Sapeta libraries, 355*753557f6STomasz Sapeta fabricEnabled, 356*753557f6STomasz Sapeta iosOutputDir, 357*753557f6STomasz Sapeta node, 358*753557f6STomasz Sapeta codegenCliPath, 359*753557f6STomasz Sapeta schemaPaths, 360*753557f6STomasz Sapeta) { 361*753557f6STomasz Sapeta let fabricEnabledTypes = ['components', 'all']; 362*753557f6STomasz Sapeta libraries.forEach(library => { 363*753557f6STomasz Sapeta if ( 364*753557f6STomasz Sapeta !fabricEnabled && 365*753557f6STomasz Sapeta fabricEnabledTypes.indexOf(library.config.type) >= 0 366*753557f6STomasz Sapeta ) { 367*753557f6STomasz Sapeta console.log( 368*753557f6STomasz Sapeta `[Codegen] ${library.config.name} skipped because fabric is not enabled.`, 369*753557f6STomasz Sapeta ); 370*753557f6STomasz Sapeta return; 371*753557f6STomasz Sapeta } 372*753557f6STomasz Sapeta const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), library.config.name)); 373*753557f6STomasz Sapeta const pathToSchema = generateSchema(tmpDir, library, node, codegenCliPath); 374*753557f6STomasz Sapeta generateCode(iosOutputDir, library, tmpDir, node, pathToSchema); 375*753557f6STomasz Sapeta 376*753557f6STomasz Sapeta // Filter the react native core library out. 377*753557f6STomasz Sapeta // In the future, core library and third party library should 378*753557f6STomasz Sapeta // use the same way to generate/register the fabric components. 379*753557f6STomasz Sapeta if (!isReactNativeCoreLibrary(library.config.name)) { 380*753557f6STomasz Sapeta schemaPaths[library.config.name] = pathToSchema; 381*753557f6STomasz Sapeta } 382*753557f6STomasz Sapeta }); 383*753557f6STomasz Sapeta} 384*753557f6STomasz Sapeta 385*753557f6STomasz Sapetafunction createComponentProvider( 386*753557f6STomasz Sapeta fabricEnabled, 387*753557f6STomasz Sapeta schemaPaths, 388*753557f6STomasz Sapeta node, 389*753557f6STomasz Sapeta iosOutputDir, 390*753557f6STomasz Sapeta) { 391*753557f6STomasz Sapeta if (fabricEnabled) { 392*753557f6STomasz Sapeta console.log('\n\n>>>>> Creating component provider'); 393*753557f6STomasz Sapeta // Save the list of spec paths to a temp file. 394*753557f6STomasz Sapeta const schemaListTmpPath = `${os.tmpdir()}/rn-tmp-schema-list.json`; 395*753557f6STomasz Sapeta const fd = fs.openSync(schemaListTmpPath, 'w'); 396*753557f6STomasz Sapeta fs.writeSync(fd, JSON.stringify(schemaPaths)); 397*753557f6STomasz Sapeta fs.closeSync(fd); 398*753557f6STomasz Sapeta console.log(`Generated schema list: ${schemaListTmpPath}`); 399*753557f6STomasz Sapeta 400*753557f6STomasz Sapeta // Generate FabricComponentProvider. 401*753557f6STomasz Sapeta // Only for iOS at this moment. 402*753557f6STomasz Sapeta executeNodeScript( 403*753557f6STomasz Sapeta node, 404*753557f6STomasz Sapeta `${path.join( 405*753557f6STomasz Sapeta RN_ROOT, 406*753557f6STomasz Sapeta 'scripts', 407*753557f6STomasz Sapeta 'generate-provider-cli.js', 408*753557f6STomasz Sapeta )} --platform ios --schemaListPath "${schemaListTmpPath}" --outputDir ${iosOutputDir}`, 409*753557f6STomasz Sapeta ); 410*753557f6STomasz Sapeta console.log(`Generated provider in: ${iosOutputDir}`); 411*753557f6STomasz Sapeta } 412*753557f6STomasz Sapeta} 413*753557f6STomasz Sapeta 414*753557f6STomasz Sapetafunction findCodegenEnabledLibraries( 415*753557f6STomasz Sapeta appRootDir, 416*753557f6STomasz Sapeta baseCodegenConfigFileDir, 417*753557f6STomasz Sapeta codegenConfigFilename, 418*753557f6STomasz Sapeta codegenConfigKey, 419*753557f6STomasz Sapeta) { 420*753557f6STomasz Sapeta const pkgJson = readPackageJSON(appRootDir); 421*753557f6STomasz Sapeta const dependencies = {...pkgJson.dependencies, ...pkgJson.devDependencies}; 422*753557f6STomasz Sapeta const libraries = []; 423*753557f6STomasz Sapeta 424*753557f6STomasz Sapeta handleReactNativeCodeLibraries( 425*753557f6STomasz Sapeta libraries, 426*753557f6STomasz Sapeta codegenConfigFilename, 427*753557f6STomasz Sapeta codegenConfigKey, 428*753557f6STomasz Sapeta ); 429*753557f6STomasz Sapeta handleThirdPartyLibraries( 430*753557f6STomasz Sapeta libraries, 431*753557f6STomasz Sapeta baseCodegenConfigFileDir, 432*753557f6STomasz Sapeta dependencies, 433*753557f6STomasz Sapeta codegenConfigFilename, 434*753557f6STomasz Sapeta codegenConfigKey, 435*753557f6STomasz Sapeta ); 436*753557f6STomasz Sapeta handleLibrariesFromReactNativeConfig( 437*753557f6STomasz Sapeta libraries, 438*753557f6STomasz Sapeta codegenConfigKey, 439*753557f6STomasz Sapeta codegenConfigFilename, 440*753557f6STomasz Sapeta appRootDir, 441*753557f6STomasz Sapeta ); 442*753557f6STomasz Sapeta handleInAppLibraries(libraries, pkgJson, codegenConfigKey, appRootDir); 443*753557f6STomasz Sapeta 444*753557f6STomasz Sapeta return libraries; 445*753557f6STomasz Sapeta} 446*753557f6STomasz Sapeta 447*753557f6STomasz Sapeta// It removes all the empty files and empty folders 448*753557f6STomasz Sapeta// it finds, starting from `filepath`, recursively. 449*753557f6STomasz Sapeta// 450*753557f6STomasz Sapeta// This function is needed since, after aligning the codegen between 451*753557f6STomasz Sapeta// iOS and Android, we have to create empty folders in advance and 452*753557f6STomasz Sapeta// we don't know wheter they will be populated up until the end of the process. 453*753557f6STomasz Sapeta// 454*753557f6STomasz Sapeta// @parameter filepath: the root path from which we want to remove the empty files and folders. 455*753557f6STomasz Sapetafunction cleanupEmptyFilesAndFolders(filepath) { 456*753557f6STomasz Sapeta const stats = fs.statSync(filepath); 457*753557f6STomasz Sapeta 458*753557f6STomasz Sapeta if (stats.isFile() && stats.size === 0) { 459*753557f6STomasz Sapeta fs.rmSync(filepath); 460*753557f6STomasz Sapeta return; 461*753557f6STomasz Sapeta } else if (stats.isFile()) { 462*753557f6STomasz Sapeta return; 463*753557f6STomasz Sapeta } 464*753557f6STomasz Sapeta 465*753557f6STomasz Sapeta const dirContent = fs.readdirSync(filepath); 466*753557f6STomasz Sapeta dirContent.forEach(contentPath => 467*753557f6STomasz Sapeta cleanupEmptyFilesAndFolders(path.join(filepath, contentPath)), 468*753557f6STomasz Sapeta ); 469*753557f6STomasz Sapeta 470*753557f6STomasz Sapeta // The original folder may be filled with empty folders 471*753557f6STomasz Sapeta // if that the case, we would also like to remove the parent. 472*753557f6STomasz Sapeta // Hence, we need to read the folder again. 473*753557f6STomasz Sapeta const newContent = fs.readdirSync(filepath); 474*753557f6STomasz Sapeta if (newContent.length === 0) { 475*753557f6STomasz Sapeta fs.rmdirSync(filepath); 476*753557f6STomasz Sapeta return; 477*753557f6STomasz Sapeta } 478*753557f6STomasz Sapeta} 479*753557f6STomasz Sapeta 480*753557f6STomasz Sapeta// Execute 481*753557f6STomasz Sapeta 482*753557f6STomasz Sapeta/** 483*753557f6STomasz Sapeta * This function is the entry point for the codegen. It: 484*753557f6STomasz Sapeta * - reads the package json 485*753557f6STomasz Sapeta * - extracts the libraries 486*753557f6STomasz Sapeta * - setups the CLI to generate the code 487*753557f6STomasz Sapeta * - generate the code 488*753557f6STomasz Sapeta * 489*753557f6STomasz Sapeta * @parameter appRootDir: the directory with the app source code, where the `codegenConfigFilename` lives. 490*753557f6STomasz Sapeta * @parameter outputPath: the base output path for the CodeGen. 491*753557f6STomasz Sapeta * @parameter node: the path to the node executable, used to run the codegen scripts. 492*753557f6STomasz Sapeta * @parameter codegenConfigFilename: the file that contains the codeGen configuration. The default is `package.json`. 493*753557f6STomasz Sapeta * @parameter codegenConfigKey: the key in the codegenConfigFile that controls the codegen. 494*753557f6STomasz Sapeta * @parameter baseCodegenConfigFileDir: the directory of the codeGenConfigFile. 495*753557f6STomasz Sapeta * @parameter fabricEnabled: whether fabric is enabled or not. 496*753557f6STomasz Sapeta * @throws If it can't find a config file for react-native. 497*753557f6STomasz Sapeta * @throws If it can't find a CodeGen configuration in the file. 498*753557f6STomasz Sapeta * @throws If it can't find a cli for the CodeGen. 499*753557f6STomasz Sapeta */ 500*753557f6STomasz Sapetafunction execute( 501*753557f6STomasz Sapeta appRootDir, 502*753557f6STomasz Sapeta outputPath, 503*753557f6STomasz Sapeta node, 504*753557f6STomasz Sapeta codegenConfigFilename, 505*753557f6STomasz Sapeta codegenConfigKey, 506*753557f6STomasz Sapeta baseCodegenConfigFileDir, 507*753557f6STomasz Sapeta fabricEnabled, 508*753557f6STomasz Sapeta) { 509*753557f6STomasz Sapeta if (!isAppRootValid(appRootDir)) { 510*753557f6STomasz Sapeta return; 511*753557f6STomasz Sapeta } 512*753557f6STomasz Sapeta 513*753557f6STomasz Sapeta try { 514*753557f6STomasz Sapeta const libraries = findCodegenEnabledLibraries( 515*753557f6STomasz Sapeta appRootDir, 516*753557f6STomasz Sapeta baseCodegenConfigFileDir, 517*753557f6STomasz Sapeta codegenConfigFilename, 518*753557f6STomasz Sapeta codegenConfigKey, 519*753557f6STomasz Sapeta ); 520*753557f6STomasz Sapeta 521*753557f6STomasz Sapeta if (libraries.length === 0) { 522*753557f6STomasz Sapeta console.log('[Codegen] No codegen-enabled libraries found.'); 523*753557f6STomasz Sapeta return; 524*753557f6STomasz Sapeta } 525*753557f6STomasz Sapeta 526*753557f6STomasz Sapeta const codegenCliPath = getCodeGenCliPath(); 527*753557f6STomasz Sapeta 528*753557f6STomasz Sapeta const schemaPaths = {}; 529*753557f6STomasz Sapeta 530*753557f6STomasz Sapeta const iosOutputDir = computeIOSOutputDir(outputPath, appRootDir); 531*753557f6STomasz Sapeta 532*753557f6STomasz Sapeta generateNativeCodegenFiles( 533*753557f6STomasz Sapeta libraries, 534*753557f6STomasz Sapeta fabricEnabled, 535*753557f6STomasz Sapeta iosOutputDir, 536*753557f6STomasz Sapeta node, 537*753557f6STomasz Sapeta codegenCliPath, 538*753557f6STomasz Sapeta schemaPaths, 539*753557f6STomasz Sapeta ); 540*753557f6STomasz Sapeta 541*753557f6STomasz Sapeta createComponentProvider(fabricEnabled, schemaPaths, node, iosOutputDir); 542*753557f6STomasz Sapeta cleanupEmptyFilesAndFolders(iosOutputDir); 543*753557f6STomasz Sapeta } catch (err) { 544*753557f6STomasz Sapeta console.error(err); 545*753557f6STomasz Sapeta process.exitCode = 1; 546*753557f6STomasz Sapeta } 547*753557f6STomasz Sapeta 548*753557f6STomasz Sapeta console.log('\n\n[Codegen] Done.'); 549*753557f6STomasz Sapeta return; 550*753557f6STomasz Sapeta} 551*753557f6STomasz Sapeta 552*753557f6STomasz Sapetamodule.exports = { 553*753557f6STomasz Sapeta execute: execute, 554*753557f6STomasz Sapeta // exported for testing purposes only: 555*753557f6STomasz Sapeta _extractLibrariesFromJSON: extractLibrariesFromJSON, 556*753557f6STomasz Sapeta _findCodegenEnabledLibraries: findCodegenEnabledLibraries, 557*753557f6STomasz Sapeta _executeNodeScript: executeNodeScript, 558*753557f6STomasz Sapeta _generateCode: generateCode, 559*753557f6STomasz Sapeta _cleanupEmptyFilesAndFolders: cleanupEmptyFilesAndFolders, 560*753557f6STomasz Sapeta}; 561