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