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