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