xref: /expo/packages/@expo/cli/src/export/saveAssets.ts (revision bfcd021e)
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