"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
var _path = _interopRequireWildcard(require("path"));
var _webpack = require("next/dist/compiled/webpack/webpack");
var _constants = require("../../../shared/lib/constants");
var _utils = require("../loaders/utils");
var _buildContext = require("../../build-context");
var _utils1 = require("../utils");
var _nonNullable = require("../../../lib/non-nullable");
var _constants1 = require("../../../lib/constants");
function _getRequireWildcardCache() {
    if (typeof WeakMap !== "function") return null;
    var cache = new WeakMap();
    _getRequireWildcardCache = function() {
        return cache;
    };
    return cache;
}
function _interopRequireWildcard(obj) {
    if (obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache();
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {};
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (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;
}
const pluginState = (0, _buildContext).getProxiedPluginState({
    serverModuleIds: {},
    edgeServerModuleIds: {},
    ASYNC_CLIENT_MODULES: []
});
const PLUGIN_NAME = "FlightManifestPlugin";
class FlightManifestPlugin {
    dev = false;
    constructor(options){
        this.dev = options.dev;
        this.appDir = options.appDir;
        this.ASYNC_CLIENT_MODULES = new Set(pluginState.ASYNC_CLIENT_MODULES);
    }
    apply(compiler) {
        compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation, { normalModuleFactory  })=>{
            compilation.dependencyFactories.set(_webpack.webpack.dependencies.ModuleDependency, normalModuleFactory);
            compilation.dependencyTemplates.set(_webpack.webpack.dependencies.ModuleDependency, new _webpack.webpack.dependencies.NullDependency.Template());
        });
        compiler.hooks.make.tap(PLUGIN_NAME, (compilation)=>{
            compilation.hooks.processAssets.tap({
                name: PLUGIN_NAME,
                // Have to be in the optimize stage to run after updating the CSS
                // asset hash via extract mini css plugin.
                stage: _webpack.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH
            }, (assets)=>this.createAsset(assets, compilation, compiler.context));
        });
    }
    createAsset(assets, compilation, context) {
        const manifest = {
            __ssr_module_mapping__: {},
            __edge_ssr_module_mapping__: {},
            __entry_css_files__: {}
        };
        const dev = this.dev;
        const clientRequestsSet = new Set();
        // Collect client requests
        function collectClientRequest(mod) {
            if (mod.resource === "" && mod.buildInfo.rsc) {
                const { requests =[]  } = mod.buildInfo.rsc;
                requests.forEach((r)=>{
                    clientRequestsSet.add(r);
                });
            }
        }
        (0, _utils1).traverseModules(compilation, (mod)=>collectClientRequest(mod));
        compilation.chunkGroups.forEach((chunkGroup)=>{
            const recordModule = (id, mod, chunkCSS)=>{
                var ref;
                const isCSSModule = _utils.regexCSS.test(mod.resource) || mod.type === "css/mini-extract" || !!mod.loaders && (dev ? mod.loaders.some((item)=>item.loader.includes("next-style-loader/index.js")) : mod.loaders.some((item)=>item.loader.includes("mini-css-extract-plugin/loader.js")));
                // Skip all modules from the pages folder. CSS modules are a special case
                // as they are generated by mini-css-extract-plugin and these modules
                // don't have layer information attached.
                if (!isCSSModule && mod.layer !== _constants1.WEBPACK_LAYERS.appClient) {
                    return;
                }
                const resource = mod.type === "css/mini-extract" ? mod._identifier.slice(mod._identifier.lastIndexOf("!") + 1) : mod.resource;
                if (!resource) {
                    return;
                }
                const moduleExports = manifest[resource] || {};
                const moduleIdMapping = manifest.__ssr_module_mapping__;
                const edgeModuleIdMapping = manifest.__edge_ssr_module_mapping__;
                // Note that this isn't that reliable as webpack is still possible to assign
                // additional queries to make sure there's no conflict even using the `named`
                // module ID strategy.
                let ssrNamedModuleId = (0, _path).relative(context, ((ref = mod.resourceResolveData) == null ? void 0 : ref.path) || resource);
                if (!ssrNamedModuleId.startsWith(".")) ssrNamedModuleId = `./${ssrNamedModuleId.replace(/\\/g, "/")}`;
                if (isCSSModule) {
                    if (!manifest[resource]) {
                        manifest[resource] = {
                            default: {
                                id,
                                name: "default",
                                chunks: chunkCSS
                            }
                        };
                    } else {
                        // It is possible that there are multiple modules with the same resource,
                        // e.g. extracted by mini-css-extract-plugin. In that case we need to
                        // merge the chunks.
                        manifest[resource].default.chunks = [
                            ...new Set([
                                ...manifest[resource].default.chunks,
                                ...chunkCSS
                            ]), 
                        ];
                    }
                    return;
                }
                // Only apply following logic to client module requests from client entry,
                // or if the module is marked as client module.
                if (!clientRequestsSet.has(resource) && !(0, _utils).isClientComponentModule(mod)) {
                    return;
                }
                const exportsInfo = compilation.moduleGraph.getExportsInfo(mod);
                const isAsyncModule = this.ASYNC_CLIENT_MODULES.has(mod.resource);
                const cjsExports = [
                    ...new Set([
                        ...mod.dependencies.map((dep)=>{
                            // Match CommonJsSelfReferenceDependency
                            if (dep.type === "cjs self exports reference") {
                                // @ts-expect-error: TODO: Fix Dependency type
                                if (dep.base === "module.exports") {
                                    return "default";
                                }
                                // `exports.foo = ...`, `exports.default = ...`
                                // @ts-expect-error: TODO: Fix Dependency type
                                if (dep.base === "exports") {
                                    // @ts-expect-error: TODO: Fix Dependency type
                                    return dep.names.filter((name)=>name !== "__esModule");
                                }
                            }
                            return null;
                        }), 
                    ]), 
                ];
                function getAppPathRequiredChunks() {
                    return chunkGroup.chunks.map((requiredChunk)=>{
                        if (_constants.SYSTEM_ENTRYPOINTS.has(requiredChunk.name)) {
                            return null;
                        }
                        return requiredChunk.id + ":" + (requiredChunk.name || requiredChunk.id) + (dev ? "" : "-" + requiredChunk.hash);
                    }).filter(_nonNullable.nonNullable);
                }
                const moduleExportedKeys = [
                    "",
                    "*"
                ].concat([
                    ...exportsInfo.exports
                ].filter((exportInfo)=>exportInfo.provided).map((exportInfo)=>exportInfo.name), ...cjsExports).filter((name)=>name !== null);
                moduleExportedKeys.forEach((name)=>{
                    // If the chunk is from `app/` chunkGroup, use it first.
                    // This make sure not to load the overlapped chunk from `pages/` chunkGroup
                    if (!moduleExports[name] || chunkGroup.name && /^app[\\/]/.test(chunkGroup.name)) {
                        const requiredChunks = getAppPathRequiredChunks();
                        moduleExports[name] = {
                            id,
                            name,
                            chunks: requiredChunks,
                            // E.g.
                            // page (server) -> local module (client) -> package (esm)
                            // The esm package will bubble up to make the entire chain till the client entry as async module.
                            async: isAsyncModule
                        };
                    }
                    if (typeof pluginState.serverModuleIds[ssrNamedModuleId] !== "undefined") {
                        moduleIdMapping[id] = moduleIdMapping[id] || {};
                        moduleIdMapping[id][name] = {
                            ...moduleExports[name],
                            id: pluginState.serverModuleIds[ssrNamedModuleId]
                        };
                    }
                    if (typeof pluginState.edgeServerModuleIds[ssrNamedModuleId] !== "undefined") {
                        edgeModuleIdMapping[id] = edgeModuleIdMapping[id] || {};
                        edgeModuleIdMapping[id][name] = {
                            ...moduleExports[name],
                            id: pluginState.edgeServerModuleIds[ssrNamedModuleId]
                        };
                    }
                });
                manifest[resource] = moduleExports;
                // The client compiler will always use the CJS Next.js build, so here we
                // also add the mapping for the ESM build (Edge runtime) to consume.
                if (/[\\/]next[\\/]dist[\\/]/.test(resource)) {
                    manifest[resource.replace(/[\\/]next[\\/]dist[\\/]/, "/next/dist/esm/".replace(/\//g, _path.default.sep))] = moduleExports;
                }
                manifest.__ssr_module_mapping__ = moduleIdMapping;
                manifest.__edge_ssr_module_mapping__ = edgeModuleIdMapping;
            };
            chunkGroup.chunks.forEach((chunk)=>{
                const chunkModules = compilation.chunkGraph.getChunkModulesIterable(chunk);
                const chunkCSS = [
                    ...chunk.files
                ].filter((f)=>!f.startsWith("static/css/pages/") && f.endsWith(".css"));
                for (const mod of chunkModules){
                    const modId = compilation.chunkGraph.getModuleId(mod) + "";
                    recordModule(modId, mod, chunkCSS);
                    // If this is a concatenation, register each child to the parent ID.
                    // TODO: remove any
                    const anyModule = mod;
                    if (anyModule.modules) {
                        anyModule.modules.forEach((concatenatedMod)=>{
                            recordModule(modId, concatenatedMod, chunkCSS);
                        });
                    }
                }
            });
            const entryCSSFiles = manifest.__entry_css_files__ || {};
            const addCSSFilesToEntry = (files, entryName)=>{
                if (entryName == null ? void 0 : entryName.startsWith("app/")) {
                    // The `key` here should be the absolute file path but without extension.
                    // We need to replace the separator in the entry name to match the system separator.
                    const key = this.appDir + entryName.slice(3).replace(/\//g, _path.sep).replace(/\.[^\\/.]+$/, "");
                    entryCSSFiles[key] = files.concat(entryCSSFiles[key] || []);
                }
            };
            const cssFiles = chunkGroup.getFiles().filter((f)=>f.endsWith(".css"));
            if (cssFiles.length) {
                // Add to chunk entry and parent chunk groups too.
                addCSSFilesToEntry(cssFiles, chunkGroup.name);
                chunkGroup.getParents().forEach((parent)=>{
                    addCSSFilesToEntry(cssFiles, parent.options.name);
                });
            }
            manifest.__entry_css_files__ = entryCSSFiles;
        });
        const file = "server/" + _constants.CLIENT_REFERENCE_MANIFEST;
        const json = JSON.stringify(manifest, null, this.dev ? 2 : undefined);
        pluginState.ASYNC_CLIENT_MODULES = [];
        assets[file + ".js"] = new _webpack.sources.RawSource("self.__RSC_MANIFEST=" + json);
        assets[file + ".json"] = new _webpack.sources.RawSource(json);
    }
}
exports.FlightManifestPlugin = FlightManifestPlugin;

//# sourceMappingURL=flight-manifest-plugin.js.map