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