1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @format
8 */
9
10'use strict';
11
12const yargs = require('yargs');
13const fs = require('fs');
14
15const CONFIG_FILE_NAME = 'react-native.config.js';
16const PROJECT_FIELD = 'project';
17const IOS_FIELD = 'ios';
18const LEGACY_COMPONENTS_FIELD = 'unstable_reactLegacyComponentNames';
19const OUTPUT_FILE_NAME = 'RCTLegacyInteropComponents.mm';
20
21const argv = yargs
22  .option('p', {
23    alias: 'path',
24    description: 'Path to React Native application',
25  })
26  .option('o', {
27    alias: 'outputPath',
28    description: 'Path where generated artifacts will be output to',
29  })
30  .usage('Usage: $0 -p [path to app]')
31  .demandOption(['p']).argv;
32
33const appRoot = argv.path;
34const outputPath = argv.outputPath;
35
36function fileBody(components) {
37  // eslint-disable duplicate-license-header
38  return `
39/*
40 * Copyright (c) Meta Platforms, Inc. and affiliates.
41 *
42 * This source code is licensed under the MIT license found in the
43 * LICENSE file in the root directory of this source tree.
44 */
45
46#import "RCTLegacyInteropComponents.h"
47
48@implementation RCTLegacyInteropComponents
49
50+ (NSArray<NSString *> *)legacyInteropComponents
51{
52  return @[
53${components}
54  ];
55}
56
57@end
58`;
59  // eslint-enable duplicate-license-header
60}
61
62function extractComponentsNames(reactNativeConfig) {
63  if (!reactNativeConfig) {
64    console.log('No reactNativeConfig in the react-native.config.js file');
65    return null;
66  }
67
68  const project = reactNativeConfig[PROJECT_FIELD];
69
70  if (!project) {
71    console.log(`No ${PROJECT_FIELD} in the react-native config`);
72    return null;
73  }
74
75  const ios = project[IOS_FIELD];
76
77  if (!ios) {
78    console.log(
79      `No ${IOS_FIELD} in the ${PROJECT_FIELD} object of the config file`,
80    );
81    return null;
82  }
83
84  const componentNames = ios[LEGACY_COMPONENTS_FIELD];
85
86  if (!componentNames) {
87    console.log(
88      `No '${LEGACY_COMPONENTS_FIELD}' field in the ${PROJECT_FIELD}.${IOS_FIELD} object`,
89    );
90    return null;
91  }
92  return componentNames;
93}
94
95function generateRCTLegacyInteropComponents() {
96  const configFilePath = `${appRoot}/${CONFIG_FILE_NAME}`;
97  let reactNativeConfig = null;
98  try {
99    reactNativeConfig = require(configFilePath);
100  } catch (error) {
101    console.log(`No ${configFilePath}. Skip LegacyInterop generation`);
102    return;
103  }
104
105  const componentNames = extractComponentsNames(reactNativeConfig);
106  if (!componentNames) {
107    console.log('Skip LegacyInterop generation');
108    return;
109  }
110
111  let componentsArray = componentNames.map(name => `\t\t\t@"${name}",`);
112  // Remove the last comma
113  if (componentsArray.length > 0) {
114    componentsArray[componentsArray.length - 1] = componentsArray[
115      componentsArray.length - 1
116    ].slice(0, -1);
117  }
118
119  const filePath = `${outputPath}/${OUTPUT_FILE_NAME}`;
120  fs.writeFileSync(filePath, fileBody(componentsArray.join('\n')));
121}
122
123generateRCTLegacyInteropComponents();
124