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