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 // Prefixes `React` with word-boundary and also in `insertReactSubview`, `removeReactSubview`. 79 find: /(\b|insert|remove)(React)/g, 80 replaceWith: `$1${prefix}$2`, 81 }, 82 { 83 find: /\b(RCT|RNC|RNG|RNR|REA|RNS)(\w+)\b/g, 84 replaceWith: `${prefix}$1$2`, 85 }, 86 { 87 find: /facebook::/g, 88 replaceWith: `${prefix}facebook::`, 89 }, 90 { 91 find: /react::/g, 92 replaceWith: `${prefix}React::`, 93 }, 94 { 95 find: /using namespace (facebook|react)/g, 96 replaceWith: (_, p1) => { 97 return `using namespace ${prefix}${p1 === 'react' ? 'React' : p1}`; 98 }, 99 }, 100 { 101 // Objective-C only, see the comment in the rule below. 102 paths: '*.{h,m,mm}', 103 find: /r(eactTag|eactSubviews|eactSuperview|eactViewController|eactSetFrame|eactAddControllerToClosestParent|eactZIndex)/gi, 104 replaceWith: `${prefix}R$1`, 105 }, 106 { 107 // Swift translates uppercased letters at the beginning of the method name to lowercased letters, we must comply with it. 108 paths: '*.swift', 109 find: /r(eactTag|eactSubviews|eactSuperview|eactViewController|eactSetFrame|eactAddControllerToClosestParent|eactZIndex)/gi, 110 replaceWith: (_, p1) => `${prefix.toLowerCase()}R${p1}`, 111 }, 112 { 113 find: /<jsi\/(.*)\.h>/, 114 replaceWith: `<${prefix}jsi/${prefix}$1.h>`, 115 }, 116 { 117 find: /(JSCExecutorFactory|HermesExecutorFactory)\.h/g, 118 replaceWith: `${prefix}$1.h`, 119 }, 120 { 121 find: /viewForReactTag/g, 122 replaceWith: `viewFor${prefix}ReactTag`, 123 }, 124 { 125 find: /isReactRootView/g, 126 replaceWith: `is${prefix}ReactRootView`, 127 }, 128 { 129 // Prefix only unindented `@objc` (notice `^` and `m` flag in the pattern). Method names shouldn't get prefixed. 130 paths: '*.swift', 131 find: /^@objc\(([^)]+)\)/gm, 132 replaceWith: `@objc(${prefix}$1)`, 133 }, 134 { 135 paths: '*.podspec.json', 136 find: new RegExp(`${prefix}React-${prefix}RCT`, 'g'), 137 replaceWith: `${prefix}React-RCT`, 138 }, 139 { 140 paths: '*.podspec.json', 141 find: new RegExp(`${prefix}React-Core\\/${prefix}RCT`, 'g'), 142 replaceWith: `${prefix}React-Core/RCT`, 143 }, 144 ], 145 }; 146} 147