import { webpack } from 'next/dist/compiled/webpack/webpack';
import { parse, stringify } from 'querystring';
import path from 'path';
import { sources } from 'next/dist/compiled/webpack/webpack';
import { getInvalidator, getEntries, EntryTypes, getEntryKey } from '../../../server/dev/on-demand-entry-handler';
import { WEBPACK_LAYERS, WEBPACK_RESOURCE_QUERIES } from '../../../lib/constants';
import { APP_CLIENT_INTERNALS, BARREL_OPTIMIZATION_PREFIX, COMPILER_NAMES, DEFAULT_RUNTIME_WEBPACK, EDGE_RUNTIME_WEBPACK, SERVER_REFERENCE_MANIFEST } from '../../../shared/lib/constants';
import { UNDERSCORE_NOT_FOUND_ROUTE_ENTRY, UNDERSCORE_GLOBAL_ERROR_ROUTE_ENTRY } from '../../../shared/lib/entry-constants';
import { isClientComponentEntryModule, isCSSMod, regexCSS } from '../loaders/utils';
import { traverseModules, forEachEntryModule, formatBarrelOptimizedResource, getModuleReferencesInOrder } from '../utils';
import { normalizePathSep } from '../../../shared/lib/page-path/normalize-path-sep';
import { getProxiedPluginState } from '../../build-context';
import { PAGE_TYPES } from '../../../lib/page-types';
import { getModuleBuildInfo } from '../loaders/get-module-build-info';
import { getAssumedSourceType } from '../loaders/next-flight-loader';
import { isAppRouteRoute } from '../../../lib/is-app-route-route';
import { DEFAULT_METADATA_ROUTE_EXTENSIONS, isMetadataRouteFile } from '../../../lib/metadata/is-metadata-route';
import getWebpackBundler from '../../../shared/lib/get-webpack-bundler';
import { isAppBuiltinPage } from '../../utils';
const PLUGIN_NAME = 'FlightClientEntryPlugin';
const pluginState = getProxiedPluginState({
    // A map to track "action" -> "list of bundles".
    serverActions: {},
    edgeServerActions: {},
    serverActionModules: {},
    edgeServerActionModules: {},
    ssrModules: {},
    edgeSsrModules: {},
    rscModules: {},
    edgeRscModules: {},
    injectedClientEntries: {}
});
const POSSIBLE_SHARED_CONVENTIONS = [
    'template',
    'layout'
];
const STANDALONE_BUNDLE_CONVENTION = 'global-not-found';
function deduplicateCSSImportsForEntry(mergedCSSimports) {
    // If multiple entry module connections are having the same CSS import,
    // we only need to have one module to keep track of that CSS import.
    // It is based on the fact that if a page or a layout is rendered in the
    // given entry, all its parent layouts are always rendered too.
    // This can avoid duplicate CSS imports in the generated CSS manifest,
    // for example, if a page and its parent layout are both using the same
    // CSS import, we only need to have the layout to keep track of that CSS
    // import.
    // To achieve this, we need to first collect all the CSS imports from
    // every connection, and deduplicate them in the order of layers from
    // top to bottom. The implementation can be generally described as:
    // - Sort by number of `/` in the request path (the more `/`, the deeper)
    // - When in the same depth, sort by the filename (template < layout < page and others)
    // Sort the connections as described above.
    const sortedCSSImports = Object.entries(mergedCSSimports).sort((a, b)=>{
        const [aPath] = a;
        const [bPath] = b;
        const aDepth = aPath.split('/').length;
        const bDepth = bPath.split('/').length;
        if (aDepth !== bDepth) {
            return aDepth - bDepth;
        }
        const aName = path.parse(aPath).name;
        const bName = path.parse(bPath).name;
        const indexA = POSSIBLE_SHARED_CONVENTIONS.indexOf(aName);
        const indexB = POSSIBLE_SHARED_CONVENTIONS.indexOf(bName);
        if (indexA === -1) return 1;
        if (indexB === -1) return -1;
        return indexA - indexB;
    });
    const dedupedCSSImports = {};
    const trackedCSSImports = new Set();
    for (const [entryFilePath, cssImports] of sortedCSSImports){
        const entryConventionName = path.parse(entryFilePath).name;
        for (const cssImport of cssImports){
            // If the CSS import is already tracked, we can skip it.
            // Or if it's any standalone entry such as `global-not-found`, it won't share any resources with other entry, skip it.
            if (trackedCSSImports.has(cssImport) && STANDALONE_BUNDLE_CONVENTION !== entryConventionName) {
                continue;
            }
            // Only track CSS imports that are in files that can inherit CSS.
            if (POSSIBLE_SHARED_CONVENTIONS.includes(entryConventionName)) {
                trackedCSSImports.add(cssImport);
            }
            if (!dedupedCSSImports[entryFilePath]) {
                dedupedCSSImports[entryFilePath] = [];
            }
            dedupedCSSImports[entryFilePath].push(cssImport);
        }
    }
    return dedupedCSSImports;
}
export class FlightClientEntryPlugin {
    constructor(options){
        this.dev = options.dev;
        this.appDir = options.appDir;
        this.projectDir = path.join(options.appDir, '..');
        this.isEdgeServer = options.isEdgeServer;
        this.assetPrefix = !this.dev && !this.isEdgeServer ? '../' : '';
        this.encryptionKey = options.encryptionKey;
        this.webpackRuntime = this.isEdgeServer ? EDGE_RUNTIME_WEBPACK : DEFAULT_RUNTIME_WEBPACK;
    }
    apply(compiler) {
        compiler.hooks.finishMake.tapPromise(PLUGIN_NAME, (compilation)=>this.createClientEntries(compiler, compilation));
        compiler.hooks.afterCompile.tap(PLUGIN_NAME, (compilation)=>{
            const recordModule = (modId, mod)=>{
                var _mod_resourceResolveData, _mod_resourceResolveData1;
                // Match Resource is undefined unless an import is using the inline match resource syntax
                // https://webpack.js.org/api/loaders/#inline-matchresource
                const modPath = mod.matchResource || ((_mod_resourceResolveData = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData.path);
                const modQuery = ((_mod_resourceResolveData1 = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData1.query) || '';
                // query is already part of mod.resource
                // so it's only necessary to add it for matchResource or mod.resourceResolveData
                const modResource = modPath ? modPath.startsWith(BARREL_OPTIMIZATION_PREFIX) ? formatBarrelOptimizedResource(mod.resource, modPath) : modPath + modQuery : mod.resource;
                if (typeof modId !== 'undefined' && modResource) {
                    if (mod.layer === WEBPACK_LAYERS.reactServerComponents) {
                        const key = path.relative(compiler.context, modResource).replace(/\/next\/dist\/esm\//, '/next/dist/');
                        const moduleInfo = {
                            moduleId: modId,
                            async: compilation.moduleGraph.isAsync(mod)
                        };
                        if (this.isEdgeServer) {
                            pluginState.edgeRscModules[key] = moduleInfo;
                        } else {
                            pluginState.rscModules[key] = moduleInfo;
                        }
                    }
                }
                if (mod.layer !== WEBPACK_LAYERS.serverSideRendering) {
                    return;
                }
                // Check mod resource to exclude the empty resource module like virtual module created by next-flight-client-entry-loader
                if (typeof modId !== 'undefined' && modResource) {
                    // 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 = path.relative(compiler.context, modResource);
                    if (!ssrNamedModuleId.startsWith('.')) {
                        // TODO use getModuleId instead
                        ssrNamedModuleId = `./${normalizePathSep(ssrNamedModuleId)}`;
                    }
                    const moduleInfo = {
                        moduleId: modId,
                        async: compilation.moduleGraph.isAsync(mod)
                    };
                    if (this.isEdgeServer) {
                        pluginState.edgeSsrModules[ssrNamedModuleId.replace(/\/next\/dist\/esm\//, '/next/dist/')] = moduleInfo;
                    } else {
                        pluginState.ssrModules[ssrNamedModuleId] = moduleInfo;
                    }
                }
            };
            traverseModules(compilation, (mod, _chunk, _chunkGroup, modId)=>{
                if (modId) recordModule(modId, mod);
            });
        });
        compiler.hooks.make.tap(PLUGIN_NAME, (compilation)=>{
            compilation.hooks.processAssets.tapPromise({
                name: PLUGIN_NAME,
                stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH
            }, ()=>this.createActionAssets(compilation));
        });
    }
    async createClientEntries(compiler, compilation) {
        const addClientEntryAndSSRModulesList = [];
        const createdSSRDependenciesForEntry = {};
        const addActionEntryList = [];
        const actionMapsPerEntry = {};
        const createdActionIds = new Set();
        // For each SC server compilation entry, we need to create its corresponding
        // client component entry.
        forEachEntryModule(compilation, ({ name, entryModule })=>{
            const internalClientComponentEntryImports = {};
            const actionEntryImports = new Map();
            const clientEntriesToInject = [];
            const mergedCSSimports = {};
            const moduleReferences = getModuleReferencesInOrder(entryModule, compilation.moduleGraph);
            for (const connection of moduleReferences){
                // Entry can be any user defined entry files such as layout, page, error, loading, etc.
                let entryRequest = connection.dependency.request;
                if (entryRequest.endsWith(WEBPACK_RESOURCE_QUERIES.metadataRoute)) {
                    const { filePath, isDynamicRouteExtension } = getMetadataRouteResource(entryRequest);
                    if (isDynamicRouteExtension === '1') {
                        entryRequest = filePath;
                    }
                }
                const { clientComponentImports, actionImports, cssImports } = this.collectComponentInfoFromServerEntryDependency({
                    entryRequest,
                    compilation,
                    resolvedModule: connection.resolvedModule
                });
                actionImports.forEach(([dep, actions])=>actionEntryImports.set(dep, actions));
                const isAbsoluteRequest = path.isAbsolute(entryRequest);
                const isAppRouterBuiltinPage = isAppBuiltinPage(entryRequest);
                // Next.js internals are put into a separate entry.
                if (!isAbsoluteRequest) {
                    Object.keys(clientComponentImports).forEach((value)=>internalClientComponentEntryImports[value] = new Set());
                    if (!isAppRouterBuiltinPage) {
                        continue;
                    }
                }
                // TODO-APP: Enable these lines. This ensures no entrypoint is created for layout/page when there are no client components.
                // Currently disabled because it causes test failures in CI.
                // if (clientImports.length === 0 && actionImports.length === 0) {
                //   continue
                // }
                const relativeRequest = isAbsoluteRequest && !isAppRouterBuiltinPage ? path.relative(compilation.options.context, entryRequest) : entryRequest;
                // Replace file suffix as `.js` will be added.
                // bundlePath will have app/ prefix but not src/.
                // e.g. src/app/foo/page.js -> app/foo/page
                let bundlePath = normalizePathSep(relativeRequest.replace(/\.[^.\\/]+$/, '').replace(/^src[\\/]/, ''));
                // For metadata routes, the entry name can be used as the bundle path,
                // as it has been normalized already.
                // e.g.
                // When `relativeRequest` is 'src/app/sitemap.js',
                // `appDirRelativeRequest` will be '/sitemap.js'
                // then `isMetadataEntryFile` will be `true`
                const appDirRelativeRequest = relativeRequest.replace(/^src[\\/]/, '').replace(/^app[\\/]/, '/');
                const isMetadataEntryFile = isMetadataRouteFile(appDirRelativeRequest, DEFAULT_METADATA_ROUTE_EXTENSIONS, true);
                if (isMetadataEntryFile) {
                    bundlePath = name;
                }
                Object.assign(mergedCSSimports, cssImports);
                clientEntriesToInject.push({
                    compiler,
                    compilation,
                    entryName: name,
                    clientComponentImports,
                    bundlePath,
                    absolutePagePath: entryRequest
                });
                // The webpack implementation of writing the client reference manifest relies on all entrypoints writing a page.js even when there is no client components in the page.
                // It needs the file in order to write the reference manifest for the path in the `.next/server` folder.
                // TODO-APP: This could be better handled, however Turbopack does not have the same problem as we resolve client components in a single graph.
                if (name === `app${UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}` && bundlePath === 'app/not-found') {
                    clientEntriesToInject.push({
                        compiler,
                        compilation,
                        entryName: name,
                        clientComponentImports: {},
                        bundlePath: `app${UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}`,
                        absolutePagePath: entryRequest
                    });
                }
                if (name === `app${UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}` && bundlePath === 'app/global-not-found') {
                    clientEntriesToInject.push({
                        compiler,
                        compilation,
                        entryName: name,
                        clientComponentImports,
                        bundlePath: `app${UNDERSCORE_NOT_FOUND_ROUTE_ENTRY}`,
                        absolutePagePath: entryRequest
                    });
                }
                if (name === `app${UNDERSCORE_GLOBAL_ERROR_ROUTE_ENTRY}`) {
                    clientEntriesToInject.push({
                        compiler,
                        compilation,
                        entryName: name,
                        clientComponentImports,
                        bundlePath: `app${UNDERSCORE_GLOBAL_ERROR_ROUTE_ENTRY}`,
                        absolutePagePath: entryRequest
                    });
                }
            }
            // Make sure CSS imports are deduplicated before injecting the client entry
            // and SSR modules.
            const dedupedCSSImports = deduplicateCSSImportsForEntry(mergedCSSimports);
            for (const clientEntryToInject of clientEntriesToInject){
                const injected = this.injectClientEntryAndSSRModules({
                    ...clientEntryToInject,
                    clientImports: {
                        ...clientEntryToInject.clientComponentImports,
                        ...(dedupedCSSImports[clientEntryToInject.absolutePagePath] || []).reduce((res, curr)=>{
                            res[curr] = new Set();
                            return res;
                        }, {})
                    }
                });
                // Track all created SSR dependencies for each entry from the server layer.
                if (!createdSSRDependenciesForEntry[clientEntryToInject.entryName]) {
                    createdSSRDependenciesForEntry[clientEntryToInject.entryName] = [];
                }
                createdSSRDependenciesForEntry[clientEntryToInject.entryName].push(injected[3]);
                addClientEntryAndSSRModulesList.push(injected);
            }
            if (!isAppRouteRoute(name)) {
                // Create internal app
                addClientEntryAndSSRModulesList.push(this.injectClientEntryAndSSRModules({
                    compiler,
                    compilation,
                    entryName: name,
                    clientImports: {
                        ...internalClientComponentEntryImports
                    },
                    bundlePath: APP_CLIENT_INTERNALS
                }));
            }
            if (actionEntryImports.size > 0) {
                if (!actionMapsPerEntry[name]) {
                    actionMapsPerEntry[name] = new Map();
                }
                actionMapsPerEntry[name] = new Map([
                    ...actionMapsPerEntry[name],
                    ...actionEntryImports
                ]);
            }
        });
        for (const [name, actionEntryImports] of Object.entries(actionMapsPerEntry)){
            addActionEntryList.push(this.injectActionEntry({
                compiler,
                compilation,
                actions: actionEntryImports,
                entryName: name,
                bundlePath: name,
                createdActionIds
            }));
        }
        // Invalidate in development to trigger recompilation
        const invalidator = getInvalidator(compiler.outputPath);
        // Check if any of the entry injections need an invalidation
        if (invalidator && addClientEntryAndSSRModulesList.some(([shouldInvalidate])=>shouldInvalidate === true)) {
            invalidator.invalidate([
                COMPILER_NAMES.client
            ]);
        }
        // Client compiler is invalidated before awaiting the compilation of the SSR
        // and RSC client component entries so that the client compiler is running
        // in parallel to the server compiler.
        await Promise.all(addClientEntryAndSSRModulesList.flatMap((addClientEntryAndSSRModules)=>[
                addClientEntryAndSSRModules[1],
                addClientEntryAndSSRModules[2]
            ]));
        // Wait for action entries to be added.
        await Promise.all(addActionEntryList);
        const addedClientActionEntryList = [];
        const actionMapsPerClientEntry = {};
        // We need to create extra action entries that are created from the
        // client layer.
        // Start from each entry's created SSR dependency from our previous step.
        for (const [name, ssrEntryDependencies] of Object.entries(createdSSRDependenciesForEntry)){
            // Collect from all entries, e.g. layout.js, page.js, loading.js, ...
            // add aggregate them.
            const actionEntryImports = this.collectClientActionsFromDependencies({
                compilation,
                dependencies: ssrEntryDependencies
            });
            if (actionEntryImports.size > 0) {
                if (!actionMapsPerClientEntry[name]) {
                    actionMapsPerClientEntry[name] = new Map();
                }
                actionMapsPerClientEntry[name] = new Map([
                    ...actionMapsPerClientEntry[name],
                    ...actionEntryImports
                ]);
            }
        }
        for (const [entryName, actionEntryImports] of Object.entries(actionMapsPerClientEntry)){
            // If an action method is already created in the server layer, we don't
            // need to create it again in the action layer.
            // This is to avoid duplicate action instances and make sure the module
            // state is shared.
            let remainingClientImportedActions = false;
            const remainingActionEntryImports = new Map();
            for (const [dep, actions] of actionEntryImports){
                const remainingActionNames = [];
                for (const action of actions){
                    if (!createdActionIds.has(entryName + '@' + action.id)) {
                        remainingActionNames.push(action);
                    }
                }
                if (remainingActionNames.length > 0) {
                    remainingActionEntryImports.set(dep, remainingActionNames);
                    remainingClientImportedActions = true;
                }
            }
            if (remainingClientImportedActions) {
                addedClientActionEntryList.push(this.injectActionEntry({
                    compiler,
                    compilation,
                    actions: remainingActionEntryImports,
                    entryName,
                    bundlePath: entryName,
                    fromClient: true,
                    createdActionIds
                }));
            }
        }
        await Promise.all(addedClientActionEntryList);
    }
    collectClientActionsFromDependencies({ compilation, dependencies }) {
        // action file path -> action names
        const collectedActions = new Map();
        // Keep track of checked modules to avoid infinite loops with recursive imports.
        const visitedModule = new Set();
        const visitedEntry = new Set();
        const collectActions = ({ entryRequest, resolvedModule })=>{
            const collectActionsInDep = (mod)=>{
                var _getModuleBuildInfo_rsc;
                if (!mod) return;
                const modResource = getModuleResource(mod);
                if (!modResource) return;
                if (visitedModule.has(modResource)) return;
                visitedModule.add(modResource);
                const actionIds = (_getModuleBuildInfo_rsc = getModuleBuildInfo(mod).rsc) == null ? void 0 : _getModuleBuildInfo_rsc.actionIds;
                if (actionIds) {
                    collectedActions.set(modResource, Object.entries(actionIds).map(([id, actionInfo])=>{
                        // Handle both old format (string) and new format (object with name)
                        const exportedName = typeof actionInfo === 'object' && actionInfo !== null ? actionInfo.name : actionInfo;
                        return {
                            id,
                            exportedName,
                            filename: path.posix.relative(this.projectDir, modResource)
                        };
                    }));
                }
                // Collect used exported actions transversely.
                getModuleReferencesInOrder(mod, compilation.moduleGraph).forEach((connection)=>{
                    collectActionsInDep(connection.resolvedModule);
                });
            };
            // Don't traverse the module graph anymore once hitting the action layer.
            if (entryRequest && !entryRequest.includes('next-flight-action-entry-loader')) {
                // Traverse the module graph to find all client components.
                collectActionsInDep(resolvedModule);
            }
        };
        for (const entryDependency of dependencies){
            const ssrEntryModule = compilation.moduleGraph.getResolvedModule(entryDependency);
            for (const connection of getModuleReferencesInOrder(ssrEntryModule, compilation.moduleGraph)){
                const depModule = connection.dependency;
                const request = depModule.request;
                // It is possible that the same entry is added multiple times in the
                // connection graph. We can just skip these to speed up the process.
                if (visitedEntry.has(request)) continue;
                visitedEntry.add(request);
                collectActions({
                    entryRequest: request,
                    resolvedModule: connection.resolvedModule
                });
            }
        }
        return collectedActions;
    }
    collectComponentInfoFromServerEntryDependency({ entryRequest, compilation, resolvedModule }) {
        // Keep track of checked modules to avoid infinite loops with recursive imports.
        const visitedOfClientComponentsTraverse = new Set();
        // Info to collect.
        const clientComponentImports = {};
        const actionImports = [];
        const CSSImports = new Set();
        const filterClientComponents = (mod, importedIdentifiers)=>{
            var _getModuleBuildInfo_rsc;
            if (!mod) return;
            const modResource = getModuleResource(mod);
            if (!modResource) return;
            if (visitedOfClientComponentsTraverse.has(modResource)) {
                if (clientComponentImports[modResource]) {
                    addClientImport(mod, modResource, clientComponentImports, importedIdentifiers, false);
                }
                return;
            }
            visitedOfClientComponentsTraverse.add(modResource);
            const actionIds = (_getModuleBuildInfo_rsc = getModuleBuildInfo(mod).rsc) == null ? void 0 : _getModuleBuildInfo_rsc.actionIds;
            if (actionIds) {
                actionImports.push([
                    modResource,
                    Object.entries(actionIds).map(([id, actionInfo])=>{
                        // Handle both old format (string) and new format (object with name)
                        const exportedName = typeof actionInfo === 'object' && actionInfo !== null ? actionInfo.name : actionInfo;
                        return {
                            id,
                            exportedName,
                            filename: path.posix.relative(this.projectDir, modResource)
                        };
                    })
                ]);
            }
            if (isCSSMod(mod)) {
                const sideEffectFree = mod.factoryMeta && mod.factoryMeta.sideEffectFree;
                if (sideEffectFree) {
                    const unused = !compilation.moduleGraph.getExportsInfo(mod).isModuleUsed(this.webpackRuntime);
                    if (unused) return;
                }
                CSSImports.add(modResource);
            } else if (isClientComponentEntryModule(mod)) {
                if (!clientComponentImports[modResource]) {
                    clientComponentImports[modResource] = new Set();
                }
                addClientImport(mod, modResource, clientComponentImports, importedIdentifiers, true);
                return;
            }
            getModuleReferencesInOrder(mod, compilation.moduleGraph).forEach((connection)=>{
                var _connection_dependency;
                let dependencyIds = [];
                // `ids` are the identifiers that are imported from the dependency,
                // if it's present, it's an array of strings.
                if ((_connection_dependency = connection.dependency) == null ? void 0 : _connection_dependency.ids) {
                    dependencyIds.push(...connection.dependency.ids);
                } else {
                    dependencyIds = [
                        '*'
                    ];
                }
                filterClientComponents(connection.resolvedModule, dependencyIds);
            });
        };
        // Traverse the module graph to find all client components.
        filterClientComponents(resolvedModule, []);
        return {
            clientComponentImports,
            cssImports: CSSImports.size ? {
                [entryRequest]: Array.from(CSSImports)
            } : {},
            actionImports
        };
    }
    injectClientEntryAndSSRModules({ compiler, compilation, entryName, clientImports, bundlePath, absolutePagePath }) {
        const bundler = getWebpackBundler();
        let shouldInvalidate = false;
        const modules = Object.keys(clientImports).sort((a, b)=>regexCSS.test(b) ? 1 : a.localeCompare(b)).map((clientImportPath)=>({
                request: clientImportPath,
                ids: [
                    ...clientImports[clientImportPath]
                ]
            }));
        // For the client entry, we always use the CJS build of Next.js. If the
        // server is using the ESM build (when using the Edge runtime), we need to
        // replace them.
        const clientBrowserLoader = `next-flight-client-entry-loader?${stringify({
            modules: (this.isEdgeServer ? modules.map(({ request, ids })=>({
                    request: request.replace(/[\\/]next[\\/]dist[\\/]esm[\\/]/, '/next/dist/'.replace(/\//g, path.sep)),
                    ids
                })) : modules).map((x)=>JSON.stringify(x)),
            server: false
        })}!`;
        const clientServerLoader = `next-flight-client-entry-loader?${stringify({
            modules: modules.map((x)=>JSON.stringify(x)),
            server: true
        })}!`;
        // Add for the client compilation
        // Inject the entry to the client compiler.
        if (this.dev) {
            const entries = getEntries(compiler.outputPath);
            const pageKey = getEntryKey(COMPILER_NAMES.client, PAGE_TYPES.APP, bundlePath);
            if (!entries[pageKey]) {
                entries[pageKey] = {
                    type: EntryTypes.CHILD_ENTRY,
                    parentEntries: new Set([
                        entryName
                    ]),
                    absoluteEntryFilePath: absolutePagePath,
                    bundlePath,
                    request: clientBrowserLoader,
                    dispose: false,
                    lastActiveTime: Date.now()
                };
                shouldInvalidate = true;
            } else {
                const entryData = entries[pageKey];
                // New version of the client loader
                if (entryData.request !== clientBrowserLoader) {
                    entryData.request = clientBrowserLoader;
                    shouldInvalidate = true;
                }
                if (entryData.type === EntryTypes.CHILD_ENTRY) {
                    entryData.parentEntries.add(entryName);
                }
                entryData.dispose = false;
                entryData.lastActiveTime = Date.now();
            }
        } else {
            pluginState.injectedClientEntries[bundlePath] = clientBrowserLoader;
        }
        const clientComponentSSREntryDep = bundler.EntryPlugin.createDependency(clientServerLoader, {
            name: bundlePath
        });
        const clientComponentRSCEntryDep = bundler.EntryPlugin.createDependency(clientServerLoader, {
            name: bundlePath
        });
        return [
            shouldInvalidate,
            // Add the entries to the server compiler for the SSR and RSC layers. The
            // promises are awaited later using `Promise.all` in order to parallelize
            // adding the entries.
            this.addEntry(compilation, compiler.context, clientComponentSSREntryDep, {
                name: entryName,
                layer: WEBPACK_LAYERS.serverSideRendering
            }),
            this.addEntry(compilation, compiler.context, clientComponentRSCEntryDep, {
                name: entryName,
                layer: WEBPACK_LAYERS.reactServerComponents
            }),
            clientComponentSSREntryDep
        ];
    }
    injectActionEntry({ compiler, compilation, actions, entryName, bundlePath, fromClient, createdActionIds }) {
        const bundler = getWebpackBundler();
        const actionsArray = Array.from(actions.entries());
        for (const [, actionsFromModule] of actions){
            for (const { id } of actionsFromModule){
                createdActionIds.add(entryName + '@' + id);
            }
        }
        if (actionsArray.length === 0) {
            return Promise.resolve();
        }
        const actionLoader = `next-flight-action-entry-loader?${stringify({
            actions: JSON.stringify(actionsArray),
            __client_imported__: fromClient
        })}!`;
        const currentCompilerServerActions = this.isEdgeServer ? pluginState.edgeServerActions : pluginState.serverActions;
        for (const [, actionsFromModule] of actionsArray){
            for (const { id, exportedName, filename } of actionsFromModule){
                if (typeof currentCompilerServerActions[id] === 'undefined') {
                    currentCompilerServerActions[id] = {
                        workers: {},
                        layer: {},
                        filename,
                        exportedName
                    };
                }
                currentCompilerServerActions[id].workers[bundlePath] = {
                    moduleId: '',
                    async: false
                };
                currentCompilerServerActions[id].layer[bundlePath] = fromClient ? WEBPACK_LAYERS.actionBrowser : WEBPACK_LAYERS.reactServerComponents;
            }
        }
        // Inject the entry to the server compiler
        const actionEntryDep = bundler.EntryPlugin.createDependency(actionLoader, {
            name: bundlePath
        });
        return this.addEntry(compilation, // Reuse compilation context.
        compiler.context, actionEntryDep, {
            name: entryName,
            layer: fromClient ? WEBPACK_LAYERS.actionBrowser : WEBPACK_LAYERS.reactServerComponents
        });
    }
    addEntry(compilation, context, dependency, options) /* Promise<module> */ {
        return new Promise((resolve, reject)=>{
            if ('rspack' in compilation.compiler) {
                compilation.addInclude(context, dependency, options, (err, module)=>{
                    if (err) {
                        return reject(err);
                    }
                    compilation.moduleGraph.getExportsInfo(module).setUsedInUnknownWay(this.isEdgeServer ? EDGE_RUNTIME_WEBPACK : DEFAULT_RUNTIME_WEBPACK);
                    return resolve(module);
                });
            } else {
                const entry = compilation.entries.get(options.name);
                entry.includeDependencies.push(dependency);
                compilation.hooks.addEntry.call(entry, options);
                compilation.addModuleTree({
                    context,
                    dependency,
                    contextInfo: {
                        issuerLayer: options.layer
                    }
                }, (err, module)=>{
                    if (err) {
                        compilation.hooks.failedEntry.call(dependency, options, err);
                        return reject(err);
                    }
                    compilation.hooks.succeedEntry.call(dependency, options, module);
                    compilation.moduleGraph.getExportsInfo(module).setUsedInUnknownWay(this.isEdgeServer ? EDGE_RUNTIME_WEBPACK : DEFAULT_RUNTIME_WEBPACK);
                    return resolve(module);
                });
            }
        });
    }
    async createActionAssets(compilation) {
        const serverActions = {};
        const edgeServerActions = {};
        traverseModules(compilation, (mod, _chunk, chunkGroup, modId)=>{
            // Go through all action entries and record the module ID for each entry.
            if (chunkGroup.name && mod.request && modId && /next-flight-action-entry-loader/.test(mod.request)) {
                const fromClient = /&__client_imported__=true/.test(mod.request);
                const mapping = this.isEdgeServer ? pluginState.edgeServerActionModules : pluginState.serverActionModules;
                if (!mapping[chunkGroup.name]) {
                    mapping[chunkGroup.name] = {};
                }
                mapping[chunkGroup.name][fromClient ? 'client' : 'server'] = {
                    moduleId: modId,
                    async: compilation.moduleGraph.isAsync(mod)
                };
            }
        });
        for(let id in pluginState.serverActions){
            const { layer, ...action } = pluginState.serverActions[id];
            for(let name in action.workers){
                const modId = pluginState.serverActionModules[name][layer[name] === WEBPACK_LAYERS.actionBrowser ? 'client' : 'server'];
                action.workers[name] = modId;
            }
            serverActions[id] = action;
        }
        for(let id in pluginState.edgeServerActions){
            const { layer, ...action } = pluginState.edgeServerActions[id];
            for(let name in action.workers){
                const modId = pluginState.edgeServerActionModules[name][layer[name] === WEBPACK_LAYERS.actionBrowser ? 'client' : 'server'];
                action.workers[name] = modId;
            }
            edgeServerActions[id] = action;
        }
        const serverManifest = {
            node: serverActions,
            edge: edgeServerActions,
            encryptionKey: this.encryptionKey
        };
        const edgeServerManifest = {
            ...serverManifest,
            encryptionKey: 'process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY'
        };
        const json = JSON.stringify(serverManifest, null, this.dev ? 2 : undefined);
        const edgeJson = JSON.stringify(edgeServerManifest, null, this.dev ? 2 : undefined);
        compilation.emitAsset(`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.js`, new sources.RawSource(`self.__RSC_SERVER_MANIFEST=${JSON.stringify(edgeJson)}`));
        compilation.emitAsset(`${this.assetPrefix}${SERVER_REFERENCE_MANIFEST}.json`, new sources.RawSource(json));
    }
}
function addClientImport(mod, modRequest, clientComponentImports, importedIdentifiers, isFirstVisitModule) {
    var _getModuleBuildInfo_rsc;
    const clientEntryType = (_getModuleBuildInfo_rsc = getModuleBuildInfo(mod).rsc) == null ? void 0 : _getModuleBuildInfo_rsc.clientEntryType;
    const isCjsModule = clientEntryType === 'cjs';
    const assumedSourceType = getAssumedSourceType(mod, isCjsModule ? 'commonjs' : 'auto');
    const clientImportsSet = clientComponentImports[modRequest];
    if (importedIdentifiers[0] === '*') {
        // If there's collected import path with named import identifiers,
        // or there's nothing in collected imports are empty.
        // we should include the whole module.
        if (!isFirstVisitModule && [
            ...clientImportsSet
        ][0] !== '*') {
            clientComponentImports[modRequest] = new Set([
                '*'
            ]);
        }
    } else {
        const isAutoModuleSourceType = assumedSourceType === 'auto';
        if (isAutoModuleSourceType) {
            clientComponentImports[modRequest] = new Set([
                '*'
            ]);
        } else {
            // If it's not analyzed as named ESM exports, e.g. if it's mixing `export *` with named exports,
            // We'll include all modules since it's not able to do tree-shaking.
            for (const name of importedIdentifiers){
                // For cjs module default import, we include the whole module since
                const isCjsDefaultImport = isCjsModule && name === 'default';
                // Always include __esModule along with cjs module default export,
                // to make sure it work with client module proxy from React.
                if (isCjsDefaultImport) {
                    clientComponentImports[modRequest].add('__esModule');
                }
                clientComponentImports[modRequest].add(name);
            }
        }
    }
}
function getModuleResource(mod) {
    var _mod_resourceResolveData, _mod_resourceResolveData1, _mod_matchResource;
    const modPath = ((_mod_resourceResolveData = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData.path) || '';
    const modQuery = ((_mod_resourceResolveData1 = mod.resourceResolveData) == null ? void 0 : _mod_resourceResolveData1.query) || '';
    // We have to always use the resolved request here to make sure the
    // server and client are using the same module path (required by RSC), as
    // the server compiler and client compiler have different resolve configs.
    let modResource = modPath + modQuery;
    // Context modules don't have a resource path, we use the identifier instead.
    if (mod.constructor.name === 'ContextModule') {
        modResource = mod.identifier();
    }
    // For the barrel optimization, we need to use the match resource instead
    // because there will be 2 modules for the same file (same resource path)
    // but they're different modules and can't be deduped via `visitedModule`.
    // The first module is a virtual re-export module created by the loader.
    if ((_mod_matchResource = mod.matchResource) == null ? void 0 : _mod_matchResource.startsWith(BARREL_OPTIMIZATION_PREFIX)) {
        modResource = mod.matchResource + ':' + modResource;
    }
    if (mod.resource === `?${WEBPACK_RESOURCE_QUERIES.metadataRoute}`) {
        return getMetadataRouteResource(mod.rawRequest).filePath;
    }
    return modResource;
}
function getMetadataRouteResource(request) {
    // e.g. next-metadata-route-loader?filePath=<some-url-encoded-path>&isDynamicRouteExtension=1!?__next_metadata_route__
    const query = request.split('!')[0].split('next-metadata-route-loader?')[1];
    return parse(query);
}

//# sourceMappingURL=flight-client-entry-plugin.js.map