"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: all[name]
    });
}
_export(exports, {
    instantiateMetroAsync: function() {
        return instantiateMetroAsync;
    },
    isWatchEnabled: function() {
        return isWatchEnabled;
    },
    loadMetroConfigAsync: function() {
        return loadMetroConfigAsync;
    }
});
function _config() {
    const data = require("@expo/config");
    _config = function() {
        return data;
    };
    return data;
}
function _paths() {
    const data = require("@expo/config/paths");
    _paths = function() {
        return data;
    };
    return data;
}
function _metroconfig() {
    const data = require("@expo/metro-config");
    _metroconfig = function() {
        return data;
    };
    return data;
}
function _chalk() {
    const data = /*#__PURE__*/ _interop_require_default(require("chalk"));
    _chalk = function() {
        return data;
    };
    return data;
}
function _RevisionNotFoundError() {
    const data = /*#__PURE__*/ _interop_require_default(require("metro/src/IncrementalBundler/RevisionNotFoundError"));
    _RevisionNotFoundError = function() {
        return data;
    };
    return data;
}
function _formatBundlingError() {
    const data = /*#__PURE__*/ _interop_require_default(require("metro/src/lib/formatBundlingError"));
    _formatBundlingError = function() {
        return data;
    };
    return data;
}
function _metroconfig1() {
    const data = require("metro-config");
    _metroconfig1 = function() {
        return data;
    };
    return data;
}
function _metrocore() {
    const data = require("metro-core");
    _metrocore = function() {
        return data;
    };
    return data;
}
function _nodeutil() {
    const data = /*#__PURE__*/ _interop_require_default(require("node:util"));
    _nodeutil = function() {
        return data;
    };
    return data;
}
function _path() {
    const data = /*#__PURE__*/ _interop_require_default(require("path"));
    _path = function() {
        return data;
    };
    return data;
}
const _DevToolsPluginWebsocketEndpoint = require("./DevToolsPluginWebsocketEndpoint");
const _MetroTerminalReporter = require("./MetroTerminalReporter");
const _attachAtlas = require("./debugging/attachAtlas");
const _createDebugMiddleware = require("./debugging/createDebugMiddleware");
const _createMetroMiddleware = require("./dev-server/createMetroMiddleware");
const _runServerfork = require("./runServer-fork");
const _withMetroMultiPlatform = require("./withMetroMultiPlatform");
const _log = require("../../../log");
const _env = require("../../../utils/env");
const _errors = require("../../../utils/errors");
const _CorsMiddleware = require("../middleware/CorsMiddleware");
const _createJsInspectorMiddleware = require("../middleware/inspector/createJsInspectorMiddleware");
const _mutations = require("../middleware/mutations");
const _platformBundlers = require("../platformBundlers");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
// Wrap terminal and polyfill console.log so we can log during bundling without breaking the indicator.
class LogRespectingTerminal extends _metrocore().Terminal {
    constructor(stream){
        super(stream);
        const sendLog = (...args)=>{
            this._logLines.push(// format args like console.log
            _nodeutil().default.format(...args));
            this._scheduleUpdate();
            // Flush the logs to the terminal immediately so logs at the end of the process are not lost.
            this.flush();
        };
        console.log = sendLog;
        console.info = sendLog;
    }
}
// Share one instance of Terminal for all instances of Metro.
const terminal = new LogRespectingTerminal(process.stdout);
async function loadMetroConfigAsync(projectRoot, options, { exp, isExporting, getMetroBundler }) {
    var _exp_experiments, _exp_experiments1, _exp_experiments2, _exp_experiments3, _config_resolver, _exp_experiments4, _exp_experiments5, _exp_experiments6;
    let reportEvent;
    const serverActionsEnabled = ((_exp_experiments = exp.experiments) == null ? void 0 : _exp_experiments.reactServerFunctions) ?? _env.env.EXPO_UNSTABLE_SERVER_FUNCTIONS;
    if (serverActionsEnabled) {
        process.env.EXPO_UNSTABLE_SERVER_FUNCTIONS = '1';
    }
    // NOTE: Enable all the experimental Metro flags when RSC is enabled.
    if (((_exp_experiments1 = exp.experiments) == null ? void 0 : _exp_experiments1.reactServerComponentRoutes) || serverActionsEnabled) {
        process.env.EXPO_USE_METRO_REQUIRE = '1';
        process.env.EXPO_USE_FAST_RESOLVER = '1';
    }
    const isReactCanaryEnabled = (((_exp_experiments2 = exp.experiments) == null ? void 0 : _exp_experiments2.reactServerComponentRoutes) || serverActionsEnabled || ((_exp_experiments3 = exp.experiments) == null ? void 0 : _exp_experiments3.reactCanary)) ?? false;
    if (isReactCanaryEnabled) {
        // The fast resolver is required for React canary to work as it can switch the node_modules location for react imports.
        process.env.EXPO_USE_FAST_RESOLVER = '1';
    }
    const serverRoot = (0, _paths().getMetroServerRoot)(projectRoot);
    const terminalReporter = new _MetroTerminalReporter.MetroTerminalReporter(serverRoot, terminal);
    const hasConfig = await (0, _metroconfig1().resolveConfig)(options.config, projectRoot);
    let config = {
        ...await (0, _metroconfig1().loadConfig)({
            cwd: projectRoot,
            projectRoot,
            ...options
        }, // If the project does not have a metro.config.js, then we use the default config.
        hasConfig.isEmpty ? (0, _metroconfig().getDefaultConfig)(projectRoot) : undefined),
        reporter: {
            update (event) {
                terminalReporter.update(event);
                if (reportEvent) {
                    reportEvent(event);
                }
            }
        }
    };
    // @ts-expect-error: Set the global require cycle ignore patterns for SSR bundles. This won't work with custom global prefixes, but we don't use those.
    globalThis.__requireCycleIgnorePatterns = (_config_resolver = config.resolver) == null ? void 0 : _config_resolver.requireCycleIgnorePatterns;
    if (isExporting) {
        var _exp_experiments7;
        // This token will be used in the asset plugin to ensure the path is correct for writing locally.
        // @ts-expect-error: typed as readonly.
        config.transformer.publicPath = `/assets?export_path=${(((_exp_experiments7 = exp.experiments) == null ? void 0 : _exp_experiments7.baseUrl) ?? '') + '/assets'}`;
    } else {
        // @ts-expect-error: typed as readonly
        config.transformer.publicPath = '/assets/?unstable_path=.';
    }
    const platformBundlers = (0, _platformBundlers.getPlatformBundlers)(projectRoot, exp);
    if ((_exp_experiments4 = exp.experiments) == null ? void 0 : _exp_experiments4.reactCompiler) {
        _log.Log.warn(`Experimental React Compiler is enabled.`);
    }
    if (_env.env.EXPO_UNSTABLE_TREE_SHAKING && !_env.env.EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH) {
        throw new _errors.CommandError('EXPO_UNSTABLE_TREE_SHAKING requires EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH to be enabled.');
    }
    if (_env.env.EXPO_UNSTABLE_METRO_OPTIMIZE_GRAPH) {
        _log.Log.warn(`Experimental bundle optimization is enabled.`);
    }
    if (_env.env.EXPO_UNSTABLE_TREE_SHAKING) {
        _log.Log.warn(`Experimental tree shaking is enabled.`);
    }
    if (serverActionsEnabled) {
        var _exp_experiments8;
        _log.Log.warn(`React Server Functions (beta) are enabled. Route rendering mode: ${((_exp_experiments8 = exp.experiments) == null ? void 0 : _exp_experiments8.reactServerComponentRoutes) ? 'server' : 'client'}`);
    }
    config = await (0, _withMetroMultiPlatform.withMetroMultiPlatformAsync)(projectRoot, {
        config,
        exp,
        platformBundlers,
        isTsconfigPathsEnabled: ((_exp_experiments5 = exp.experiments) == null ? void 0 : _exp_experiments5.tsconfigPaths) ?? true,
        isFastResolverEnabled: _env.env.EXPO_USE_FAST_RESOLVER,
        isExporting,
        isReactCanaryEnabled,
        isNamedRequiresEnabled: _env.env.EXPO_USE_METRO_REQUIRE,
        isReactServerComponentsEnabled: !!((_exp_experiments6 = exp.experiments) == null ? void 0 : _exp_experiments6.reactServerComponentRoutes),
        getMetroBundler
    });
    return {
        config,
        setEventReporter: (logger)=>reportEvent = logger,
        reporter: terminalReporter
    };
}
async function instantiateMetroAsync(metroBundler, options, { isExporting, exp = (0, _config().getConfig)(metroBundler.projectRoot, {
    skipSDKVersionRequirement: true
}).exp }) {
    const projectRoot = metroBundler.projectRoot;
    const { config: metroConfig, setEventReporter, reporter } = await loadMetroConfigAsync(projectRoot, options, {
        exp,
        isExporting,
        getMetroBundler () {
            return metro.getBundler().getBundler();
        }
    });
    // Create the core middleware stack for Metro, including websocket listeners
    const { middleware, messagesSocket, eventsSocket, websocketEndpoints } = (0, _createMetroMiddleware.createMetroMiddleware)(metroConfig);
    if (!isExporting) {
        // Enable correct CORS headers for Expo Router features
        (0, _mutations.prependMiddleware)(middleware, (0, _CorsMiddleware.createCorsMiddleware)(exp));
        // Enable debug middleware for CDP-related debugging
        const { debugMiddleware, debugWebsocketEndpoints } = (0, _createDebugMiddleware.createDebugMiddleware)(metroBundler, reporter);
        Object.assign(websocketEndpoints, debugWebsocketEndpoints);
        middleware.use(debugMiddleware);
        middleware.use('/_expo/debugger', (0, _createJsInspectorMiddleware.createJsInspectorMiddleware)());
        // TODO(cedric): `enhanceMiddleware` is deprecated, but is currently used to unify the middleware stacks
        // See: https://github.com/facebook/metro/commit/22e85fde85ec454792a1b70eba4253747a2587a9
        // See: https://github.com/facebook/metro/commit/d0d554381f119bb80ab09dbd6a1d310b54737e52
        const customEnhanceMiddleware = metroConfig.server.enhanceMiddleware;
        // @ts-expect-error: can't mutate readonly config
        metroConfig.server.enhanceMiddleware = (metroMiddleware, server)=>{
            if (customEnhanceMiddleware) {
                metroMiddleware = customEnhanceMiddleware(metroMiddleware, server);
            }
            return middleware.use(metroMiddleware);
        };
    }
    // Attach Expo Atlas if enabled
    await (0, _attachAtlas.attachAtlasAsync)({
        isExporting,
        exp,
        projectRoot,
        middleware,
        metroConfig,
        // NOTE(cedric): reset the Atlas file once, and reuse it for static exports
        resetAtlasFile: isExporting
    });
    const { server, hmrServer, metro } = await (0, _runServerfork.runServer)(metroBundler, metroConfig, {
        websocketEndpoints: {
            ...websocketEndpoints,
            ...(0, _DevToolsPluginWebsocketEndpoint.createDevToolsPluginWebsocketEndpoint)()
        },
        watch: !isExporting && isWatchEnabled()
    }, {
        mockServer: isExporting
    });
    // Patch transform file to remove inconvenient customTransformOptions which are only used in single well-known files.
    const originalTransformFile = metro.getBundler().getBundler().transformFile.bind(metro.getBundler().getBundler());
    metro.getBundler().getBundler().transformFile = async function(filePath, transformOptions, fileBuffer) {
        return originalTransformFile(filePath, pruneCustomTransformOptions(filePath, // Clone the options so we don't mutate the original.
        {
            ...transformOptions,
            customTransformOptions: {
                __proto__: null,
                ...transformOptions.customTransformOptions
            }
        }), fileBuffer);
    };
    setEventReporter(eventsSocket.reportMetroEvent);
    // This function ensures that modules in source maps are sorted in the same
    // order as in a plain JS bundle.
    metro._getSortedModules = function(graph) {
        var _graph_transformOptions_customTransformOptions;
        const modules = [
            ...graph.dependencies.values()
        ];
        const ctx = {
            platform: graph.transformOptions.platform,
            environment: (_graph_transformOptions_customTransformOptions = graph.transformOptions.customTransformOptions) == null ? void 0 : _graph_transformOptions_customTransformOptions.environment
        };
        // Assign IDs to modules in a consistent order
        for (const module of modules){
            // @ts-expect-error
            this._createModuleId(module.path, ctx);
        }
        // Sort by IDs
        return modules.sort(// @ts-expect-error
        (a, b)=>this._createModuleId(a.path, ctx) - this._createModuleId(b.path, ctx));
    };
    if (hmrServer) {
        let hmrJSBundle;
        try {
            hmrJSBundle = require('@expo/metro-config/build/serializer/fork/hmrJSBundle').default;
        } catch  {
            // Add fallback for monorepo tests up until the fork is merged.
            _log.Log.warn('Failed to load HMR serializer from @expo/metro-config, using fallback version.');
            hmrJSBundle = require('metro/src/DeltaBundler/Serializers/hmrJSBundle');
        }
        // Patch HMR Server to send more info to the `_createModuleId` function for deterministic module IDs and add support for serializing HMR updates the same as all other bundles.
        hmrServer._prepareMessage = async function(group, options, changeEvent) {
            // Fork of https://github.com/facebook/metro/blob/3b3e0aaf725cfa6907bf2c8b5fbc0da352d29efe/packages/metro/src/HmrServer.js#L327-L393
            // with patch for `_createModuleId`.
            const logger = !options.isInitialUpdate ? changeEvent == null ? void 0 : changeEvent.logger : null;
            try {
                var _revision_graph_transformOptions_customTransformOptions;
                const revPromise = this._bundler.getRevision(group.revisionId);
                if (!revPromise) {
                    return {
                        type: 'error',
                        body: (0, _formatBundlingError().default)(new (_RevisionNotFoundError()).default(group.revisionId))
                    };
                }
                logger == null ? void 0 : logger.point('updateGraph_start');
                const { revision, delta } = await this._bundler.updateGraph(await revPromise, false);
                logger == null ? void 0 : logger.point('updateGraph_end');
                this._clientGroups.delete(group.revisionId);
                group.revisionId = revision.id;
                for (const client of group.clients){
                    client.revisionIds = client.revisionIds.filter((revisionId)=>revisionId !== group.revisionId);
                    client.revisionIds.push(revision.id);
                }
                this._clientGroups.set(group.revisionId, group);
                logger == null ? void 0 : logger.point('serialize_start');
                // NOTE(EvanBacon): This is the patch
                const moduleIdContext = {
                    platform: revision.graph.transformOptions.platform,
                    environment: (_revision_graph_transformOptions_customTransformOptions = revision.graph.transformOptions.customTransformOptions) == null ? void 0 : _revision_graph_transformOptions_customTransformOptions.environment
                };
                const hmrUpdate = hmrJSBundle(delta, revision.graph, {
                    clientUrl: group.clientUrl,
                    // NOTE(EvanBacon): This is also the patch
                    createModuleId: (moduleId)=>{
                        // @ts-expect-error
                        return this._createModuleId(moduleId, moduleIdContext);
                    },
                    includeAsyncPaths: group.graphOptions.lazy,
                    projectRoot: this._config.projectRoot,
                    serverRoot: this._config.server.unstable_serverRoot ?? this._config.projectRoot
                });
                logger == null ? void 0 : logger.point('serialize_end');
                return {
                    type: 'update',
                    body: {
                        revisionId: revision.id,
                        isInitialUpdate: options.isInitialUpdate,
                        ...hmrUpdate
                    }
                };
            } catch (error) {
                const formattedError = (0, _formatBundlingError().default)(error);
                this._config.reporter.update({
                    type: 'bundling_error',
                    error
                });
                return {
                    type: 'error',
                    body: formattedError
                };
            }
        };
    }
    return {
        metro,
        hmrServer,
        server,
        middleware,
        messageSocket: messagesSocket
    };
}
// TODO: Fork the entire transform function so we can simply regex the file contents for keywords instead.
function pruneCustomTransformOptions(filePath, transformOptions) {
    var _transformOptions_customTransformOptions, _transformOptions_customTransformOptions1, _transformOptions_customTransformOptions2, _transformOptions_customTransformOptions3;
    // Normalize the filepath for cross platform checking.
    filePath = filePath.split(_path().default.sep).join('/');
    if (((_transformOptions_customTransformOptions = transformOptions.customTransformOptions) == null ? void 0 : _transformOptions_customTransformOptions.dom) && // The only generated file that needs the dom root is `expo/dom/entry.js`
    !filePath.match(/expo\/dom\/entry\.js$/)) {
        // Clear the dom root option if we aren't transforming the magic entry file, this ensures
        // that cached artifacts from other DOM component bundles can be reused.
        transformOptions.customTransformOptions.dom = 'true';
    }
    if (((_transformOptions_customTransformOptions1 = transformOptions.customTransformOptions) == null ? void 0 : _transformOptions_customTransformOptions1.routerRoot) && // The router root is used all over expo-router (`process.env.EXPO_ROUTER_ABS_APP_ROOT`, `process.env.EXPO_ROUTER_APP_ROOT`) so we'll just ignore the entire package.
    !(filePath.match(/\/expo-router\/_ctx/) || filePath.match(/\/expo-router\/build\//))) {
        // Set to the default value.
        transformOptions.customTransformOptions.routerRoot = 'app';
    }
    if (((_transformOptions_customTransformOptions2 = transformOptions.customTransformOptions) == null ? void 0 : _transformOptions_customTransformOptions2.asyncRoutes) && // The async routes settings are also used in `expo-router/_ctx.ios.js` (and other platform variants) via `process.env.EXPO_ROUTER_IMPORT_MODE`
    !(filePath.match(/\/expo-router\/_ctx/) || filePath.match(/\/expo-router\/build\//))) {
        delete transformOptions.customTransformOptions.asyncRoutes;
    }
    if (((_transformOptions_customTransformOptions3 = transformOptions.customTransformOptions) == null ? void 0 : _transformOptions_customTransformOptions3.clientBoundaries) && // The client boundaries are only used in `expo/virtual/rsc.js` for production RSC exports.
    !filePath.match(/\/expo\/virtual\/rsc\.js$/)) {
        delete transformOptions.customTransformOptions.clientBoundaries;
    }
    return transformOptions;
}
function isWatchEnabled() {
    if (_env.env.CI) {
        _log.Log.log((0, _chalk().default)`Metro is running in CI mode, reloads are disabled. Remove {bold CI=true} to enable watch mode.`);
    }
    return !_env.env.CI;
}

//# sourceMappingURL=instantiateMetro.js.map