1import chalk from 'chalk'; 2import fs from 'fs-extra'; 3import path from 'path'; 4 5import { IOS_DIR } from '../../Constants'; 6import logger from '../../Logger'; 7import { copyFileWithTransformsAsync } from '../../Transforms'; 8import { FileTransforms } from '../../Transforms.types'; 9import { searchFilesAsync } from '../../Utils'; 10import vendoredModulesTransforms from './transforms/vendoredModulesTransforms'; 11 12const IOS_VENDORED_DIR = path.join(IOS_DIR, 'vendored'); 13 14/** 15 * Versions iOS vendored modules. 16 */ 17export async function versionVendoredModulesAsync( 18 sdkNumber: number, 19 filterModules: string[] | null 20): Promise<void> { 21 const prefix = `ABI${sdkNumber}_0_0`; 22 const config = vendoredModulesTransforms(prefix); 23 const baseTransforms = baseTransformsFactory(prefix); 24 const unversionedDir = path.join(IOS_VENDORED_DIR, 'unversioned'); 25 const versionedDir = path.join(IOS_VENDORED_DIR, `sdk${sdkNumber}`); 26 const sourceDirents = (await fs.readdir(unversionedDir, { withFileTypes: true })).filter( 27 (dirent) => { 28 return dirent.isDirectory() && (!filterModules || filterModules.includes(dirent.name)); 29 } 30 ); 31 32 for (const { name } of sourceDirents) { 33 logger.info(' Versioning vendored module %s', chalk.green(name)); 34 35 const moduleConfig = config[name]; 36 const sourceDirectory = path.join(unversionedDir, name); 37 const targetDirectory = path.join(versionedDir, name); 38 const files = await searchFilesAsync(sourceDirectory, '**'); 39 40 await fs.remove(targetDirectory); 41 42 for (const sourceFile of files) { 43 await copyFileWithTransformsAsync({ 44 sourceFile, 45 sourceDirectory, 46 targetDirectory, 47 transforms: { 48 path: [...baseTransforms.path, ...(moduleConfig?.path ?? [])], 49 content: [...baseTransforms.content, ...(moduleConfig?.content ?? [])], 50 }, 51 }); 52 } 53 } 54} 55 56/** 57 * Generates base transforms to apply for all vendored modules. 58 */ 59function baseTransformsFactory(prefix: string): Required<FileTransforms> { 60 return { 61 path: [ 62 { 63 find: /([^/]+\.podspec\.json)$\b/, 64 replaceWith: `${prefix}$1`, 65 }, 66 { 67 find: /\b(RCT|RNC|RNG|RNR|REA|RNS)([^/]*\.)(h|m|mm)/, 68 replaceWith: `${prefix}$1$2$3`, 69 }, 70 ], 71 content: [ 72 { 73 paths: '*.podspec.json', 74 find: /"name": "([\w-]+)"/, 75 replaceWith: `"name": "${prefix}$1"`, 76 }, 77 { 78 find: /\b(React)/g, 79 replaceWith: `${prefix}$1`, 80 }, 81 { 82 find: /\b(RCT|RNC|RNG|RNR|REA|RNS)(\w+)\b/g, 83 replaceWith: `${prefix}$1$2`, 84 }, 85 { 86 find: /facebook::/g, 87 replaceWith: `${prefix}facebook::`, 88 }, 89 { 90 find: /react::/g, 91 replaceWith: `${prefix}React::`, 92 }, 93 { 94 find: /using namespace (facebook|react)/g, 95 replaceWith: (_, p1) => { 96 return `using namespace ${prefix}${p1 === 'react' ? 'React' : p1}`; 97 }, 98 }, 99 { 100 // Objective-C only, see the comment in the rule below. 101 paths: '*.{h,m,mm}', 102 find: /r(eactTag|eactSubviews|eactSuperview|eactViewController|eactSetFrame|eactAddControllerToClosestParent|eactZIndex)/gi, 103 replaceWith: `${prefix}R$1`, 104 }, 105 { 106 // Swift translates uppercased letters at the beginning of the method name to lowercased letters, we must comply with it. 107 paths: '*.swift', 108 find: /r(eactTag|eactSubviews|eactSuperview|eactViewController|eactSetFrame|eactAddControllerToClosestParent|eactZIndex)/gi, 109 replaceWith: (_, p1) => `${prefix.toLowerCase()}R${p1}`, 110 }, 111 { 112 find: /<jsi\/(.*)\.h>/, 113 replaceWith: `<${prefix}jsi/${prefix}$1.h>`, 114 }, 115 { 116 find: /(JSCExecutorFactory|HermesExecutorFactory)\.h/g, 117 replaceWith: `${prefix}$1.h`, 118 }, 119 { 120 find: /viewForReactTag/g, 121 replaceWith: `viewFor${prefix}ReactTag`, 122 }, 123 { 124 find: /isReactRootView/g, 125 replaceWith: `is${prefix}ReactRootView`, 126 }, 127 { 128 // Prefix only unindented `@objc` (notice `^` and `m` flag in the pattern). Method names shouldn't get prefixed. 129 paths: '*.swift', 130 find: /^@objc\(([^)]+)\)/gm, 131 replaceWith: `@objc(${prefix}$1)`, 132 }, 133 { 134 paths: '*.podspec.json', 135 find: new RegExp(`${prefix}React-${prefix}RCT`, 'g'), 136 replaceWith: `${prefix}React-RCT`, 137 }, 138 { 139 paths: '*.podspec.json', 140 find: new RegExp(`${prefix}React-Core\\/${prefix}RCT`, 'g'), 141 replaceWith: `${prefix}React-Core/RCT`, 142 }, 143 ], 144 }; 145} 146