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 Sapetaconst fs = require('fs'); 13*753557f6STomasz Sapetaconst mkdirp = require('mkdirp'); 14*753557f6STomasz Sapetaconst path = require('path'); 15*753557f6STomasz Sapetaconst utils = require('./codegen-utils'); 16*753557f6STomasz Sapetaconst RNCodegen = utils.getCodegen(); 17*753557f6STomasz Sapeta 18*753557f6STomasz Sapetaconst GENERATORS = { 19*753557f6STomasz Sapeta all: { 20*753557f6STomasz Sapeta android: ['componentsAndroid', 'modulesAndroid'], 21*753557f6STomasz Sapeta ios: ['componentsIOS', 'modulesIOS'], 22*753557f6STomasz Sapeta }, 23*753557f6STomasz Sapeta components: { 24*753557f6STomasz Sapeta android: ['componentsAndroid'], 25*753557f6STomasz Sapeta ios: ['componentsIOS'], 26*753557f6STomasz Sapeta }, 27*753557f6STomasz Sapeta modules: { 28*753557f6STomasz Sapeta android: ['modulesAndroid'], 29*753557f6STomasz Sapeta ios: ['modulesIOS'], 30*753557f6STomasz Sapeta }, 31*753557f6STomasz Sapeta}; 32*753557f6STomasz Sapeta 33*753557f6STomasz Sapetafunction createOutputDirectoryIfNeeded(outputDirectory, libraryName) { 34*753557f6STomasz Sapeta if (!outputDirectory) { 35*753557f6STomasz Sapeta outputDirectory = path.resolve(__dirname, '..', 'Libraries', libraryName); 36*753557f6STomasz Sapeta } 37*753557f6STomasz Sapeta mkdirp.sync(outputDirectory); 38*753557f6STomasz Sapeta} 39*753557f6STomasz Sapeta 40*753557f6STomasz Sapetafunction createFolderIfDefined(folder) { 41*753557f6STomasz Sapeta if (folder) { 42*753557f6STomasz Sapeta mkdirp.sync(folder); 43*753557f6STomasz Sapeta } 44*753557f6STomasz Sapeta} 45*753557f6STomasz Sapeta 46*753557f6STomasz Sapeta/** 47*753557f6STomasz Sapeta * This function read a JSON schema from a path and parses it. 48*753557f6STomasz Sapeta * It throws if the schema don't exists or it can't be parsed. 49*753557f6STomasz Sapeta * 50*753557f6STomasz Sapeta * @parameter schemaPath: the path to the schema 51*753557f6STomasz Sapeta * @return a valid schema 52*753557f6STomasz Sapeta * @throw an Error if the schema doesn't exists in a given path or if it can't be parsed. 53*753557f6STomasz Sapeta */ 54*753557f6STomasz Sapetafunction readAndParseSchema(schemaPath) { 55*753557f6STomasz Sapeta const schemaText = fs.readFileSync(schemaPath, 'utf-8'); 56*753557f6STomasz Sapeta 57*753557f6STomasz Sapeta if (schemaText == null) { 58*753557f6STomasz Sapeta throw new Error(`Can't find schema at ${schemaPath}`); 59*753557f6STomasz Sapeta } 60*753557f6STomasz Sapeta 61*753557f6STomasz Sapeta try { 62*753557f6STomasz Sapeta return JSON.parse(schemaText); 63*753557f6STomasz Sapeta } catch (err) { 64*753557f6STomasz Sapeta throw new Error(`Can't parse schema to JSON. ${schemaPath}`); 65*753557f6STomasz Sapeta } 66*753557f6STomasz Sapeta} 67*753557f6STomasz Sapeta 68*753557f6STomasz Sapetafunction validateLibraryType(libraryType) { 69*753557f6STomasz Sapeta if (GENERATORS[libraryType] == null) { 70*753557f6STomasz Sapeta throw new Error(`Invalid library type. ${libraryType}`); 71*753557f6STomasz Sapeta } 72*753557f6STomasz Sapeta} 73*753557f6STomasz Sapeta 74*753557f6STomasz Sapetafunction generateSpec( 75*753557f6STomasz Sapeta platform, 76*753557f6STomasz Sapeta schemaPath, 77*753557f6STomasz Sapeta outputDirectory, 78*753557f6STomasz Sapeta libraryName, 79*753557f6STomasz Sapeta packageName, 80*753557f6STomasz Sapeta libraryType, 81*753557f6STomasz Sapeta) { 82*753557f6STomasz Sapeta validateLibraryType(libraryType); 83*753557f6STomasz Sapeta 84*753557f6STomasz Sapeta let schema = readAndParseSchema(schemaPath); 85*753557f6STomasz Sapeta 86*753557f6STomasz Sapeta createOutputDirectoryIfNeeded(outputDirectory, libraryName); 87*753557f6STomasz Sapeta function composePath(intermediate) { 88*753557f6STomasz Sapeta return path.join(outputDirectory, intermediate, libraryName); 89*753557f6STomasz Sapeta } 90*753557f6STomasz Sapeta 91*753557f6STomasz Sapeta // These are hardcoded and should not be changed. 92*753557f6STomasz Sapeta // The codegen creates some C++ code with #include directive 93*753557f6STomasz Sapeta // which uses these paths. Those directive are not customizable yet. 94*753557f6STomasz Sapeta createFolderIfDefined(composePath('react/renderer/components/')); 95*753557f6STomasz Sapeta createFolderIfDefined(composePath('./')); 96*753557f6STomasz Sapeta 97*753557f6STomasz Sapeta RNCodegen.generate( 98*753557f6STomasz Sapeta { 99*753557f6STomasz Sapeta libraryName, 100*753557f6STomasz Sapeta schema, 101*753557f6STomasz Sapeta outputDirectory, 102*753557f6STomasz Sapeta packageName, 103*753557f6STomasz Sapeta }, 104*753557f6STomasz Sapeta { 105*753557f6STomasz Sapeta generators: GENERATORS[libraryType][platform], 106*753557f6STomasz Sapeta }, 107*753557f6STomasz Sapeta ); 108*753557f6STomasz Sapeta 109*753557f6STomasz Sapeta if (platform === 'android') { 110*753557f6STomasz Sapeta // Move all components C++ files to a structured jni folder for now. 111*753557f6STomasz Sapeta // Note: this should've been done by RNCodegen's generators, but: 112*753557f6STomasz Sapeta // * the generators don't support platform option yet 113*753557f6STomasz Sapeta // * this subdir structure is Android-only, not applicable to iOS 114*753557f6STomasz Sapeta const files = fs.readdirSync(outputDirectory); 115*753557f6STomasz Sapeta const jniOutputDirectory = `${outputDirectory}/jni/react/renderer/components/${libraryName}`; 116*753557f6STomasz Sapeta mkdirp.sync(jniOutputDirectory); 117*753557f6STomasz Sapeta files 118*753557f6STomasz Sapeta .filter(f => f.endsWith('.h') || f.endsWith('.cpp')) 119*753557f6STomasz Sapeta .forEach(f => { 120*753557f6STomasz Sapeta fs.renameSync(`${outputDirectory}/${f}`, `${jniOutputDirectory}/${f}`); 121*753557f6STomasz Sapeta }); 122*753557f6STomasz Sapeta } 123*753557f6STomasz Sapeta} 124*753557f6STomasz Sapeta 125*753557f6STomasz Sapetamodule.exports = { 126*753557f6STomasz Sapeta execute: generateSpec, 127*753557f6STomasz Sapeta}; 128