"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "exportAppAsync", {
    enumerable: true,
    get: ()=>exportAppAsync
});
function _config() {
    const data = require("@expo/config");
    _config = function() {
        return data;
    };
    return data;
}
function _assert() {
    const data = /*#__PURE__*/ _interopRequireDefault(require("assert"));
    _assert = function() {
        return data;
    };
    return data;
}
function _chalk() {
    const data = /*#__PURE__*/ _interopRequireDefault(require("chalk"));
    _chalk = function() {
        return data;
    };
    return data;
}
function _fs() {
    const data = /*#__PURE__*/ _interopRequireDefault(require("fs"));
    _fs = function() {
        return data;
    };
    return data;
}
function _path() {
    const data = /*#__PURE__*/ _interopRequireDefault(require("path"));
    _path = function() {
        return data;
    };
    return data;
}
const _createMetadataJson = require("./createMetadataJson");
const _exportAssets = require("./exportAssets");
const _exportDomComponents = require("./exportDomComponents");
const _exportHermes = require("./exportHermes");
const _exportStaticAsync = require("./exportStaticAsync");
const _favicon = require("./favicon");
const _getPublicExpoManifest = require("./getPublicExpoManifest");
const _publicFolder = require("./publicFolder");
const _saveAssets = require("./saveAssets");
const _writeContents = require("./writeContents");
const _log = /*#__PURE__*/ _interopRequireWildcard(require("../log"));
const _webSupportProjectPrerequisite = require("../start/doctor/web/WebSupportProjectPrerequisite");
const _devServerManager = require("../start/server/DevServerManager");
const _metroBundlerDevServer = require("../start/server/metro/MetroBundlerDevServer");
const _router = require("../start/server/metro/router");
const _serializeHtml = require("../start/server/metro/serializeHtml");
const _manifestMiddleware = require("../start/server/middleware/ManifestMiddleware");
const _metroOptions = require("../start/server/middleware/metroOptions");
const _webTemplate = require("../start/server/webTemplate");
const _env = require("../utils/env");
const _errors = require("../utils/errors");
const _nodeEnv = require("../utils/nodeEnv");
function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
function _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interopRequireWildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {};
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
async function exportAppAsync(projectRoot, { platforms , outputDir , clear , dev , dumpAssetmap , sourceMaps , minify , bytecode , maxWorkers , skipSSG  }) {
    var ref, ref1;
    (0, _nodeEnv.setNodeEnv)(dev ? "development" : "production");
    require("@expo/env").load(projectRoot);
    const projectConfig = (0, _config().getConfig)(projectRoot);
    const exp = await (0, _getPublicExpoManifest.getPublicExpoManifestAsync)(projectRoot, {
        // Web doesn't require validation.
        skipValidation: platforms.length === 1 && platforms[0] === "web"
    });
    if (platforms.includes("web")) {
        await new _webSupportProjectPrerequisite.WebSupportProjectPrerequisite(projectRoot).assertAsync();
    }
    const useServerRendering = [
        "static",
        "server"
    ].includes(((ref = exp.web) == null ? void 0 : ref.output) ?? "");
    if (skipSSG && ((ref1 = exp.web) == null ? void 0 : ref1.output) !== "server") {
        throw new _errors.CommandError("--no-ssg can only be used with `web.output: server`");
    }
    const baseUrl = (0, _metroOptions.getBaseUrlFromExpoConfig)(exp);
    if (!bytecode && (platforms.includes("ios") || platforms.includes("android"))) {
        _log.warn(`Bytecode makes the app startup faster, disabling bytecode is highly discouraged and should only be used for debugging purposes.`);
    }
    // Print out logs
    if (baseUrl) {
        _log.log();
        _log.log(_chalk().default.gray`Using (experimental) base path: ${baseUrl}`);
        // Warn if not using an absolute path.
        if (!baseUrl.startsWith("/")) {
            _log.log(_chalk().default.yellow`  Base path does not start with a slash. Requests will not be absolute.`);
        }
    }
    const mode = dev ? "development" : "production";
    const publicPath = _path().default.resolve(projectRoot, _env.env.EXPO_PUBLIC_FOLDER);
    const outputPath = _path().default.resolve(projectRoot, outputDir);
    // Write the JS bundles to disk, and get the bundle file names (this could change with async chunk loading support).
    const files = new Map();
    const devServerManager = await _devServerManager.DevServerManager.startMetroAsync(projectRoot, {
        minify,
        mode,
        port: 8081,
        isExporting: true,
        location: {},
        resetDevServer: clear,
        maxWorkers
    });
    const devServer = devServerManager.getDefaultDevServer();
    (0, _assert().default)(devServer instanceof _metroBundlerDevServer.MetroBundlerDevServer);
    const bundles = {};
    const domComponentAssetsMetadata = {};
    const spaPlatforms = // TODO: Support server and static rendering for server component exports.
    useServerRendering && !devServer.isReactServerComponentsEnabled ? platforms.filter((platform)=>platform !== "web") : platforms;
    try {
        if (devServer.isReactServerComponentsEnabled) {
            // In RSC mode, we only need these to be in the client dir.
            // TODO: Merge back with other copy after we add SSR.
            try {
                await (0, _publicFolder.copyPublicFolderAsync)(publicPath, _path().default.join(outputPath, "client"));
            } catch (error) {
                _log.error("Failed to copy public directory to dist directory");
                throw error;
            }
        } else {
            // NOTE(kitten): The public folder is currently always copied, regardless of targetDomain
            // split. Hence, there's another separate `copyPublicFolderAsync` call below for `web`
            await (0, _publicFolder.copyPublicFolderAsync)(publicPath, outputPath);
        }
        let templateHtml;
        // Can be empty during web-only SSG.
        if (spaPlatforms.length) {
            await Promise.all(spaPlatforms.map(async (platform)=>{
                var ref;
                // Assert early so the user doesn't have to wait until bundling is complete to find out that
                // Hermes won't be available.
                const isHermes = (0, _exportHermes.isEnableHermesManaged)(exp, platform);
                if (isHermes) {
                    await (0, _exportHermes.assertEngineMismatchAsync)(projectRoot, exp, platform);
                }
                // Run metro bundler and create the JS bundles/source maps.
                const bundle = await devServer.nativeExportBundleAsync({
                    platform,
                    splitChunks: !_env.env.EXPO_NO_BUNDLE_SPLITTING && (devServer.isReactServerComponentsEnabled && !bytecode || platform === "web"),
                    mainModuleName: (0, _manifestMiddleware.getEntryWithServerRoot)(projectRoot, {
                        platform,
                        pkg: projectConfig.pkg
                    }),
                    mode: dev ? "development" : "production",
                    engine: isHermes ? "hermes" : undefined,
                    serializerIncludeMaps: sourceMaps,
                    bytecode: bytecode && isHermes,
                    reactCompiler: !!((ref = exp.experiments) == null ? void 0 : ref.reactCompiler)
                }, files);
                bundles[platform] = bundle;
                (0, _saveAssets.getFilesFromSerialAssets)(bundle.artifacts, {
                    includeSourceMaps: sourceMaps,
                    files,
                    isServerHosted: devServer.isReactServerComponentsEnabled
                });
                // TODO: Remove duplicates...
                const expoDomComponentReferences = bundle.artifacts.map((artifact)=>Array.isArray(artifact.metadata.expoDomComponentReferences) ? artifact.metadata.expoDomComponentReferences : []).flat();
                await Promise.all(// TODO: Make a version of this which uses `this.metro.getBundler().buildGraphForEntries([])` to bundle all the DOM components at once.
                expoDomComponentReferences.map(async (filePath)=>{
                    const { bundle: platformDomComponentsBundle , htmlOutputName  } = await (0, _exportDomComponents.exportDomComponentAsync)({
                        filePath,
                        projectRoot,
                        dev,
                        devServer,
                        isHermes,
                        includeSourceMaps: sourceMaps,
                        exp,
                        files,
                        useMd5Filename: true
                    });
                    // Merge the assets from the DOM component into the output assets.
                    // @ts-expect-error: mutate assets
                    bundle.assets.push(...platformDomComponentsBundle.assets);
                    (0, _exportDomComponents.transformNativeBundleForMd5Filename)({
                        domComponentReference: filePath,
                        nativeBundle: bundle,
                        files,
                        htmlOutputName
                    });
                    domComponentAssetsMetadata[platform] = [
                        ...await (0, _exportDomComponents.addDomBundleToMetadataAsync)(platformDomComponentsBundle),
                        ...(0, _exportDomComponents.transformDomEntryForMd5Filename)({
                            files,
                            htmlOutputName
                        }), 
                    ];
                }));
                if (platform === "web") {
                    // TODO: Unify with exportStaticAsync
                    // TODO: Maybe move to the serializer.
                    let html = await (0, _serializeHtml.serializeHtmlWithAssets)({
                        isExporting: true,
                        resources: bundle.artifacts,
                        template: await (0, _webTemplate.createTemplateHtmlFromExpoConfigAsync)(projectRoot, {
                            scripts: [],
                            cssLinks: [],
                            exp: projectConfig.exp
                        }),
                        baseUrl
                    });
                    // Add the favicon assets to the HTML.
                    const modifyHtml = await (0, _favicon.getVirtualFaviconAssetsAsync)(projectRoot, {
                        outputDir,
                        baseUrl,
                        files,
                        exp: projectConfig.exp
                    });
                    if (modifyHtml) {
                        html = modifyHtml(html);
                    }
                    // HACK: This is used for adding SSR shims in React Server Components.
                    templateHtml = html;
                    // Generate SPA-styled HTML file.
                    // If web exists, then write the template HTML file.
                    files.set("index.html", {
                        contents: html,
                        targetDomain: devServer.isReactServerComponentsEnabled ? "server" : "client"
                    });
                }
            }));
            if (devServer.isReactServerComponentsEnabled) {
                const isWeb = platforms.includes("web");
                await (0, _exportStaticAsync.exportApiRoutesStandaloneAsync)(devServer, {
                    files,
                    platform: "web",
                    apiRoutesOnly: !isWeb,
                    templateHtml
                });
            }
            // TODO: Use same asset system across platforms again.
            const { assets , embeddedHashSet  } = await (0, _exportAssets.exportAssetsAsync)(projectRoot, {
                files,
                exp,
                outputDir: outputPath,
                bundles,
                baseUrl
            });
            if (dumpAssetmap) {
                _log.log("Creating asset map");
                files.set("assetmap.json", {
                    contents: JSON.stringify((0, _writeContents.createAssetMap)({
                        assets
                    }))
                });
            }
            const targetDomain = devServer.isReactServerComponentsEnabled ? "client/" : "";
            const fileNames = Object.fromEntries(Object.entries(bundles).map(([platform, bundle])=>[
                    platform,
                    bundle.artifacts.filter((asset)=>asset.type === "js").map((asset)=>targetDomain + asset.filename), 
                ]));
            // Generate a `metadata.json` for EAS Update.
            const contents = (0, _createMetadataJson.createMetadataJson)({
                bundles,
                fileNames,
                embeddedHashSet,
                domComponentAssetsMetadata
            });
            files.set("metadata.json", {
                contents: JSON.stringify(contents)
            });
        }
        // Additional web-only steps...
        if (platforms.includes("web") && useServerRendering) {
            var ref2;
            const exportServer = ((ref2 = exp.web) == null ? void 0 : ref2.output) === "server";
            if (exportServer) {
                // TODO: Remove when this is abstracted into the files map
                await (0, _publicFolder.copyPublicFolderAsync)(publicPath, _path().default.resolve(outputPath, "client"));
            }
            if (skipSSG) {
                _log.log("Skipping static site generation");
                await (0, _exportStaticAsync.exportApiRoutesStandaloneAsync)(devServer, {
                    files,
                    platform: "web",
                    apiRoutesOnly: true
                });
                // Output a placeholder index.html if one doesn't exist in the public directory.
                // This ensures native + API routes have some content at the root URL.
                const placeholderIndex = _path().default.resolve(outputPath, "client/index.html");
                if (!_fs().default.existsSync(placeholderIndex)) {
                    files.set("index.html", {
                        contents: `<html><body></body></html>`,
                        targetDomain: "client"
                    });
                }
            } else if (// TODO: Support static export with RSC.
            !devServer.isReactServerComponentsEnabled) {
                var ref3;
                await (0, _exportStaticAsync.exportFromServerAsync)(projectRoot, devServer, {
                    mode,
                    files,
                    clear: !!clear,
                    outputDir: outputPath,
                    minify,
                    baseUrl,
                    includeSourceMaps: sourceMaps,
                    routerRoot: (0, _router.getRouterDirectoryModuleIdWithManifest)(projectRoot, exp),
                    reactCompiler: !!((ref3 = exp.experiments) == null ? void 0 : ref3.reactCompiler),
                    exportServer,
                    maxWorkers,
                    isExporting: true,
                    exp: projectConfig.exp
                });
            }
        }
    } finally{
        await devServerManager.stopAsync();
    }
    // Write all files at the end for unified logging.
    await (0, _saveAssets.persistMetroFilesAsync)(files, outputPath);
}

//# sourceMappingURL=exportApp.js.map