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