1eeffdb10STomasz Sapetaimport * as fs from 'fs-extra'; 2eeffdb10STomasz Sapetaimport walkSync from 'klaw-sync'; 3eeffdb10STomasz Sapetaimport * as path from 'path'; 4eeffdb10STomasz Sapeta 5eeffdb10STomasz Sapetaimport { ModuleConfiguration } from './ModuleConfiguration'; 6eeffdb10STomasz Sapeta 7eeffdb10STomasz Sapetatype PreparedPrefixes = [nameWithExpoPrefix: string, nameWithoutExpoPrefix: string]; 8eeffdb10STomasz Sapeta 9eeffdb10STomasz Sapeta/** 10eeffdb10STomasz Sapeta * prepares _Expo_ prefixes for specified name 11eeffdb10STomasz Sapeta * @param name module name, e.g. JS package name 12eeffdb10STomasz Sapeta * @param prefix prefix to prepare with, defaults to _Expo_ 13eeffdb10STomasz Sapeta * @returns tuple `[nameWithPrefix: string, nameWithoutPrefix: string]` 14eeffdb10STomasz Sapeta */ 15eeffdb10STomasz Sapetaconst preparePrefixes = (name: string, prefix: string = 'Expo'): PreparedPrefixes => 16eeffdb10STomasz Sapeta name.startsWith(prefix) ? [name, name.substr(prefix.length)] : [`${prefix}${name}`, name]; 17eeffdb10STomasz Sapeta 18eeffdb10STomasz Sapetaconst asyncForEach = async <T>( 19eeffdb10STomasz Sapeta array: T[], 20eeffdb10STomasz Sapeta callback: (value: T, index: number, array: T[]) => Promise<void> 21eeffdb10STomasz Sapeta) => { 22eeffdb10STomasz Sapeta for (let index = 0; index < array.length; index++) { 23eeffdb10STomasz Sapeta await callback(array[index], index, array); 24eeffdb10STomasz Sapeta } 25eeffdb10STomasz Sapeta}; 26eeffdb10STomasz Sapeta 27eeffdb10STomasz Sapeta/** 28eeffdb10STomasz Sapeta * Removes specified files. If one file doesn't exist already, skips it 29eeffdb10STomasz Sapeta * @param directoryPath directory containing files to remove 30eeffdb10STomasz Sapeta * @param filenames array of filenames to remove 31eeffdb10STomasz Sapeta */ 32eeffdb10STomasz Sapetaasync function removeFiles(directoryPath: string, filenames: string[]) { 33eeffdb10STomasz Sapeta await Promise.all(filenames.map((filename) => fs.remove(path.resolve(directoryPath, filename)))); 34eeffdb10STomasz Sapeta} 35eeffdb10STomasz Sapeta 36eeffdb10STomasz Sapeta/** 37eeffdb10STomasz Sapeta * Renames files names 38eeffdb10STomasz Sapeta * @param directoryPath - directory that holds files to be renamed 39eeffdb10STomasz Sapeta * @param extensions - array of extensions for files that would be renamed, must be provided with leading dot or empty for no extension, e.g. ['.html', ''] 40eeffdb10STomasz Sapeta * @param renamings - array of filenames and their replacers 41eeffdb10STomasz Sapeta */ 42eeffdb10STomasz Sapetaconst renameFilesWithExtensions = async ( 43eeffdb10STomasz Sapeta directoryPath: string, 44eeffdb10STomasz Sapeta extensions: string[], 45eeffdb10STomasz Sapeta renamings: { from: string; to: string }[] 46eeffdb10STomasz Sapeta) => { 47eeffdb10STomasz Sapeta await asyncForEach( 48eeffdb10STomasz Sapeta renamings, 49eeffdb10STomasz Sapeta async ({ from, to }) => 50eeffdb10STomasz Sapeta await asyncForEach(extensions, async (extension) => { 51eeffdb10STomasz Sapeta const fromFilename = `${from}${extension}`; 52eeffdb10STomasz Sapeta if (!fs.existsSync(path.join(directoryPath, fromFilename))) { 53eeffdb10STomasz Sapeta return; 54eeffdb10STomasz Sapeta } 55eeffdb10STomasz Sapeta const toFilename = `${to}${extension}`; 56eeffdb10STomasz Sapeta await fs.rename( 57eeffdb10STomasz Sapeta path.join(directoryPath, fromFilename), 58eeffdb10STomasz Sapeta path.join(directoryPath, toFilename) 59eeffdb10STomasz Sapeta ); 60eeffdb10STomasz Sapeta }) 61eeffdb10STomasz Sapeta ); 62eeffdb10STomasz Sapeta}; 63eeffdb10STomasz Sapeta 64eeffdb10STomasz Sapeta/** 65eeffdb10STomasz Sapeta * Enters each file recursively in provided dir and replaces content by invoking provided callback function 66eeffdb10STomasz Sapeta * @param directoryPath - root directory 67eeffdb10STomasz Sapeta * @param replaceFunction - function that converts current content into something different 68eeffdb10STomasz Sapeta */ 69eeffdb10STomasz Sapetaconst replaceContents = async ( 70eeffdb10STomasz Sapeta directoryPath: string, 71eeffdb10STomasz Sapeta replaceFunction: (contentOfSingleFile: string) => string 72eeffdb10STomasz Sapeta) => { 73eeffdb10STomasz Sapeta await Promise.all( 74eeffdb10STomasz Sapeta walkSync(directoryPath, { nodir: true }).map((file) => 75eeffdb10STomasz Sapeta replaceContent(file.path, replaceFunction) 76eeffdb10STomasz Sapeta ) 77eeffdb10STomasz Sapeta ); 78eeffdb10STomasz Sapeta}; 79eeffdb10STomasz Sapeta 80eeffdb10STomasz Sapeta/** 81eeffdb10STomasz Sapeta * Replaces content in file. Does nothing if the file doesn't exist 82eeffdb10STomasz Sapeta * @param filePath - provided file 83eeffdb10STomasz Sapeta * @param replaceFunction - function that converts current content into something different 84eeffdb10STomasz Sapeta */ 85eeffdb10STomasz Sapetaconst replaceContent = async ( 86eeffdb10STomasz Sapeta filePath: string, 87eeffdb10STomasz Sapeta replaceFunction: (contentOfSingleFile: string) => string 88eeffdb10STomasz Sapeta) => { 89eeffdb10STomasz Sapeta if (!fs.existsSync(filePath)) { 90eeffdb10STomasz Sapeta return; 91eeffdb10STomasz Sapeta } 92eeffdb10STomasz Sapeta 93eeffdb10STomasz Sapeta const content = await fs.readFile(filePath, 'utf8'); 94eeffdb10STomasz Sapeta const newContent = replaceFunction(content); 95eeffdb10STomasz Sapeta if (newContent !== content) { 96eeffdb10STomasz Sapeta await fs.writeFile(filePath, newContent); 97eeffdb10STomasz Sapeta } 98eeffdb10STomasz Sapeta}; 99eeffdb10STomasz Sapeta 100eeffdb10STomasz Sapeta/** 101eeffdb10STomasz Sapeta * Removes all empty subdirs up to and including dirPath 102eeffdb10STomasz Sapeta * Recursively enters all subdirs and removes them if one is empty or cantained only empty subdirs 103eeffdb10STomasz Sapeta * @param dirPath - directory path that is being inspected 104eeffdb10STomasz Sapeta * @returns whether the given base directory and any empty subdirectories were deleted or not 105eeffdb10STomasz Sapeta */ 106eeffdb10STomasz Sapetaconst removeUponEmptyOrOnlyEmptySubdirs = async (dirPath: string): Promise<boolean> => { 107eeffdb10STomasz Sapeta const contents = await fs.readdir(dirPath); 108eeffdb10STomasz Sapeta const results = await Promise.all( 109eeffdb10STomasz Sapeta contents.map(async (file) => { 110eeffdb10STomasz Sapeta const filePath = path.join(dirPath, file); 111eeffdb10STomasz Sapeta const fileStats = await fs.lstat(filePath); 112eeffdb10STomasz Sapeta return fileStats.isDirectory() && (await removeUponEmptyOrOnlyEmptySubdirs(filePath)); 113eeffdb10STomasz Sapeta }) 114eeffdb10STomasz Sapeta ); 115eeffdb10STomasz Sapeta const isRemovable = results.reduce((acc, current) => acc && current, true); 116eeffdb10STomasz Sapeta if (isRemovable) { 117eeffdb10STomasz Sapeta await fs.remove(dirPath); 118eeffdb10STomasz Sapeta } 119eeffdb10STomasz Sapeta return isRemovable; 120eeffdb10STomasz Sapeta}; 121eeffdb10STomasz Sapeta 122eeffdb10STomasz Sapeta/** 123eeffdb10STomasz Sapeta * Prepares iOS part, mainly by renaming all files and some template word in files 124eeffdb10STomasz Sapeta * Versioning is done automatically based on package.json from JS/TS part 125eeffdb10STomasz Sapeta * @param modulePath - module directory 126eeffdb10STomasz Sapeta * @param configuration - naming configuration 127eeffdb10STomasz Sapeta */ 128eeffdb10STomasz Sapetaasync function configureIOS( 129eeffdb10STomasz Sapeta modulePath: string, 130eeffdb10STomasz Sapeta { podName, jsPackageName, viewManager }: ModuleConfiguration 131eeffdb10STomasz Sapeta) { 132eeffdb10STomasz Sapeta const iosPath = path.join(modulePath, 'ios'); 133eeffdb10STomasz Sapeta 134eeffdb10STomasz Sapeta // remove ViewManager from template 135eeffdb10STomasz Sapeta if (!viewManager) { 136*74d26049SŁukasz Kosmaty await removeFiles(iosPath, [ 137eeffdb10STomasz Sapeta `EXModuleTemplateView.h`, 138eeffdb10STomasz Sapeta `EXModuleTemplateView.m`, 139eeffdb10STomasz Sapeta `EXModuleTemplateViewManager.h`, 140eeffdb10STomasz Sapeta `EXModuleTemplateViewManager.m`, 141eeffdb10STomasz Sapeta ]); 142eeffdb10STomasz Sapeta } 143eeffdb10STomasz Sapeta 144eeffdb10STomasz Sapeta await renameFilesWithExtensions( 145*74d26049SŁukasz Kosmaty iosPath, 146*74d26049SŁukasz Kosmaty ['.swift'], 147eeffdb10STomasz Sapeta [ 148*74d26049SŁukasz Kosmaty { from: 'ExpoModuleTemplateModule', to: `${podName}Module` }, 149eeffdb10STomasz Sapeta { 150*74d26049SŁukasz Kosmaty from: 'ExpoModuleTemplateView', 151eeffdb10STomasz Sapeta to: `${podName}View`, 152eeffdb10STomasz Sapeta }, 153eeffdb10STomasz Sapeta { 154*74d26049SŁukasz Kosmaty from: 'ExpoModuleTemplateViewManager', 155eeffdb10STomasz Sapeta to: `${podName}ViewManager`, 156eeffdb10STomasz Sapeta }, 157eeffdb10STomasz Sapeta ] 158eeffdb10STomasz Sapeta ); 159eeffdb10STomasz Sapeta await renameFilesWithExtensions( 160eeffdb10STomasz Sapeta iosPath, 161eeffdb10STomasz Sapeta ['', '.podspec'], 162*74d26049SŁukasz Kosmaty [{ from: 'ExpoModuleTemplate', to: `${podName}` }] 163eeffdb10STomasz Sapeta ); 164eeffdb10STomasz Sapeta await replaceContents(iosPath, (singleFileContent) => 165eeffdb10STomasz Sapeta singleFileContent 166*74d26049SŁukasz Kosmaty .replace(/ExpoModuleTemplate/g, podName) 167eeffdb10STomasz Sapeta .replace(/ExpoModuleTemplate/g, jsPackageName) 168eeffdb10STomasz Sapeta ); 169eeffdb10STomasz Sapeta} 170eeffdb10STomasz Sapeta 171eeffdb10STomasz Sapeta/** 172eeffdb10STomasz Sapeta * Gets path to Android source base dir: android/src/main/[java|kotlin] 173eeffdb10STomasz Sapeta * Defaults to Java path if both exist 174eeffdb10STomasz Sapeta * @param androidPath path do module android/ directory 175eeffdb10STomasz Sapeta * @param flavor package flavor e.g main, test. Defaults to main 176eeffdb10STomasz Sapeta * @returns path to flavor source base directory 177eeffdb10STomasz Sapeta */ 178eeffdb10STomasz Sapetafunction findAndroidSourceDir(androidPath: string, flavor: string = 'main'): string { 179eeffdb10STomasz Sapeta const androidSrcPathBase = path.join(androidPath, 'src', flavor); 180eeffdb10STomasz Sapeta 181eeffdb10STomasz Sapeta const javaExists = fs.pathExistsSync(path.join(androidSrcPathBase, 'java')); 182eeffdb10STomasz Sapeta const kotlinExists = fs.pathExistsSync(path.join(androidSrcPathBase, 'kotlin')); 183eeffdb10STomasz Sapeta 184eeffdb10STomasz Sapeta if (!javaExists && !kotlinExists) { 185eeffdb10STomasz Sapeta throw new Error( 186eeffdb10STomasz Sapeta `Invalid template. Android source directory not found: ${androidSrcPathBase}/[java|kotlin]` 187eeffdb10STomasz Sapeta ); 188eeffdb10STomasz Sapeta } 189eeffdb10STomasz Sapeta 190eeffdb10STomasz Sapeta return path.join(androidSrcPathBase, javaExists ? 'java' : 'kotlin'); 191eeffdb10STomasz Sapeta} 192eeffdb10STomasz Sapeta 193eeffdb10STomasz Sapeta/** 194eeffdb10STomasz Sapeta * Finds java package name based on directory structure 195eeffdb10STomasz Sapeta * @param flavorSrcPath Path to source base directory: e.g. android/src/main/java 196eeffdb10STomasz Sapeta * @returns java package name 197eeffdb10STomasz Sapeta */ 198eeffdb10STomasz Sapetafunction findTemplateAndroidPackage(flavorSrcPath: string) { 199eeffdb10STomasz Sapeta const srcFiles = walkSync(flavorSrcPath, { 200eeffdb10STomasz Sapeta filter: (item) => item.path.endsWith('.kt') || item.path.endsWith('.java'), 201eeffdb10STomasz Sapeta nodir: true, 202eeffdb10STomasz Sapeta traverseAll: true, 203eeffdb10STomasz Sapeta }); 204eeffdb10STomasz Sapeta 205eeffdb10STomasz Sapeta if (srcFiles.length === 0) { 206eeffdb10STomasz Sapeta throw new Error('No Android source files found in the template'); 207eeffdb10STomasz Sapeta } 208eeffdb10STomasz Sapeta 209eeffdb10STomasz Sapeta // srcFiles[0] will always be at the most top-level of the package structure 210eeffdb10STomasz Sapeta const packageDirNames = path.relative(flavorSrcPath, srcFiles[0].path).split('/').slice(0, -1); 211eeffdb10STomasz Sapeta 212eeffdb10STomasz Sapeta if (packageDirNames.length === 0) { 213eeffdb10STomasz Sapeta throw new Error('Template Android sources must be within a package.'); 214eeffdb10STomasz Sapeta } 215eeffdb10STomasz Sapeta 216eeffdb10STomasz Sapeta return packageDirNames.join('.'); 217eeffdb10STomasz Sapeta} 218eeffdb10STomasz Sapeta 219eeffdb10STomasz Sapeta/** 220eeffdb10STomasz Sapeta * Prepares Android part, mainly by renaming all files and template words in files 221eeffdb10STomasz Sapeta * Sets all versions in Gradle to 1.0.0 222eeffdb10STomasz Sapeta * @param modulePath - module directory 223eeffdb10STomasz Sapeta * @param configuration - naming configuration 224eeffdb10STomasz Sapeta */ 225eeffdb10STomasz Sapetaasync function configureAndroid( 226eeffdb10STomasz Sapeta modulePath: string, 227eeffdb10STomasz Sapeta { javaPackage, jsPackageName, viewManager }: ModuleConfiguration 228eeffdb10STomasz Sapeta) { 229eeffdb10STomasz Sapeta const androidPath = path.join(modulePath, 'android'); 230eeffdb10STomasz Sapeta const [, moduleName] = preparePrefixes(jsPackageName, 'Expo'); 231eeffdb10STomasz Sapeta 232eeffdb10STomasz Sapeta const androidSrcPath = findAndroidSourceDir(androidPath); 233eeffdb10STomasz Sapeta const templateJavaPackage = findTemplateAndroidPackage(androidSrcPath); 234eeffdb10STomasz Sapeta 235eeffdb10STomasz Sapeta const sourceFilesPath = path.join(androidSrcPath, ...templateJavaPackage.split('.')); 236eeffdb10STomasz Sapeta const destinationFilesPath = path.join(androidSrcPath, ...javaPackage.split('.')); 237eeffdb10STomasz Sapeta 238eeffdb10STomasz Sapeta // remove ViewManager from template 239eeffdb10STomasz Sapeta if (!viewManager) { 240eeffdb10STomasz Sapeta removeFiles(sourceFilesPath, [`ModuleTemplateView.kt`, `ModuleTemplateViewManager.kt`]); 241eeffdb10STomasz Sapeta 242eeffdb10STomasz Sapeta replaceContent(path.join(sourceFilesPath, 'ModuleTemplatePackage.kt'), (packageContent) => 243eeffdb10STomasz Sapeta packageContent 244eeffdb10STomasz Sapeta .replace(/(^\s+)+(^.*?){1}createViewManagers[\s\W\w]+?\}/m, '') 245eeffdb10STomasz Sapeta .replace(/^.*ViewManager$/, '') 246eeffdb10STomasz Sapeta ); 247eeffdb10STomasz Sapeta } 248eeffdb10STomasz Sapeta 249eeffdb10STomasz Sapeta await fs.mkdirp(destinationFilesPath); 250eeffdb10STomasz Sapeta await fs.copy(sourceFilesPath, destinationFilesPath); 251eeffdb10STomasz Sapeta 252eeffdb10STomasz Sapeta // Remove leaf directory content 253eeffdb10STomasz Sapeta await fs.remove(sourceFilesPath); 254eeffdb10STomasz Sapeta // Cleanup all empty subdirs up to template package root dir 255eeffdb10STomasz Sapeta await removeUponEmptyOrOnlyEmptySubdirs( 256eeffdb10STomasz Sapeta path.join(androidSrcPath, templateJavaPackage.split('.')[0]) 257eeffdb10STomasz Sapeta ); 258eeffdb10STomasz Sapeta 259eeffdb10STomasz Sapeta // prepare tests 260eeffdb10STomasz Sapeta if (fs.existsSync(path.resolve(androidPath, 'src', 'test'))) { 261eeffdb10STomasz Sapeta const androidTestPath = findAndroidSourceDir(androidPath, 'test'); 262eeffdb10STomasz Sapeta const templateTestPackage = findTemplateAndroidPackage(androidTestPath); 263eeffdb10STomasz Sapeta const testSourcePath = path.join(androidTestPath, ...templateTestPackage.split('.')); 264eeffdb10STomasz Sapeta const testDestinationPath = path.join(androidTestPath, ...javaPackage.split('.')); 265eeffdb10STomasz Sapeta 266eeffdb10STomasz Sapeta await fs.mkdirp(testDestinationPath); 267eeffdb10STomasz Sapeta await fs.copy(testSourcePath, testDestinationPath); 268eeffdb10STomasz Sapeta await fs.remove(testSourcePath); 269eeffdb10STomasz Sapeta await removeUponEmptyOrOnlyEmptySubdirs( 270eeffdb10STomasz Sapeta path.join(androidTestPath, templateTestPackage.split('.')[0]) 271eeffdb10STomasz Sapeta ); 272eeffdb10STomasz Sapeta 273eeffdb10STomasz Sapeta await replaceContents(testDestinationPath, (singleFileContent) => 274eeffdb10STomasz Sapeta singleFileContent.replace(new RegExp(templateTestPackage, 'g'), javaPackage) 275eeffdb10STomasz Sapeta ); 276eeffdb10STomasz Sapeta 277eeffdb10STomasz Sapeta await renameFilesWithExtensions( 278eeffdb10STomasz Sapeta testDestinationPath, 279eeffdb10STomasz Sapeta ['.kt', '.java'], 280eeffdb10STomasz Sapeta [{ from: 'ModuleTemplateModuleTest', to: `${moduleName}ModuleTest` }] 281eeffdb10STomasz Sapeta ); 282eeffdb10STomasz Sapeta } 283eeffdb10STomasz Sapeta 284eeffdb10STomasz Sapeta // Replace contents of destination files 285eeffdb10STomasz Sapeta await replaceContents(androidPath, (singleFileContent) => 286eeffdb10STomasz Sapeta singleFileContent 287eeffdb10STomasz Sapeta .replace(new RegExp(templateJavaPackage, 'g'), javaPackage) 288eeffdb10STomasz Sapeta .replace(/ModuleTemplate/g, moduleName) 289eeffdb10STomasz Sapeta .replace(/ExpoModuleTemplate/g, jsPackageName) 290eeffdb10STomasz Sapeta ); 291eeffdb10STomasz Sapeta await replaceContent(path.join(androidPath, 'build.gradle'), (gradleContent) => 292eeffdb10STomasz Sapeta gradleContent 293eeffdb10STomasz Sapeta .replace(/\bversion = ['"][\w.-]+['"]/, "version = '1.0.0'") 294eeffdb10STomasz Sapeta .replace(/versionCode \d+/, 'versionCode 1') 295eeffdb10STomasz Sapeta .replace(/versionName ['"][\w.-]+['"]/, "versionName '1.0.0'") 296eeffdb10STomasz Sapeta ); 297eeffdb10STomasz Sapeta await renameFilesWithExtensions( 298eeffdb10STomasz Sapeta destinationFilesPath, 299eeffdb10STomasz Sapeta ['.kt', '.java'], 300eeffdb10STomasz Sapeta [ 301eeffdb10STomasz Sapeta { from: 'ModuleTemplateModule', to: `${moduleName}Module` }, 302eeffdb10STomasz Sapeta { from: 'ModuleTemplatePackage', to: `${moduleName}Package` }, 303eeffdb10STomasz Sapeta { from: 'ModuleTemplateView', to: `${moduleName}View` }, 304eeffdb10STomasz Sapeta { from: 'ModuleTemplateViewManager', to: `${moduleName}ViewManager` }, 305eeffdb10STomasz Sapeta ] 306eeffdb10STomasz Sapeta ); 307eeffdb10STomasz Sapeta} 308eeffdb10STomasz Sapeta 309eeffdb10STomasz Sapeta/** 310eeffdb10STomasz Sapeta * Prepares TS part. 311eeffdb10STomasz Sapeta * @param modulePath - module directory 312eeffdb10STomasz Sapeta * @param configuration - naming configuration 313eeffdb10STomasz Sapeta */ 314eeffdb10STomasz Sapetaasync function configureTS( 315eeffdb10STomasz Sapeta modulePath: string, 316eeffdb10STomasz Sapeta { jsPackageName, viewManager }: ModuleConfiguration 317eeffdb10STomasz Sapeta) { 318eeffdb10STomasz Sapeta const [moduleNameWithExpoPrefix, moduleName] = preparePrefixes(jsPackageName); 319eeffdb10STomasz Sapeta 320eeffdb10STomasz Sapeta const tsPath = path.join(modulePath, 'src'); 321eeffdb10STomasz Sapeta 322eeffdb10STomasz Sapeta // remove View Manager from template 323eeffdb10STomasz Sapeta if (!viewManager) { 324eeffdb10STomasz Sapeta await removeFiles(path.join(tsPath), [ 325eeffdb10STomasz Sapeta 'ExpoModuleTemplateView.tsx', 326eeffdb10STomasz Sapeta 'ExpoModuleTemplateNativeView.ts', 327eeffdb10STomasz Sapeta 'ExpoModuleTemplateNativeView.web.tsx', 328eeffdb10STomasz Sapeta ]); 329eeffdb10STomasz Sapeta await replaceContent(path.join(tsPath, 'ModuleTemplate.ts'), (fileContent) => 330eeffdb10STomasz Sapeta fileContent.replace(/(^\s+)+(^.*?){1}ExpoModuleTemplateView.*$/m, '') 331eeffdb10STomasz Sapeta ); 332eeffdb10STomasz Sapeta } 333eeffdb10STomasz Sapeta 334eeffdb10STomasz Sapeta await renameFilesWithExtensions( 335eeffdb10STomasz Sapeta path.join(tsPath, '__tests__'), 336eeffdb10STomasz Sapeta ['.ts'], 337eeffdb10STomasz Sapeta [{ from: 'ModuleTemplate-test', to: `${moduleName}-test` }] 338eeffdb10STomasz Sapeta ); 339eeffdb10STomasz Sapeta await renameFilesWithExtensions( 340eeffdb10STomasz Sapeta tsPath, 341eeffdb10STomasz Sapeta ['.tsx', '.ts'], 342eeffdb10STomasz Sapeta [ 343eeffdb10STomasz Sapeta { from: 'ExpoModuleTemplateView', to: `${moduleNameWithExpoPrefix}View` }, 344eeffdb10STomasz Sapeta { from: 'ExpoModuleTemplateNativeView', to: `${moduleNameWithExpoPrefix}NativeView` }, 345eeffdb10STomasz Sapeta { from: 'ExpoModuleTemplateNativeView.web', to: `${moduleNameWithExpoPrefix}NativeView.web` }, 346eeffdb10STomasz Sapeta { from: 'ExpoModuleTemplate', to: moduleNameWithExpoPrefix }, 347eeffdb10STomasz Sapeta { from: 'ExpoModuleTemplate.web', to: `${moduleNameWithExpoPrefix}.web` }, 348eeffdb10STomasz Sapeta { from: 'ModuleTemplate', to: moduleName }, 349eeffdb10STomasz Sapeta { from: 'ModuleTemplate.types', to: `${moduleName}.types` }, 350eeffdb10STomasz Sapeta ] 351eeffdb10STomasz Sapeta ); 352eeffdb10STomasz Sapeta 353eeffdb10STomasz Sapeta await replaceContents(tsPath, (singleFileContent) => 354eeffdb10STomasz Sapeta singleFileContent 355eeffdb10STomasz Sapeta .replace(/ExpoModuleTemplate/g, moduleNameWithExpoPrefix) 356eeffdb10STomasz Sapeta .replace(/ModuleTemplate/g, moduleName) 357eeffdb10STomasz Sapeta ); 358eeffdb10STomasz Sapeta} 359eeffdb10STomasz Sapeta 360eeffdb10STomasz Sapeta/** 361eeffdb10STomasz Sapeta * Prepares files for npm (package.json and README.md). 362eeffdb10STomasz Sapeta * @param modulePath - module directory 363eeffdb10STomasz Sapeta * @param configuration - naming configuration 364eeffdb10STomasz Sapeta */ 365eeffdb10STomasz Sapetaasync function configureNPM( 366eeffdb10STomasz Sapeta modulePath: string, 367eeffdb10STomasz Sapeta { npmModuleName, podName, jsPackageName }: ModuleConfiguration 368eeffdb10STomasz Sapeta) { 369eeffdb10STomasz Sapeta const [, moduleName] = preparePrefixes(jsPackageName); 370eeffdb10STomasz Sapeta 371eeffdb10STomasz Sapeta await replaceContent(path.join(modulePath, 'package.json'), (singleFileContent) => 372eeffdb10STomasz Sapeta singleFileContent 373eeffdb10STomasz Sapeta .replace(/expo-module-template/g, npmModuleName) 374eeffdb10STomasz Sapeta .replace(/"version": "[\w.-]+"/, '"version": "1.0.0"') 375eeffdb10STomasz Sapeta .replace(/ExpoModuleTemplate/g, jsPackageName) 376eeffdb10STomasz Sapeta .replace(/ModuleTemplate/g, moduleName) 377eeffdb10STomasz Sapeta ); 378eeffdb10STomasz Sapeta await replaceContent(path.join(modulePath, 'README.md'), (readmeContent) => 379eeffdb10STomasz Sapeta readmeContent 380eeffdb10STomasz Sapeta .replace(/expo-module-template/g, npmModuleName) 381eeffdb10STomasz Sapeta .replace(/ExpoModuleTemplate/g, jsPackageName) 382eeffdb10STomasz Sapeta .replace(/EXModuleTemplate/g, podName) 383eeffdb10STomasz Sapeta ); 384eeffdb10STomasz Sapeta} 385eeffdb10STomasz Sapeta 386eeffdb10STomasz Sapeta/** 387eeffdb10STomasz Sapeta * Configures TS, Android and iOS parts of generated module mostly by applying provided renamings. 388eeffdb10STomasz Sapeta * @param modulePath - module directory 389eeffdb10STomasz Sapeta * @param configuration - naming configuration 390eeffdb10STomasz Sapeta */ 391eeffdb10STomasz Sapetaexport default async function configureModule( 392eeffdb10STomasz Sapeta newModulePath: string, 393eeffdb10STomasz Sapeta configuration: ModuleConfiguration 394eeffdb10STomasz Sapeta) { 395eeffdb10STomasz Sapeta await configureNPM(newModulePath, configuration); 396eeffdb10STomasz Sapeta await configureTS(newModulePath, configuration); 397eeffdb10STomasz Sapeta await configureAndroid(newModulePath, configuration); 398eeffdb10STomasz Sapeta await configureIOS(newModulePath, configuration); 399eeffdb10STomasz Sapeta} 400