1import { BundleAssetWithFileHashes } from '@expo/dev-server'; 2import path from 'path'; 3 4import * as Log from '../log'; 5import { chunk } from '../utils/array'; 6import { copyAsync } from '../utils/dir'; 7 8export type ManifestAsset = { fileHashes: string[]; files: string[]; hash: string }; 9 10export type Asset = ManifestAsset | BundleAssetWithFileHashes; 11 12function logAssetTask(projectRoot: string, action: 'uploading' | 'saving', pathName: string) { 13 Log.debug(`${action} ${pathName}`); 14 15 const relativePath = pathName.replace(projectRoot, ''); 16 Log.log(`${action} ${relativePath}`); 17} 18 19function collectAssetPaths(assets: Asset[]): Record<string, string> { 20 // Collect paths by key, also effectively handles duplicates in the array 21 const paths: { [fileHash: string]: string } = {}; 22 assets.forEach((asset) => { 23 asset.files.forEach((path: string, index: number) => { 24 paths[asset.fileHashes[index]] = path; 25 }); 26 }); 27 return paths; 28} 29 30export async function saveAssetsAsync( 31 projectRoot: string, 32 { assets, outputDir }: { assets: Asset[]; outputDir: string } 33) { 34 // Collect paths by key, also effectively handles duplicates in the array 35 const paths = collectAssetPaths(assets); 36 37 // save files one chunk at a time 38 for (const keys of chunk(Object.entries(paths), 5)) { 39 await Promise.all( 40 keys.map(([key, pathName]) => { 41 logAssetTask(projectRoot, 'saving', pathName); 42 // copy file over to assetPath 43 return copyAsync(pathName, path.join(outputDir, 'assets', key)); 44 }) 45 ); 46 } 47 Log.log('Files successfully saved.'); 48} 49