1import fs from 'fs-extra'; 2import minimatch from 'minimatch'; 3import path from 'path'; 4 5import { CopyFileOptions, CopyFileResult, StringTransform } from './Transforms.types'; 6import { arrayize } from './Utils'; 7 8export * from './Transforms.types'; 9 10/** 11 * Transforms input string according to the given transform rules. 12 */ 13export function transformString( 14 input: string, 15 transforms: StringTransform[] | null | undefined 16): string { 17 if (!transforms) { 18 return input; 19 } 20 return transforms.reduce( 21 // @ts-ignore @tsapeta: TS gets crazy on `replaceWith` being a function. 22 (acc, { find, replaceWith }) => acc.replace(find, replaceWith), 23 input 24 ); 25} 26 27/** 28 * Copies a file from source directory to target directory with transformed relative path and content. 29 */ 30export async function copyFileWithTransformsAsync( 31 options: CopyFileOptions 32): Promise<CopyFileResult> { 33 const { sourceFile, sourceDirectory, targetDirectory, transforms } = options; 34 const sourcePath = path.join(sourceDirectory, sourceFile); 35 36 // Transform the target path according to rename rules. 37 const targetFile = transformString(sourceFile, transforms.path); 38 const targetPath = path.join(targetDirectory, targetFile); 39 40 // Filter out transforms that don't match paths patterns. 41 const filteredContentTransforms = 42 transforms.content?.filter( 43 ({ paths }) => 44 !paths || 45 arrayize(paths).some((pattern) => minimatch(sourceFile, pattern, { matchBase: true })) 46 ) ?? []; 47 48 // Transform source content. 49 const content = transformString(await fs.readFile(sourcePath, 'utf8'), filteredContentTransforms); 50 51 // Save transformed source file at renamed target path. 52 await fs.outputFile(targetPath, content); 53 54 return { 55 content, 56 targetFile, 57 }; 58} 59