1import fs from 'fs';
2import Server from 'metro/src/Server';
3import output from 'metro/src/shared/output/bundle';
4import type { BundleOptions } from 'metro/src/shared/types';
5import path from 'path';
6
7import { Options } from './resolveOptions';
8import { Log } from '../../log';
9import { loadMetroConfigAsync } from '../../start/server/metro/instantiateMetro';
10import { importCliSaveAssetsFromProject } from '../../start/server/metro/resolveFromProject';
11import { setNodeEnv } from '../../utils/nodeEnv';
12import { getAssets } from '../fork-bundleAsync';
13
14export async function exportEmbedAsync(projectRoot: string, options: Options) {
15  setNodeEnv(options.dev ? 'development' : 'production');
16  require('@expo/env').load(projectRoot);
17
18  const { config } = await loadMetroConfigAsync(
19    projectRoot,
20    {
21      maxWorkers: options.maxWorkers,
22      resetCache: options.resetCache,
23      config: options.config,
24    },
25    {
26      isExporting: true,
27    }
28  );
29
30  // NOTE(EvanBacon): This may need to be adjusted in the future if want to support basePath on native
31  // platforms when doing production embeds (unlikely).
32  const saveAssets = importCliSaveAssetsFromProject(projectRoot);
33
34  let sourceMapUrl = options.sourcemapOutput;
35  if (sourceMapUrl && !options.sourcemapUseAbsolutePath) {
36    sourceMapUrl = path.basename(sourceMapUrl);
37  }
38
39  const bundleRequest = {
40    ...Server.DEFAULT_BUNDLE_OPTIONS,
41    entryFile: options.entryFile,
42    sourceMapUrl,
43    dev: options.dev,
44    minify: !!options.minify,
45    platform: options.platform,
46    unstable_transformProfile:
47      options.unstableTransformProfile as BundleOptions['unstable_transformProfile'],
48  };
49
50  const server = new Server(config, {
51    watch: false,
52  });
53
54  try {
55    const bundle = await server.build({
56      ...bundleRequest,
57      bundleType: 'bundle',
58    });
59
60    fs.mkdirSync(path.dirname(options.bundleOutput), { recursive: true, mode: 0o755 });
61
62    // Persist bundle and source maps.
63    await output.save(bundle, options, Log.log);
64
65    // Save the assets of the bundle
66    const outputAssets = await getAssets(server, {
67      ...bundleRequest,
68      bundleType: 'todo',
69    });
70
71    await saveAssets(outputAssets, options.platform, options.assetsDest, options.assetCatalogDest);
72  } finally {
73    server.end();
74  }
75}
76