"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
var _webpack = require("next/dist/compiled/webpack/webpack");
var _constants = require("../../../shared/lib/constants");
var _utils = require("../loaders/utils");
var _path = require("path");
const PLUGIN_NAME = "FlightManifestPlugin";
class FlightManifestPlugin {
    dev = false;
    appDir = false;
    constructor(options){
        if (typeof options.dev === "boolean") {
            this.dev = options.dev;
        }
        this.appDir = options.appDir;
        this.pageExtensions = options.pageExtensions;
    }
    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.
                // @ts-ignore TODO: Remove ignore when webpack 5 is stable
                stage: _webpack.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH
            }, (assets)=>this.createAsset(assets, compilation, compiler.context));
        });
    }
    createAsset(assets, compilation, context) {
        const manifest = {};
        const appDir = this.appDir;
        const dev = this.dev;
        compilation.chunkGroups.forEach((chunkGroup)=>{
            function recordModule(chunk, id, mod) {
                var ref2;
                const isCSSModule = 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")));
                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__ || {};
                moduleIdMapping[id] = moduleIdMapping[id] || {};
                // 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, ((ref2 = mod.resourceResolveData) == null ? void 0 : ref2.path) || resource);
                if (!ssrNamedModuleId.startsWith(".")) ssrNamedModuleId = `./${ssrNamedModuleId}`;
                if (isCSSModule) {
                    if (!manifest[resource]) {
                        if (dev) {
                            const chunkIdNameMapping = (chunk.ids || []).map((chunkId)=>{
                                return chunkId + ":" + (chunk.name || chunk.id) + (dev ? "" : "-" + chunk.hash);
                            });
                            manifest[resource] = {
                                default: {
                                    id,
                                    name: "default",
                                    chunks: chunkIdNameMapping
                                }
                            };
                            moduleIdMapping[id]["default"] = {
                                id: ssrNamedModuleId,
                                name: "default",
                                chunks: chunkIdNameMapping
                            };
                            manifest.__ssr_module_mapping__ = moduleIdMapping;
                        } else {
                            const chunks = [
                                ...chunk.files
                            ].filter((f)=>f.endsWith(".css"));
                            manifest[resource] = {
                                default: {
                                    id,
                                    name: "default",
                                    chunks
                                }
                            };
                            moduleIdMapping[id]["default"] = {
                                id: ssrNamedModuleId,
                                name: "default",
                                chunks
                            };
                            manifest.__ssr_module_mapping__ = moduleIdMapping;
                        }
                    }
                    return;
                }
                // TODO: Hook into deps instead of the target module.
                // That way we know by the type of dep whether to include.
                // It also resolves conflicts when the same module is in multiple chunks.
                if (!_utils.clientComponentRegex.test(resource)) {
                    return;
                }
                const exportsInfo = compilation.moduleGraph.getExportsInfo(mod);
                const cjsExports = [
                    ...new Set([].concat(mod.dependencies.map((dep)=>{
                        // Match CommonJsSelfReferenceDependency
                        if (dep.type === "cjs self exports reference") {
                            // `module.exports = ...`
                            if (dep.base === "module.exports") {
                                return "default";
                            }
                            // `exports.foo = ...`, `exports.default = ...`
                            if (dep.base === "exports") {
                                return dep.names.filter((name)=>name !== "__esModule");
                            }
                        }
                        return null;
                    }))), 
                ];
                const moduleExportedKeys = [
                    "",
                    "*"
                ].concat([
                    ...exportsInfo.exports
                ].filter((exportInfo)=>exportInfo.provided).map((exportInfo)=>exportInfo.name), ...cjsExports).filter((name)=>name !== null);
                // Get all CSS files imported from the module's dependencies.
                const visitedModule = new Set();
                const cssChunks = new Set();
                function collectClientImportedCss(module) {
                    if (!module) return;
                    const modRequest = module.userRequest;
                    if (visitedModule.has(modRequest)) return;
                    visitedModule.add(modRequest);
                    if (/\.css$/.test(modRequest)) {
                        // collect relative imported css chunks
                        compilation.chunkGraph.getModuleChunks(module).forEach((c)=>{
                            [
                                ...c.files
                            ].filter((file)=>file.endsWith(".css")).forEach((file)=>cssChunks.add(file));
                        });
                    }
                    const connections = Array.from(compilation.moduleGraph.getOutgoingConnections(module));
                    connections.forEach((connection)=>{
                        collectClientImportedCss(compilation.moduleGraph.getResolvedModule(connection.dependency));
                    });
                }
                collectClientImportedCss(mod);
                moduleExportedKeys.forEach((name)=>{
                    let requiredChunks = [];
                    if (!moduleExports[name]) {
                        const isRelatedChunk = (c)=>{
                            var // If current chunk is a page, it should require the related page chunk;
                            // If current chunk is a component, it should filter out the related page chunk;
                            ref, ref1;
                            return ((ref = chunk.name) == null ? void 0 : ref.startsWith("pages/")) || !((ref1 = c.name) == null ? void 0 : ref1.startsWith("pages/"));
                        };
                        if (appDir) {
                            requiredChunks = chunkGroup.chunks.filter(isRelatedChunk).map((requiredChunk)=>{
                                return requiredChunk.id + ":" + (requiredChunk.name || requiredChunk.id) + (dev ? "" : "-" + requiredChunk.hash);
                            });
                        }
                        moduleExports[name] = {
                            id,
                            name,
                            chunks: requiredChunks.concat([
                                ...cssChunks
                            ])
                        };
                    }
                    if (!moduleIdMapping[id][name]) {
                        moduleIdMapping[id][name] = {
                            ...moduleExports[name],
                            id: ssrNamedModuleId
                        };
                    }
                });
                manifest[resource] = moduleExports;
                manifest.__ssr_module_mapping__ = moduleIdMapping;
            }
            chunkGroup.chunks.forEach((chunk)=>{
                const chunkModules = compilation.chunkGraph.getChunkModulesIterable(chunk);
                for (const mod of chunkModules){
                    const modId = compilation.chunkGraph.getModuleId(mod);
                    recordModule(chunk, modId, mod);
                    // If this is a concatenation, register each child to the parent ID.
                    const anyModule = mod;
                    if (anyModule.modules) {
                        anyModule.modules.forEach((concatenatedMod)=>{
                            recordModule(chunk, modId, concatenatedMod);
                        });
                    }
                }
            });
        });
        const file1 = "server/" + _constants.FLIGHT_MANIFEST;
        const json = JSON.stringify(manifest);
        assets[file1 + ".js"] = new _webpack.sources.RawSource("self.__RSC_MANIFEST=" + json);
        assets[file1 + ".json"] = new _webpack.sources.RawSource(json);
    }
}
exports.FlightManifestPlugin = FlightManifestPlugin;

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